scriptmonkey
Advanced tools
+81
-168
| Metadata-Version: 2.1 | ||
| Name: scriptmonkey | ||
| Version: 1.4.4 | ||
| Summary: A Python package that generates complex software projects and fixes errors in your code using OpenAI's GPT API. | ||
| Version: 2.0.0 | ||
| Summary: A simple CLI utility for copying files and directory trees to clipboard for easy sharing with LLMs. | ||
| Home-page: https://github.com/lukerbs/ScriptMonkey | ||
@@ -9,3 +9,3 @@ Author: Luke Kerbs | ||
| License: MIT | ||
| Keywords: openai,GPT,AI,project generation,multi-file project,complex project generator,code error fixing,code automation,GPT API,error correction,code assistant,AI coding tools,script monkey,automation,development tools,python tools,code analysis,machine learning,project bootstrap,custom code generation,python package,cursor,devin,copilot,automatic code correction | ||
| Keywords: clipboard,file sharing,directory tree,development tools,python tools,LLM helper,code sharing,project structure,file utility,developer tools | ||
| Classifier: Programming Language :: Python :: 3 | ||
@@ -16,31 +16,16 @@ Classifier: License :: OSI Approved :: MIT License | ||
| Description-Content-Type: text/markdown | ||
| Requires-Dist: openai | ||
| Requires-Dist: pydantic | ||
| Requires-Dist: tqdm | ||
| Requires-Dist: python-dotenv | ||
| Requires-Dist: rich | ||
| Requires-Dist: pyperclip | ||
| # ScriptMonkey 🐒 | ||
| ScriptMonkey is an AI-powered CLI tool and Python library that reimagines how projects are built. It doesn’t just generate simple scripts or templates like traditional LLMs—it creates entire, multi-file, multi-directory projects with fully custom code. Complex software projects can be generated in seconds based on your natural language descriptions, providing everything you need, from models and routes to templates and configuration files. With ScriptMonkey, you can instantly bootstrap new ideas or start complex projects without the tedious setup. The tool is perfect for developers looking to quickly prototype, experiment, or build production-ready applications without having to write boilerplate code manually. | ||
| ScriptMonkey is a simple CLI utility that helps developers quickly copy files and directory structures to their clipboard in a formatted way. Perfect for sharing code context with LLMs, creating documentation, or getting help with your projects. | ||
| Importantly, ScriptMonkey is versatile and can be used to build any type of software project in any programming language. Whether you’re working with Python, JavaScript, Java, C++, or any other language, ScriptMonkey can generate project structures and provide tailored code. Additionally, its context-aware Q&A feature allows you to ask technical questions about projects in any language, with or without providing files for context. You can even use your default editor for longer, multi-line prompts, ensuring that ScriptMonkey can adapt to your specific needs. | ||
| ## Features | ||
| - **Custom Project Generation**: Create entire Python projects, not just boilerplate code. ScriptMonkey generates the specific files and directories you need based on your description. | ||
| - **Lightning-Fast Setup**: Complex Python projects can be generated in seconds, saving you time and effort. | ||
| - **Automatic Error Detection**: Captures errors during runtime. | ||
| - **AI-Powered Fixes**: Uses OpenAI's GPT API to understand and resolve errors. | ||
| - **Code Auto-Correction**: Automatically updates your Python files with the fixes. | ||
| - **Cross-IDE Compatibility**: Works with any IDE or code editor. | ||
| - **Context-Aware Q&A**: Ask questions directly to ChatGPT with or without providing files for context. Get detailed answers with code examples, explanations, and best practices tailored to your needs. | ||
| - **File Copying**: Copy one or multiple files to clipboard with clean formatting | ||
| - **Directory Tree**: Generate and copy directory structure to clipboard | ||
| - **Combined Output**: Copy both files and directory tree together | ||
| - **Smart Filtering**: Intelligent directory tree that filters out build artifacts, dependencies, and other non-essential files | ||
| - **Cross-Platform**: Works on Windows, macOS, and Linux | ||
| ## 🚀 Watch the Demo | ||
| [](https://youtu.be/2zoCDlf0Zf8) | ||
| Click the image above or watch the video directly on [YouTube](https://youtu.be/2zoCDlf0Zf8). | ||
| ## Installation | ||
@@ -56,181 +41,109 @@ | ||
| ### Project Generation with `scriptmonkey` CLI Tool | ||
| ### Copy Files to Clipboard | ||
| ScriptMonkey can generate a complete, custom-coded project structure based on a description you provide. This feature helps you quickly set up new projects with the necessary files and folders. | ||
| Copy one or more files to your clipboard with clean formatting: | ||
| #### How to Use | ||
| ```bash | ||
| scriptmonkey --files path/to/file1.py path/to/file2.js | ||
| ``` | ||
| 1. Run the following command in your terminal: | ||
| The files will be copied to your clipboard in this format: | ||
| ``` | ||
| - - - - - - - - - - | ||
| Here are some details about the project. | ||
| ```bash | ||
| scriptmonkey | ||
| ``` | ||
| # path/to/file1.py | ||
| <content from file1.py> | ||
| 2. A text editor will open (e.g., `nano`, `vim`, or `notepad` depending on your environment). Follow the on-screen instructions to provide a detailed description of your project. | ||
| - - - - - - - - - - | ||
| 3. Save and close the editor. ScriptMonkey will then: | ||
| - Generate a complete project structure based on your description. | ||
| - Create directories, code files, and templates. | ||
| - Automatically generate a `README.md` with installation instructions and usage details. | ||
| # path/to/file2.js | ||
| <content from file2.js> | ||
| #### Example Project Description Prompt | ||
| - - - - - - - - - - | ||
| ``` | ||
| I need a Flask-based web application for managing a book library. The application should include: | ||
| - User authentication (login, registration, password reset). | ||
| - Models for Users, Books, and Authors using SQLAlchemy. | ||
| - A REST API with routes for adding, updating, and deleting books and authors. | ||
| - An admin dashboard for managing users and viewing statistics. | ||
| - HTML templates for user login, book list, and book detail views. | ||
| - The database should use PostgreSQL. | ||
| - Include environment-specific configurations for development and production. | ||
| ``` | ||
| ScriptMonkey will use this description to create a project structure and code files for you in a directory named `generated_project`. | ||
| ### Copy Directory Tree to Clipboard | ||
| ### Context-Aware Q&A with `scriptmonkey --ask` CLI Tool | ||
| Generate and copy a clean directory tree structure: | ||
| ScriptMonkey can help answer your technical questions, whether or not you provide code files for context. This feature allows you to leverage the power of ChatGPT to ask questions about files, clarify concepts, get code reviews, or understand best practices in various programming languages. | ||
| ```bash | ||
| scriptmonkey --tree | ||
| ``` | ||
| #### How to Use | ||
| This creates an intelligent directory tree that: | ||
| - Filters out build artifacts (`node_modules`, `__pycache__`, `.git`, etc.) | ||
| - Excludes temporary files (`*.log`, `*.pyc`, `.DS_Store`, etc.) | ||
| - Shows important directories as shallow entries (`vendor/`, `build/ [...]`) | ||
| - Provides special handling for Rust `target` directories | ||
| - **Pass a question directly**: | ||
| ### Copy Both Files and Tree | ||
| For quick, short prompts, you can directly pass your question using the `--ask` parameter: | ||
| Combine file contents with directory structure: | ||
| ```bash | ||
| scriptmonkey --ask "What are the best practices for database indexing?" | ||
| ``` | ||
| ```bash | ||
| scriptmonkey --files src/main.py src/utils.py --tree | ||
| ``` | ||
| - **Use your default editor for longer prompts**: | ||
| This copies both the specified files and the directory tree to your clipboard, giving you complete context for sharing with LLMs or documentation. | ||
| If you need to provide a more detailed or multi-line prompt, simply use `--ask` without specifying a question. This will open up your default text editor (e.g., `vim`, `nano`, `notepad`) where you can write out your question or prompt: | ||
| ## Smart Directory Filtering | ||
| ```bash | ||
| scriptmonkey --ask | ||
| ``` | ||
| ScriptMonkey uses intelligent filtering to show you only the important parts of your project: | ||
| After you write your question in the editor and save and close it, ScriptMonkey will use the content as the question. This is especially useful for longer or more complex queries that require more explanation. | ||
| **Shallow Directories** (shown but contents hidden): | ||
| - `node_modules`, `.git`, `build`, `dist` | ||
| - `__pycache__`, `.venv`, `vendor` | ||
| - `test`, `tests`, `assets`, `img` | ||
| - **Ask a question about specific local files**: | ||
| **Excluded Files** (completely hidden): | ||
| - `*.log`, `*.pyc`, `*.swp`, `.DS_Store` | ||
| - `*.class`, `*.o`, `*.dll`, `*.so` | ||
| - Build artifacts and temporary files | ||
| ```bash | ||
| scriptmonkey --ask "Can you help me optimize this function?" --files ./path/to/file1.py ./path/to/file2.js | ||
| ``` | ||
| **Special Handling**: | ||
| - Rust `target/release` directories show filtered contents | ||
| - Model directories are treated as shallow | ||
| - Test directories are marked but not expanded | ||
| The `--files` flag allows you to provide specific files as context for your question. ScriptMonkey will include the contents of these files in its analysis, allowing it to reference the actual code or data you are working with. This is particularly useful for getting detailed feedback on specific code snippets, debugging issues, or seeking advice on how to improve certain parts of your project. | ||
| When you use `--files`, ScriptMonkey will read the contents of each provided file, include them in the prompt, and tailor its response based on the combined context of your question and the file contents. This feature ensures that you get precise, context-aware answers, helping you solve code challenges or understand complex concepts more effectively. | ||
| ## Use Cases | ||
| - **Ask a question with a directory tree**: | ||
| - **LLM Assistance**: Quickly share your code context with ChatGPT, Claude, or other AI assistants | ||
| - **Code Reviews**: Copy relevant files for review discussions | ||
| - **Documentation**: Generate clean file listings for documentation | ||
| - **Project Sharing**: Share project structure and key files with teammates | ||
| - **Debugging Help**: Include relevant code and project structure when asking for help | ||
| ```bash | ||
| scriptmonkey --ask "How do I organize this project better?" --tree | ||
| ``` | ||
| ## Requirements | ||
| - Python 3.6 or later | ||
| - Cross-platform clipboard support (automatic) | ||
| The `--tree` flag will include a tree representation of the current working directory in the context for your question. This is particularly useful when you want to get feedback on the structure of your codebase or when your question relates to the project organization. It can be used in tandem with the `--files` flag to provide additional context about how those files fit within the larger context of the project. | ||
| ## Examples | ||
| ScriptMonkey will analyze your question and any provided files or the directory tree to give a detailed, markdown-formatted response with explanations and code suggestions, if applicable. This feature is great for in-depth guidance on code optimization, architecture, or general programming questions. | ||
| **Copy a single Python file:** | ||
| ```bash | ||
| scriptmonkey --files main.py | ||
| ``` | ||
| ### Copy Key Files and Project Details with `--copy` | ||
| **Copy multiple configuration files:** | ||
| ```bash | ||
| scriptmonkey --files config.json requirements.txt .env.example | ||
| ``` | ||
| ScriptMonkey's new `--copy` feature is designed to streamline the process of copying critical code files and project structure details into your clipboard, making it easier to ask questions to LLMs like ChatGPT or Claude. This feature formats the copied content in a neat way that includes file contents and the directory tree, making it simple to paste into a conversation for contextual help. | ||
| #### How to Use | ||
| - **Copy file contents directly**: | ||
| You can use the --copy flag to quickly copy the contents of specified files into your clipboard, neatly formatted for easy sharing with an LLM. When combined with the --files flag, ScriptMonkey will copy the contents of the selected files along with a complete directory tree of your project. This provides additional context, helping LLMs better understand your project’s structure: | ||
| ```bash | ||
| scriptmonkey --copy --files path/to/file1.py path/to/file2.js | ||
| ``` | ||
| Your files and project directory tree are automatically copied to your clipboard in the format: | ||
| ``` | ||
| - - - - - - - - - - | ||
| Here are some details about the project. | ||
| # path/to/file1.py | ||
| <content from file1.py> | ||
| - - - - - - - - - - | ||
| # path/to/file2.js | ||
| <content from file2.js> | ||
| - - - - - - - - - - | ||
| # PROJECT TREE | ||
| <directory structure> | ||
| ``` | ||
| ### Error Handling with `scriptmonkey.run()` | ||
| ScriptMonkey doesn't just build projects; it also makes debugging a breeze. | ||
| 1. Import `scriptmonkey` in your Python script. | ||
| 2. Call `scriptmonkey.run()` to activate the error handler. | ||
| 3. Run your code, and let ScriptMonkey handle any errors that occur. | ||
| #### Example | ||
| ```python | ||
| import scriptmonkey | ||
| # Enable ScriptMonkey's error handler | ||
| scriptmonkey.run() | ||
| # Intentional error for testing | ||
| def add(a, b): | ||
| return a + b # This will fail if b is a string | ||
| print(add(2, "3")) # ScriptMonkey will automatically fix this error and update the file. | ||
| **Just the directory tree:** | ||
| ```bash | ||
| scriptmonkey --tree | ||
| ``` | ||
| Once an error occurs, ScriptMonkey will: | ||
| 1. Detect the error. | ||
| 2. Send the error and code to OpenAI for analysis. | ||
| 3. Provide a solution and automatically update the file with the corrected code. | ||
| ### Setting or Updating Your OpenAI API Key | ||
| If you haven't set your OpenAI API key yet or need to update it, you can do so with the following command: | ||
| **Everything together:** | ||
| ```bash | ||
| python3 -m scriptmonkey --set-api-key your-api-key | ||
| scriptmonkey --files src/*.py --tree | ||
| ``` | ||
| This will store the API key in a config locally and use it for all future interactions with OpenAI. | ||
| ## Legacy Compatibility | ||
| ## Requirements | ||
| - Python 3.6 or later | ||
| - An OpenAI API key (follow the steps below if you don't have one) | ||
| For users upgrading from ScriptMonkey 1.x: The `scriptmonkey.run()` function is still available but no longer provides error handling functionality. It exists only for backward compatibility and does nothing. | ||
| ## Obtaining an OpenAI API Key | ||
| --- | ||
| 1. Go to the OpenAI website: [OpenAI Platform](https://platform.openai.com/) | ||
| 2. Sign up or log in to your account. | ||
| 3. Navigate to the **API keys** section in your account dashboard. | ||
| 4. Create a new API key and copy it. | ||
| ## Configuring the API Key for ScriptMonkey | ||
| - **Option 1: Environment Variable** | ||
| You can set up your API key as an environment variable: | ||
| ```bash | ||
| export OPENAI_API_KEY='your-api-key' | ||
| ``` | ||
| - **Option 2: Entering the API Key When Prompted** | ||
| If ScriptMonkey does not find an API key in your environment variables, it will prompt you to enter your key. Once entered, it will save the key to a configuration file for future use. | ||
| - **Option 3: Using the `--set-api-key` Command** | ||
| Use the `--set-api-key` command as shown above to set or update your API key easily. | ||
| Let ScriptMonkey take care of your Python errors and project setup so you can focus on building! | ||
| **Version 2.0.0** - Simplified for focus and reliability. No more AI dependencies, just pure clipboard utility goodness! 🐒 |
+78
-161
@@ -1,23 +0,12 @@ | ||
| # ScriptMonkey 🐒 | ||
| ScriptMonkey is an AI-powered CLI tool and Python library that reimagines how projects are built. It doesn’t just generate simple scripts or templates like traditional LLMs—it creates entire, multi-file, multi-directory projects with fully custom code. Complex software projects can be generated in seconds based on your natural language descriptions, providing everything you need, from models and routes to templates and configuration files. With ScriptMonkey, you can instantly bootstrap new ideas or start complex projects without the tedious setup. The tool is perfect for developers looking to quickly prototype, experiment, or build production-ready applications without having to write boilerplate code manually. | ||
| ScriptMonkey is a simple CLI utility that helps developers quickly copy files and directory structures to their clipboard in a formatted way. Perfect for sharing code context with LLMs, creating documentation, or getting help with your projects. | ||
| Importantly, ScriptMonkey is versatile and can be used to build any type of software project in any programming language. Whether you’re working with Python, JavaScript, Java, C++, or any other language, ScriptMonkey can generate project structures and provide tailored code. Additionally, its context-aware Q&A feature allows you to ask technical questions about projects in any language, with or without providing files for context. You can even use your default editor for longer, multi-line prompts, ensuring that ScriptMonkey can adapt to your specific needs. | ||
| ## Features | ||
| - **Custom Project Generation**: Create entire Python projects, not just boilerplate code. ScriptMonkey generates the specific files and directories you need based on your description. | ||
| - **Lightning-Fast Setup**: Complex Python projects can be generated in seconds, saving you time and effort. | ||
| - **Automatic Error Detection**: Captures errors during runtime. | ||
| - **AI-Powered Fixes**: Uses OpenAI's GPT API to understand and resolve errors. | ||
| - **Code Auto-Correction**: Automatically updates your Python files with the fixes. | ||
| - **Cross-IDE Compatibility**: Works with any IDE or code editor. | ||
| - **Context-Aware Q&A**: Ask questions directly to ChatGPT with or without providing files for context. Get detailed answers with code examples, explanations, and best practices tailored to your needs. | ||
| - **File Copying**: Copy one or multiple files to clipboard with clean formatting | ||
| - **Directory Tree**: Generate and copy directory structure to clipboard | ||
| - **Combined Output**: Copy both files and directory tree together | ||
| - **Smart Filtering**: Intelligent directory tree that filters out build artifacts, dependencies, and other non-essential files | ||
| - **Cross-Platform**: Works on Windows, macOS, and Linux | ||
| ## 🚀 Watch the Demo | ||
| [](https://youtu.be/2zoCDlf0Zf8) | ||
| Click the image above or watch the video directly on [YouTube](https://youtu.be/2zoCDlf0Zf8). | ||
| ## Installation | ||
@@ -33,181 +22,109 @@ | ||
| ### Project Generation with `scriptmonkey` CLI Tool | ||
| ### Copy Files to Clipboard | ||
| ScriptMonkey can generate a complete, custom-coded project structure based on a description you provide. This feature helps you quickly set up new projects with the necessary files and folders. | ||
| Copy one or more files to your clipboard with clean formatting: | ||
| #### How to Use | ||
| ```bash | ||
| scriptmonkey --files path/to/file1.py path/to/file2.js | ||
| ``` | ||
| 1. Run the following command in your terminal: | ||
| The files will be copied to your clipboard in this format: | ||
| ``` | ||
| - - - - - - - - - - | ||
| Here are some details about the project. | ||
| ```bash | ||
| scriptmonkey | ||
| ``` | ||
| # path/to/file1.py | ||
| <content from file1.py> | ||
| 2. A text editor will open (e.g., `nano`, `vim`, or `notepad` depending on your environment). Follow the on-screen instructions to provide a detailed description of your project. | ||
| - - - - - - - - - - | ||
| 3. Save and close the editor. ScriptMonkey will then: | ||
| - Generate a complete project structure based on your description. | ||
| - Create directories, code files, and templates. | ||
| - Automatically generate a `README.md` with installation instructions and usage details. | ||
| # path/to/file2.js | ||
| <content from file2.js> | ||
| #### Example Project Description Prompt | ||
| - - - - - - - - - - | ||
| ``` | ||
| I need a Flask-based web application for managing a book library. The application should include: | ||
| - User authentication (login, registration, password reset). | ||
| - Models for Users, Books, and Authors using SQLAlchemy. | ||
| - A REST API with routes for adding, updating, and deleting books and authors. | ||
| - An admin dashboard for managing users and viewing statistics. | ||
| - HTML templates for user login, book list, and book detail views. | ||
| - The database should use PostgreSQL. | ||
| - Include environment-specific configurations for development and production. | ||
| ``` | ||
| ScriptMonkey will use this description to create a project structure and code files for you in a directory named `generated_project`. | ||
| ### Copy Directory Tree to Clipboard | ||
| ### Context-Aware Q&A with `scriptmonkey --ask` CLI Tool | ||
| Generate and copy a clean directory tree structure: | ||
| ScriptMonkey can help answer your technical questions, whether or not you provide code files for context. This feature allows you to leverage the power of ChatGPT to ask questions about files, clarify concepts, get code reviews, or understand best practices in various programming languages. | ||
| ```bash | ||
| scriptmonkey --tree | ||
| ``` | ||
| #### How to Use | ||
| This creates an intelligent directory tree that: | ||
| - Filters out build artifacts (`node_modules`, `__pycache__`, `.git`, etc.) | ||
| - Excludes temporary files (`*.log`, `*.pyc`, `.DS_Store`, etc.) | ||
| - Shows important directories as shallow entries (`vendor/`, `build/ [...]`) | ||
| - Provides special handling for Rust `target` directories | ||
| - **Pass a question directly**: | ||
| ### Copy Both Files and Tree | ||
| For quick, short prompts, you can directly pass your question using the `--ask` parameter: | ||
| Combine file contents with directory structure: | ||
| ```bash | ||
| scriptmonkey --ask "What are the best practices for database indexing?" | ||
| ``` | ||
| ```bash | ||
| scriptmonkey --files src/main.py src/utils.py --tree | ||
| ``` | ||
| - **Use your default editor for longer prompts**: | ||
| This copies both the specified files and the directory tree to your clipboard, giving you complete context for sharing with LLMs or documentation. | ||
| If you need to provide a more detailed or multi-line prompt, simply use `--ask` without specifying a question. This will open up your default text editor (e.g., `vim`, `nano`, `notepad`) where you can write out your question or prompt: | ||
| ## Smart Directory Filtering | ||
| ```bash | ||
| scriptmonkey --ask | ||
| ``` | ||
| ScriptMonkey uses intelligent filtering to show you only the important parts of your project: | ||
| After you write your question in the editor and save and close it, ScriptMonkey will use the content as the question. This is especially useful for longer or more complex queries that require more explanation. | ||
| **Shallow Directories** (shown but contents hidden): | ||
| - `node_modules`, `.git`, `build`, `dist` | ||
| - `__pycache__`, `.venv`, `vendor` | ||
| - `test`, `tests`, `assets`, `img` | ||
| - **Ask a question about specific local files**: | ||
| **Excluded Files** (completely hidden): | ||
| - `*.log`, `*.pyc`, `*.swp`, `.DS_Store` | ||
| - `*.class`, `*.o`, `*.dll`, `*.so` | ||
| - Build artifacts and temporary files | ||
| ```bash | ||
| scriptmonkey --ask "Can you help me optimize this function?" --files ./path/to/file1.py ./path/to/file2.js | ||
| ``` | ||
| **Special Handling**: | ||
| - Rust `target/release` directories show filtered contents | ||
| - Model directories are treated as shallow | ||
| - Test directories are marked but not expanded | ||
| The `--files` flag allows you to provide specific files as context for your question. ScriptMonkey will include the contents of these files in its analysis, allowing it to reference the actual code or data you are working with. This is particularly useful for getting detailed feedback on specific code snippets, debugging issues, or seeking advice on how to improve certain parts of your project. | ||
| When you use `--files`, ScriptMonkey will read the contents of each provided file, include them in the prompt, and tailor its response based on the combined context of your question and the file contents. This feature ensures that you get precise, context-aware answers, helping you solve code challenges or understand complex concepts more effectively. | ||
| ## Use Cases | ||
| - **Ask a question with a directory tree**: | ||
| - **LLM Assistance**: Quickly share your code context with ChatGPT, Claude, or other AI assistants | ||
| - **Code Reviews**: Copy relevant files for review discussions | ||
| - **Documentation**: Generate clean file listings for documentation | ||
| - **Project Sharing**: Share project structure and key files with teammates | ||
| - **Debugging Help**: Include relevant code and project structure when asking for help | ||
| ```bash | ||
| scriptmonkey --ask "How do I organize this project better?" --tree | ||
| ``` | ||
| ## Requirements | ||
| - Python 3.6 or later | ||
| - Cross-platform clipboard support (automatic) | ||
| The `--tree` flag will include a tree representation of the current working directory in the context for your question. This is particularly useful when you want to get feedback on the structure of your codebase or when your question relates to the project organization. It can be used in tandem with the `--files` flag to provide additional context about how those files fit within the larger context of the project. | ||
| ## Examples | ||
| ScriptMonkey will analyze your question and any provided files or the directory tree to give a detailed, markdown-formatted response with explanations and code suggestions, if applicable. This feature is great for in-depth guidance on code optimization, architecture, or general programming questions. | ||
| **Copy a single Python file:** | ||
| ```bash | ||
| scriptmonkey --files main.py | ||
| ``` | ||
| ### Copy Key Files and Project Details with `--copy` | ||
| **Copy multiple configuration files:** | ||
| ```bash | ||
| scriptmonkey --files config.json requirements.txt .env.example | ||
| ``` | ||
| ScriptMonkey's new `--copy` feature is designed to streamline the process of copying critical code files and project structure details into your clipboard, making it easier to ask questions to LLMs like ChatGPT or Claude. This feature formats the copied content in a neat way that includes file contents and the directory tree, making it simple to paste into a conversation for contextual help. | ||
| #### How to Use | ||
| - **Copy file contents directly**: | ||
| You can use the --copy flag to quickly copy the contents of specified files into your clipboard, neatly formatted for easy sharing with an LLM. When combined with the --files flag, ScriptMonkey will copy the contents of the selected files along with a complete directory tree of your project. This provides additional context, helping LLMs better understand your project’s structure: | ||
| ```bash | ||
| scriptmonkey --copy --files path/to/file1.py path/to/file2.js | ||
| ``` | ||
| Your files and project directory tree are automatically copied to your clipboard in the format: | ||
| ``` | ||
| - - - - - - - - - - | ||
| Here are some details about the project. | ||
| # path/to/file1.py | ||
| <content from file1.py> | ||
| - - - - - - - - - - | ||
| # path/to/file2.js | ||
| <content from file2.js> | ||
| - - - - - - - - - - | ||
| # PROJECT TREE | ||
| <directory structure> | ||
| ``` | ||
| ### Error Handling with `scriptmonkey.run()` | ||
| ScriptMonkey doesn't just build projects; it also makes debugging a breeze. | ||
| 1. Import `scriptmonkey` in your Python script. | ||
| 2. Call `scriptmonkey.run()` to activate the error handler. | ||
| 3. Run your code, and let ScriptMonkey handle any errors that occur. | ||
| #### Example | ||
| ```python | ||
| import scriptmonkey | ||
| # Enable ScriptMonkey's error handler | ||
| scriptmonkey.run() | ||
| # Intentional error for testing | ||
| def add(a, b): | ||
| return a + b # This will fail if b is a string | ||
| print(add(2, "3")) # ScriptMonkey will automatically fix this error and update the file. | ||
| **Just the directory tree:** | ||
| ```bash | ||
| scriptmonkey --tree | ||
| ``` | ||
| Once an error occurs, ScriptMonkey will: | ||
| 1. Detect the error. | ||
| 2. Send the error and code to OpenAI for analysis. | ||
| 3. Provide a solution and automatically update the file with the corrected code. | ||
| ### Setting or Updating Your OpenAI API Key | ||
| If you haven't set your OpenAI API key yet or need to update it, you can do so with the following command: | ||
| **Everything together:** | ||
| ```bash | ||
| python3 -m scriptmonkey --set-api-key your-api-key | ||
| scriptmonkey --files src/*.py --tree | ||
| ``` | ||
| This will store the API key in a config locally and use it for all future interactions with OpenAI. | ||
| ## Legacy Compatibility | ||
| ## Requirements | ||
| - Python 3.6 or later | ||
| - An OpenAI API key (follow the steps below if you don't have one) | ||
| For users upgrading from ScriptMonkey 1.x: The `scriptmonkey.run()` function is still available but no longer provides error handling functionality. It exists only for backward compatibility and does nothing. | ||
| ## Obtaining an OpenAI API Key | ||
| --- | ||
| 1. Go to the OpenAI website: [OpenAI Platform](https://platform.openai.com/) | ||
| 2. Sign up or log in to your account. | ||
| 3. Navigate to the **API keys** section in your account dashboard. | ||
| 4. Create a new API key and copy it. | ||
| ## Configuring the API Key for ScriptMonkey | ||
| - **Option 1: Environment Variable** | ||
| You can set up your API key as an environment variable: | ||
| ```bash | ||
| export OPENAI_API_KEY='your-api-key' | ||
| ``` | ||
| - **Option 2: Entering the API Key When Prompted** | ||
| If ScriptMonkey does not find an API key in your environment variables, it will prompt you to enter your key. Once entered, it will save the key to a configuration file for future use. | ||
| - **Option 3: Using the `--set-api-key` Command** | ||
| Use the `--set-api-key` command as shown above to set or update your API key easily. | ||
| Let ScriptMonkey take care of your Python errors and project setup so you can focus on building! | ||
| **Version 2.0.0** - Simplified for focus and reliability. No more AI dependencies, just pure clipboard utility goodness! 🐒 |
| Metadata-Version: 2.1 | ||
| Name: scriptmonkey | ||
| Version: 1.4.4 | ||
| Summary: A Python package that generates complex software projects and fixes errors in your code using OpenAI's GPT API. | ||
| Version: 2.0.0 | ||
| Summary: A simple CLI utility for copying files and directory trees to clipboard for easy sharing with LLMs. | ||
| Home-page: https://github.com/lukerbs/ScriptMonkey | ||
@@ -9,3 +9,3 @@ Author: Luke Kerbs | ||
| License: MIT | ||
| Keywords: openai,GPT,AI,project generation,multi-file project,complex project generator,code error fixing,code automation,GPT API,error correction,code assistant,AI coding tools,script monkey,automation,development tools,python tools,code analysis,machine learning,project bootstrap,custom code generation,python package,cursor,devin,copilot,automatic code correction | ||
| Keywords: clipboard,file sharing,directory tree,development tools,python tools,LLM helper,code sharing,project structure,file utility,developer tools | ||
| Classifier: Programming Language :: Python :: 3 | ||
@@ -16,31 +16,16 @@ Classifier: License :: OSI Approved :: MIT License | ||
| Description-Content-Type: text/markdown | ||
| Requires-Dist: openai | ||
| Requires-Dist: pydantic | ||
| Requires-Dist: tqdm | ||
| Requires-Dist: python-dotenv | ||
| Requires-Dist: rich | ||
| Requires-Dist: pyperclip | ||
| # ScriptMonkey 🐒 | ||
| ScriptMonkey is an AI-powered CLI tool and Python library that reimagines how projects are built. It doesn’t just generate simple scripts or templates like traditional LLMs—it creates entire, multi-file, multi-directory projects with fully custom code. Complex software projects can be generated in seconds based on your natural language descriptions, providing everything you need, from models and routes to templates and configuration files. With ScriptMonkey, you can instantly bootstrap new ideas or start complex projects without the tedious setup. The tool is perfect for developers looking to quickly prototype, experiment, or build production-ready applications without having to write boilerplate code manually. | ||
| ScriptMonkey is a simple CLI utility that helps developers quickly copy files and directory structures to their clipboard in a formatted way. Perfect for sharing code context with LLMs, creating documentation, or getting help with your projects. | ||
| Importantly, ScriptMonkey is versatile and can be used to build any type of software project in any programming language. Whether you’re working with Python, JavaScript, Java, C++, or any other language, ScriptMonkey can generate project structures and provide tailored code. Additionally, its context-aware Q&A feature allows you to ask technical questions about projects in any language, with or without providing files for context. You can even use your default editor for longer, multi-line prompts, ensuring that ScriptMonkey can adapt to your specific needs. | ||
| ## Features | ||
| - **Custom Project Generation**: Create entire Python projects, not just boilerplate code. ScriptMonkey generates the specific files and directories you need based on your description. | ||
| - **Lightning-Fast Setup**: Complex Python projects can be generated in seconds, saving you time and effort. | ||
| - **Automatic Error Detection**: Captures errors during runtime. | ||
| - **AI-Powered Fixes**: Uses OpenAI's GPT API to understand and resolve errors. | ||
| - **Code Auto-Correction**: Automatically updates your Python files with the fixes. | ||
| - **Cross-IDE Compatibility**: Works with any IDE or code editor. | ||
| - **Context-Aware Q&A**: Ask questions directly to ChatGPT with or without providing files for context. Get detailed answers with code examples, explanations, and best practices tailored to your needs. | ||
| - **File Copying**: Copy one or multiple files to clipboard with clean formatting | ||
| - **Directory Tree**: Generate and copy directory structure to clipboard | ||
| - **Combined Output**: Copy both files and directory tree together | ||
| - **Smart Filtering**: Intelligent directory tree that filters out build artifacts, dependencies, and other non-essential files | ||
| - **Cross-Platform**: Works on Windows, macOS, and Linux | ||
| ## 🚀 Watch the Demo | ||
| [](https://youtu.be/2zoCDlf0Zf8) | ||
| Click the image above or watch the video directly on [YouTube](https://youtu.be/2zoCDlf0Zf8). | ||
| ## Installation | ||
@@ -56,181 +41,109 @@ | ||
| ### Project Generation with `scriptmonkey` CLI Tool | ||
| ### Copy Files to Clipboard | ||
| ScriptMonkey can generate a complete, custom-coded project structure based on a description you provide. This feature helps you quickly set up new projects with the necessary files and folders. | ||
| Copy one or more files to your clipboard with clean formatting: | ||
| #### How to Use | ||
| ```bash | ||
| scriptmonkey --files path/to/file1.py path/to/file2.js | ||
| ``` | ||
| 1. Run the following command in your terminal: | ||
| The files will be copied to your clipboard in this format: | ||
| ``` | ||
| - - - - - - - - - - | ||
| Here are some details about the project. | ||
| ```bash | ||
| scriptmonkey | ||
| ``` | ||
| # path/to/file1.py | ||
| <content from file1.py> | ||
| 2. A text editor will open (e.g., `nano`, `vim`, or `notepad` depending on your environment). Follow the on-screen instructions to provide a detailed description of your project. | ||
| - - - - - - - - - - | ||
| 3. Save and close the editor. ScriptMonkey will then: | ||
| - Generate a complete project structure based on your description. | ||
| - Create directories, code files, and templates. | ||
| - Automatically generate a `README.md` with installation instructions and usage details. | ||
| # path/to/file2.js | ||
| <content from file2.js> | ||
| #### Example Project Description Prompt | ||
| - - - - - - - - - - | ||
| ``` | ||
| I need a Flask-based web application for managing a book library. The application should include: | ||
| - User authentication (login, registration, password reset). | ||
| - Models for Users, Books, and Authors using SQLAlchemy. | ||
| - A REST API with routes for adding, updating, and deleting books and authors. | ||
| - An admin dashboard for managing users and viewing statistics. | ||
| - HTML templates for user login, book list, and book detail views. | ||
| - The database should use PostgreSQL. | ||
| - Include environment-specific configurations for development and production. | ||
| ``` | ||
| ScriptMonkey will use this description to create a project structure and code files for you in a directory named `generated_project`. | ||
| ### Copy Directory Tree to Clipboard | ||
| ### Context-Aware Q&A with `scriptmonkey --ask` CLI Tool | ||
| Generate and copy a clean directory tree structure: | ||
| ScriptMonkey can help answer your technical questions, whether or not you provide code files for context. This feature allows you to leverage the power of ChatGPT to ask questions about files, clarify concepts, get code reviews, or understand best practices in various programming languages. | ||
| ```bash | ||
| scriptmonkey --tree | ||
| ``` | ||
| #### How to Use | ||
| This creates an intelligent directory tree that: | ||
| - Filters out build artifacts (`node_modules`, `__pycache__`, `.git`, etc.) | ||
| - Excludes temporary files (`*.log`, `*.pyc`, `.DS_Store`, etc.) | ||
| - Shows important directories as shallow entries (`vendor/`, `build/ [...]`) | ||
| - Provides special handling for Rust `target` directories | ||
| - **Pass a question directly**: | ||
| ### Copy Both Files and Tree | ||
| For quick, short prompts, you can directly pass your question using the `--ask` parameter: | ||
| Combine file contents with directory structure: | ||
| ```bash | ||
| scriptmonkey --ask "What are the best practices for database indexing?" | ||
| ``` | ||
| ```bash | ||
| scriptmonkey --files src/main.py src/utils.py --tree | ||
| ``` | ||
| - **Use your default editor for longer prompts**: | ||
| This copies both the specified files and the directory tree to your clipboard, giving you complete context for sharing with LLMs or documentation. | ||
| If you need to provide a more detailed or multi-line prompt, simply use `--ask` without specifying a question. This will open up your default text editor (e.g., `vim`, `nano`, `notepad`) where you can write out your question or prompt: | ||
| ## Smart Directory Filtering | ||
| ```bash | ||
| scriptmonkey --ask | ||
| ``` | ||
| ScriptMonkey uses intelligent filtering to show you only the important parts of your project: | ||
| After you write your question in the editor and save and close it, ScriptMonkey will use the content as the question. This is especially useful for longer or more complex queries that require more explanation. | ||
| **Shallow Directories** (shown but contents hidden): | ||
| - `node_modules`, `.git`, `build`, `dist` | ||
| - `__pycache__`, `.venv`, `vendor` | ||
| - `test`, `tests`, `assets`, `img` | ||
| - **Ask a question about specific local files**: | ||
| **Excluded Files** (completely hidden): | ||
| - `*.log`, `*.pyc`, `*.swp`, `.DS_Store` | ||
| - `*.class`, `*.o`, `*.dll`, `*.so` | ||
| - Build artifacts and temporary files | ||
| ```bash | ||
| scriptmonkey --ask "Can you help me optimize this function?" --files ./path/to/file1.py ./path/to/file2.js | ||
| ``` | ||
| **Special Handling**: | ||
| - Rust `target/release` directories show filtered contents | ||
| - Model directories are treated as shallow | ||
| - Test directories are marked but not expanded | ||
| The `--files` flag allows you to provide specific files as context for your question. ScriptMonkey will include the contents of these files in its analysis, allowing it to reference the actual code or data you are working with. This is particularly useful for getting detailed feedback on specific code snippets, debugging issues, or seeking advice on how to improve certain parts of your project. | ||
| When you use `--files`, ScriptMonkey will read the contents of each provided file, include them in the prompt, and tailor its response based on the combined context of your question and the file contents. This feature ensures that you get precise, context-aware answers, helping you solve code challenges or understand complex concepts more effectively. | ||
| ## Use Cases | ||
| - **Ask a question with a directory tree**: | ||
| - **LLM Assistance**: Quickly share your code context with ChatGPT, Claude, or other AI assistants | ||
| - **Code Reviews**: Copy relevant files for review discussions | ||
| - **Documentation**: Generate clean file listings for documentation | ||
| - **Project Sharing**: Share project structure and key files with teammates | ||
| - **Debugging Help**: Include relevant code and project structure when asking for help | ||
| ```bash | ||
| scriptmonkey --ask "How do I organize this project better?" --tree | ||
| ``` | ||
| ## Requirements | ||
| - Python 3.6 or later | ||
| - Cross-platform clipboard support (automatic) | ||
| The `--tree` flag will include a tree representation of the current working directory in the context for your question. This is particularly useful when you want to get feedback on the structure of your codebase or when your question relates to the project organization. It can be used in tandem with the `--files` flag to provide additional context about how those files fit within the larger context of the project. | ||
| ## Examples | ||
| ScriptMonkey will analyze your question and any provided files or the directory tree to give a detailed, markdown-formatted response with explanations and code suggestions, if applicable. This feature is great for in-depth guidance on code optimization, architecture, or general programming questions. | ||
| **Copy a single Python file:** | ||
| ```bash | ||
| scriptmonkey --files main.py | ||
| ``` | ||
| ### Copy Key Files and Project Details with `--copy` | ||
| **Copy multiple configuration files:** | ||
| ```bash | ||
| scriptmonkey --files config.json requirements.txt .env.example | ||
| ``` | ||
| ScriptMonkey's new `--copy` feature is designed to streamline the process of copying critical code files and project structure details into your clipboard, making it easier to ask questions to LLMs like ChatGPT or Claude. This feature formats the copied content in a neat way that includes file contents and the directory tree, making it simple to paste into a conversation for contextual help. | ||
| #### How to Use | ||
| - **Copy file contents directly**: | ||
| You can use the --copy flag to quickly copy the contents of specified files into your clipboard, neatly formatted for easy sharing with an LLM. When combined with the --files flag, ScriptMonkey will copy the contents of the selected files along with a complete directory tree of your project. This provides additional context, helping LLMs better understand your project’s structure: | ||
| ```bash | ||
| scriptmonkey --copy --files path/to/file1.py path/to/file2.js | ||
| ``` | ||
| Your files and project directory tree are automatically copied to your clipboard in the format: | ||
| ``` | ||
| - - - - - - - - - - | ||
| Here are some details about the project. | ||
| # path/to/file1.py | ||
| <content from file1.py> | ||
| - - - - - - - - - - | ||
| # path/to/file2.js | ||
| <content from file2.js> | ||
| - - - - - - - - - - | ||
| # PROJECT TREE | ||
| <directory structure> | ||
| ``` | ||
| ### Error Handling with `scriptmonkey.run()` | ||
| ScriptMonkey doesn't just build projects; it also makes debugging a breeze. | ||
| 1. Import `scriptmonkey` in your Python script. | ||
| 2. Call `scriptmonkey.run()` to activate the error handler. | ||
| 3. Run your code, and let ScriptMonkey handle any errors that occur. | ||
| #### Example | ||
| ```python | ||
| import scriptmonkey | ||
| # Enable ScriptMonkey's error handler | ||
| scriptmonkey.run() | ||
| # Intentional error for testing | ||
| def add(a, b): | ||
| return a + b # This will fail if b is a string | ||
| print(add(2, "3")) # ScriptMonkey will automatically fix this error and update the file. | ||
| **Just the directory tree:** | ||
| ```bash | ||
| scriptmonkey --tree | ||
| ``` | ||
| Once an error occurs, ScriptMonkey will: | ||
| 1. Detect the error. | ||
| 2. Send the error and code to OpenAI for analysis. | ||
| 3. Provide a solution and automatically update the file with the corrected code. | ||
| ### Setting or Updating Your OpenAI API Key | ||
| If you haven't set your OpenAI API key yet or need to update it, you can do so with the following command: | ||
| **Everything together:** | ||
| ```bash | ||
| python3 -m scriptmonkey --set-api-key your-api-key | ||
| scriptmonkey --files src/*.py --tree | ||
| ``` | ||
| This will store the API key in a config locally and use it for all future interactions with OpenAI. | ||
| ## Legacy Compatibility | ||
| ## Requirements | ||
| - Python 3.6 or later | ||
| - An OpenAI API key (follow the steps below if you don't have one) | ||
| For users upgrading from ScriptMonkey 1.x: The `scriptmonkey.run()` function is still available but no longer provides error handling functionality. It exists only for backward compatibility and does nothing. | ||
| ## Obtaining an OpenAI API Key | ||
| --- | ||
| 1. Go to the OpenAI website: [OpenAI Platform](https://platform.openai.com/) | ||
| 2. Sign up or log in to your account. | ||
| 3. Navigate to the **API keys** section in your account dashboard. | ||
| 4. Create a new API key and copy it. | ||
| ## Configuring the API Key for ScriptMonkey | ||
| - **Option 1: Environment Variable** | ||
| You can set up your API key as an environment variable: | ||
| ```bash | ||
| export OPENAI_API_KEY='your-api-key' | ||
| ``` | ||
| - **Option 2: Entering the API Key When Prompted** | ||
| If ScriptMonkey does not find an API key in your environment variables, it will prompt you to enter your key. Once entered, it will save the key to a configuration file for future use. | ||
| - **Option 3: Using the `--set-api-key` Command** | ||
| Use the `--set-api-key` command as shown above to set or update your API key easily. | ||
| Let ScriptMonkey take care of your Python errors and project setup so you can focus on building! | ||
| **Version 2.0.0** - Simplified for focus and reliability. No more AI dependencies, just pure clipboard utility goodness! 🐒 |
@@ -1,6 +0,2 @@ | ||
| openai | ||
| pydantic | ||
| tqdm | ||
| python-dotenv | ||
| rich | ||
| pyperclip |
@@ -5,5 +5,3 @@ README.md | ||
| scriptmonkey/__main__.py | ||
| scriptmonkey/agents.py | ||
| scriptmonkey/core.py | ||
| scriptmonkey/scripting.py | ||
| scriptmonkey.egg-info/PKG-INFO | ||
@@ -15,14 +13,4 @@ scriptmonkey.egg-info/SOURCES.txt | ||
| scriptmonkey.egg-info/top_level.txt | ||
| scriptmonkey/openai_client/__init__.py | ||
| scriptmonkey/openai_client/basemodels.py | ||
| scriptmonkey/openai_client/client.py | ||
| scriptmonkey/openai_client/prompting.py | ||
| scriptmonkey/openai_client/prompts/fix_error.txt | ||
| scriptmonkey/openai_client/prompts/project_description.txt | ||
| scriptmonkey/utils/__init__.py | ||
| scriptmonkey/utils/fence.py | ||
| scriptmonkey/utils/file_handler.py | ||
| scriptmonkey/utils/key_manager.py | ||
| scriptmonkey/utils/parsers.py | ||
| scriptmonkey/utils/tree.py | ||
| scriptmonkey/utils/ui.py | ||
| scriptmonkey/utils/tree.py |
+24
-126
| import os | ||
| import sys | ||
| import time | ||
| import argparse | ||
| import platform | ||
| import traceback | ||
| from pprint import pprint | ||
| from rich.console import Console | ||
| from .utils.ui import Spinner, cli_text_editor | ||
| from .utils.key_manager import update_api_key | ||
| from .utils.file_handler import read_file, write_file, copy_files_to_clipboard | ||
| from .agents import ( | ||
| ask_gpt_with_files, | ||
| generate_project_structure, | ||
| build_project, | ||
| generate_readme, | ||
| ) | ||
| from .utils.tree import create_tree | ||
| from .utils.file_handler import read_file, copy_to_clipboard | ||
| from .openai_client.basemodels import ScriptMonkeyResponse | ||
| from .openai_client import ( | ||
| chatgpt_json, | ||
| default_prompts, | ||
| ) | ||
| console = Console() | ||
| CONFIG_FILE = os.path.expanduser("~/.scriptmonkey_config") | ||
| def get_platform(): | ||
| os_name = platform.system() | ||
| os_version = platform.release() | ||
| return f"# Operating System: {os_name}, Version: {os_version}\n\n" | ||
| def scriptmonkey_exception_handler(exc_type, exc_value, exc_traceback): | ||
| if issubclass(exc_type, KeyboardInterrupt): | ||
| sys.__excepthook__(exc_type, exc_value, exc_traceback) | ||
| return | ||
| error_message = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback)) | ||
| print(f"\n🐒 ScriptMonkey Detected an Error:") | ||
| print(error_message, "\n") | ||
| frame = traceback.extract_tb(exc_traceback)[-1] | ||
| file_path = frame.filename | ||
| original_code = read_file(file_path) | ||
| content = f"{get_platform()}# Original Code:\n```\n{original_code}\n```\n\n# Error Message:\n{error_message}" | ||
| solution = None | ||
| with Spinner("🐒 ScriptMonkey is working on a solution"): | ||
| solution = chatgpt_json( | ||
| instructions=default_prompts.fix_error, content=content, response_format=ScriptMonkeyResponse | ||
| ) | ||
| print(f"\n🐒 ScriptMonkey Fixed It:\nProblem:\n{solution['problem']}\n") | ||
| print(f"Suggested Solution:\n{solution['solution']}\n") | ||
| corrected_code = solution["corrected_code"].replace("```python", "").replace("```", "") | ||
| write_file(file_path, corrected_code) | ||
| print(f"🐒 ScriptMonkey automatically fixed your code at: '{file_path}'.") | ||
| def run(): | ||
| sys.excepthook = scriptmonkey_exception_handler | ||
| def handle_no_prompt(): | ||
| print(f"\nNo Prompt Provided (Tip: Did you save before closing the editor?).\n🐒 Quitting ScriptMonkey...\n") | ||
| exit() | ||
| def main(): | ||
| parser = argparse.ArgumentParser(description="ScriptMonkey - Generate Python projects and fix code.") | ||
| parser.add_argument("--ask", nargs="?", const=True, help="Ask a question to ChatGPT", type=str) | ||
| parser.add_argument("--files", nargs="*", help="Paths to files to include in the prompt", type=str) | ||
| parser.add_argument("--tree", help="Include a directory tree in the prompt", action="store_true") | ||
| parser.add_argument("--set-api-key", help="Set the OpenAI API key", action="store_true") | ||
| parser.add_argument( | ||
| "--copy", help="Copy the content of the specified files to the clipboard", action="store_true" | ||
| ) # New --copy flag | ||
| parser = argparse.ArgumentParser(description="ScriptMonkey - Copy files and directory trees to clipboard") | ||
| parser.add_argument("--files", nargs="*", help="Paths to files to copy to clipboard", type=str) | ||
| parser.add_argument("--tree", help="Include directory tree in clipboard", action="store_true") | ||
| args = parser.parse_args() | ||
| print(f"\n- - 🐒 WELCOME TO SCRIPT MONKEY 🐒 - - -\n") | ||
| print(f"\n🐒 ScriptMonkey - File & Tree Clipboard Utility\n") | ||
| if args.set_api_key: | ||
| # Handle setting the API key | ||
| update_api_key() | ||
| # Check if any valid arguments were provided | ||
| if not args.files and not args.tree: | ||
| console.print("[bold red]❌ Please specify --files, --tree, or both.[/bold red]") | ||
| parser.print_help() | ||
| return | ||
| if args.copy: | ||
| # Handle the --copy functionality | ||
| file_paths = args.files if args.files else [] | ||
| if not file_paths: | ||
| console.print("[bold red]❌ No files specified to copy. Use --files to specify file paths.[/bold red]") | ||
| return | ||
| include_tree = args.tree | ||
| copy_files_to_clipboard(file_paths) | ||
| return | ||
| # Handle the clipboard functionality | ||
| file_paths = args.files if args.files else [] | ||
| include_tree = args.tree | ||
| if args.ask is not None: | ||
| # Handle the --ask functionality | ||
| # Check if the --ask flag was used without a direct question (e.g., `--ask` alone) | ||
| if args.ask is True: | ||
| question = cli_text_editor(mode="ASK") | ||
| if not question: | ||
| handle_no_prompt() | ||
| else: | ||
| question = args.ask | ||
| # Validate that files exist if provided | ||
| if file_paths: | ||
| for file_path in file_paths: | ||
| if not os.path.exists(file_path): | ||
| console.print(f"[bold red]❌ File not found: {file_path}[/bold red]") | ||
| return | ||
| file_paths = args.files if args.files else [] | ||
| include_tree = args.tree | ||
| ask_gpt_with_files(question, file_paths, include_tree) | ||
| return | ||
| else: | ||
| # Handle the build project functionality | ||
| print(f"Opening prompt editor... ") | ||
| time.sleep(2) | ||
| copy_to_clipboard(file_paths, include_tree) | ||
| # Step 1: Get multi-line project description from user | ||
| project_description = cli_text_editor(mode="BUILD") | ||
| if not project_description: | ||
| handle_no_prompt() | ||
| print(f"Project Description: {project_description}") | ||
| # Step 2: Generate the project structure using OpenAI API | ||
| project_structure = generate_project_structure(project_description) | ||
| print(f"\n🐒 ScriptMonkey created a project blueprint:") | ||
| pprint(project_structure) | ||
| # Step 3: Create the project structure (directories and files) on the filesystem | ||
| print(f"\n🐒 ScriptMonkey is coding...") | ||
| build_project(project_structure_response=project_structure, project_description=project_description) | ||
| print("\nProject structure creation complete.") | ||
| # Step 4: Generate the README.md content based on the project description and structure | ||
| readme_content = generate_readme(project_description, project_structure) | ||
| readme_path = "./generated_project/README.md" | ||
| with open(readme_path, "w") as readme_file: | ||
| readme_file.write(readme_content) | ||
| print(f"🐒 ScriptMonkey wrote a README.md file at: '{readme_path}'") | ||
| return | ||
| # Legacy function for backward compatibility (now does nothing) | ||
| def run(): | ||
| """Legacy function - no longer provides error handling.""" | ||
| pass |
@@ -12,30 +12,54 @@ import os | ||
| def copy_files_to_clipboard(file_paths, include_tree=True): | ||
| def copy_to_clipboard(file_paths, include_tree=False): | ||
| """ | ||
| Reads the content from the specified files and copies it to the clipboard in the specified format. | ||
| Optionally includes a project directory tree. | ||
| Reads content from specified files and/or directory tree and copies to clipboard. | ||
| Also prints the output to terminal. | ||
| """ | ||
| formatted_output = "- - - - - - - - - -\nHere are some details about the project.\n\n" | ||
| if not file_paths and not include_tree: | ||
| console.print("[bold red]❌ No files or tree specified to copy.[/bold red]") | ||
| return | ||
| for path in file_paths: | ||
| try: | ||
| content = read_file(path) | ||
| formatted_output += f"# {path}\n{content}\n\n- - - - - - - - - -\n" | ||
| except FileNotFoundError: | ||
| console.print(f"[bold yellow]Warning: {path} not found. Skipping this file.[/bold yellow]") | ||
| except Exception as e: | ||
| console.print(f"[bold red]Error reading {path}: {e}[/bold red]") | ||
| formatted_output = "" | ||
| # Handle files if provided | ||
| if file_paths: | ||
| formatted_output += "- - - - - - - - - -\nHere are some details about the project.\n\n" | ||
| for path in file_paths: | ||
| try: | ||
| content = read_file(path) | ||
| formatted_output += f"# {path}\n{content}\n\n- - - - - - - - - -\n\n" | ||
| except FileNotFoundError: | ||
| console.print(f"[bold yellow]Warning: {path} not found. Skipping this file.[/bold yellow]") | ||
| except Exception as e: | ||
| console.print(f"[bold red]Error reading {path}: {e}[/bold red]") | ||
| # Include the directory tree if requested | ||
| if include_tree: | ||
| start_directory = os.getcwd() | ||
| dir_name = os.path.basename(start_directory) | ||
| tree = create_tree(start_directory) | ||
| formatted_output += "- - - - - - - - - -\n\n# PROJECT TREE\n" | ||
| formatted_output += f"{tree}\n\n" | ||
| if file_paths: | ||
| formatted_output += "# PROJECT TREE\n" | ||
| formatted_output += f"{dir_name}\n{tree}" | ||
| # Copy the formatted output to the clipboard | ||
| pyperclip.copy(formatted_output) | ||
| console.print("[green]🐒 Content has been copied to the clipboard.[/green]") | ||
| # Copy the formatted output to the clipboard and print to terminal | ||
| if formatted_output: | ||
| pyperclip.copy(formatted_output.strip()) | ||
| # Print the output to terminal | ||
| print(formatted_output.strip()) | ||
| print() # Add some spacing | ||
| # Show what was copied | ||
| if file_paths and include_tree: | ||
| console.print(f"[green]🐒 Copied {len(file_paths)} file(s) and directory tree to clipboard.[/green]") | ||
| elif file_paths: | ||
| console.print(f"[green]🐒 Copied {len(file_paths)} file(s) to clipboard.[/green]") | ||
| elif include_tree: | ||
| console.print("[green]🐒 Copied directory tree to clipboard.[/green]") | ||
| else: | ||
| console.print("[bold red]❌ Nothing to copy.[/bold red]") | ||
| def read_file(path: str) -> str: | ||
@@ -42,0 +66,0 @@ """Loads a file and returns the content. |
+212
-117
| import os | ||
| from collections import defaultdict | ||
| # SHALLOW DIRECTORIES (Non-Recursive Scan) | ||
| # These directories are shown but their contents are not scanned | ||
| SHALLOW_DIRS = { | ||
| "node_modules", | ||
| ".git", | ||
| "build", | ||
| "dist", | ||
| "vendor", | ||
| "__pycache__", | ||
| "venv", | ||
| ".venv", | ||
| ".bundle", | ||
| "DerivedData", | ||
| "site", | ||
| "assets", | ||
| "img", | ||
| "fonts", | ||
| ".cache", | ||
| # Vendored dependency rules | ||
| "deps", | ||
| "google", | ||
| "pyasn1_modules", | ||
| "babel", | ||
| "espeak-ng-data", | ||
| "templates", | ||
| "migrations", | ||
| "lib", | ||
| "meta", | ||
| # Model directory rules | ||
| "embedding_models", | ||
| "imagegen_models", | ||
| "textgen_models", | ||
| "tts_models", | ||
| "whisper_models", | ||
| # Test directory rules | ||
| "test", | ||
| "tests", | ||
| "__tests__", | ||
| # Project-specific config/output rules | ||
| ".erb", | ||
| ".vscode", | ||
| ".github", | ||
| "release", | ||
| # Rust-specific rules | ||
| "debug", | ||
| "incremental", | ||
| } | ||
| def create_tree(start_path, prefix="", max_depth=None, current_depth=0, max_files_per_type=5): | ||
| # FULL EXCLUSION (Completely Hidden) | ||
| # These files/directories are completely hidden from the tree | ||
| EXCLUDE_PATTERNS = [ | ||
| ".DS_Store", | ||
| "*.log", | ||
| "*.swp", | ||
| "*.swo", | ||
| "*~", | ||
| "#*#", | ||
| "*.pyc", | ||
| "*.class", | ||
| "*.o", | ||
| "*.dylib", | ||
| "*.so", | ||
| "*.dll", | ||
| "*.a", | ||
| ".eslintcache", | ||
| ".prettiercache", | ||
| "Thumbs.db", | ||
| ".gitkeep", | ||
| "*.ico", | ||
| "*.icns", | ||
| "*.patch", | ||
| "*.metadata", | ||
| "sitemap.xml", | ||
| "sitemap.xml.gz", | ||
| "*.gguf.lock", | ||
| "CACHEDIR.TAG", | ||
| # Generated & vendor file rules | ||
| "*_pb2.py", | ||
| "*_pb2.pyi", | ||
| "cacerts.txt", | ||
| "*.dat", | ||
| "*_dict", | ||
| ] | ||
| # RUST RELEASE EXCLUSION (For target/release) | ||
| # These items are hidden within the 'release' directory | ||
| RELEASE_EXCLUDE_PATTERNS = { | ||
| ".cargo-lock", | ||
| ".fingerprint", | ||
| "build", | ||
| "deps", | ||
| "examples", | ||
| "incremental", | ||
| } | ||
| def create_tree(start_path, prefix="", max_depth=None, current_depth=0): | ||
| """ | ||
| Generates a directory tree as a string with options to limit files of the same type, | ||
| ignore directories, and handle critical code files. | ||
| Generates a directory tree as a string with intelligent filtering for source code projects. | ||
| Mimics the codetree bash script behavior with shallow directories, exclusions, and special handling. | ||
| """ | ||
@@ -15,128 +109,129 @@ # If max_depth is defined and the current depth exceeds it, stop recursion | ||
| tree = "" | ||
| files = sorted(os.listdir(start_path)) | ||
| # Group files by their extensions | ||
| files_by_extension = defaultdict(list) | ||
| for name in files: | ||
| if os.path.isdir(os.path.join(start_path, name)): | ||
| files_by_extension["<dir>"].append(name) | ||
| else: | ||
| _, ext = os.path.splitext(name) | ||
| files_by_extension[ext].append(name) | ||
| try: | ||
| all_items = sorted(os.listdir(start_path)) | ||
| except (OSError, PermissionError): | ||
| return "" | ||
| # Build a list of files to display | ||
| display_files = [] | ||
| # Filter out excluded items | ||
| items_to_process = [] | ||
| for item in all_items: | ||
| if not is_excluded(item): | ||
| items_to_process.append(item) | ||
| # Add directories first | ||
| display_files.extend(sorted(files_by_extension["<dir>"])) | ||
| count = len(items_to_process) | ||
| # Add files, limiting non-important file types | ||
| for ext, ext_files in files_by_extension.items(): | ||
| if ext == "<dir>": | ||
| continue | ||
| if ext in important_extensions: | ||
| # Include all files of important types | ||
| display_files.extend(sorted(ext_files)) | ||
| for i, item in enumerate(items_to_process): | ||
| full_path = os.path.join(start_path, item) | ||
| connector = "└── " if i == count - 1 else "├── " | ||
| next_prefix = prefix + (" " if i == count - 1 else "│ ") | ||
| if os.path.isdir(full_path): | ||
| # Special handling for Rust target directory | ||
| if item == "target": | ||
| tree += prefix + connector + item + "\n" | ||
| tree += handle_target_directory(full_path, next_prefix) | ||
| elif is_shallow(item): | ||
| tree += prefix + connector + item + "/ [...]" + "\n" | ||
| else: | ||
| tree += prefix + connector + item + "\n" | ||
| tree += create_tree( | ||
| full_path, | ||
| next_prefix, | ||
| max_depth=max_depth, | ||
| current_depth=current_depth + 1, | ||
| ) | ||
| else: | ||
| # Limit the number of files to `max_files_per_type` for non-important types | ||
| display_files.extend(sorted(ext_files)[:max_files_per_type]) | ||
| if len(ext_files) > max_files_per_type: | ||
| display_files.append(f"... ({len(ext_files) - max_files_per_type} more {ext} files omitted)") | ||
| tree += prefix + connector + item + "\n" | ||
| # Iterate over the files and directories to build the tree | ||
| for index, name in enumerate(display_files): | ||
| path = os.path.join(start_path, name) | ||
| return tree | ||
| # Skip ignored directories | ||
| if os.path.isdir(path) and name in ignored_dirs: | ||
| continue | ||
| connector = "└── " if index == len(display_files) - 1 else "├── " | ||
| tree += prefix + connector + name + "\n" | ||
| def handle_target_directory(target_path, prefix): | ||
| """Special handling for Rust target directories""" | ||
| tree = "" | ||
| release_path = os.path.join(target_path, "release") | ||
| debug_path = os.path.join(target_path, "debug") | ||
| if os.path.isdir(path): | ||
| new_prefix = prefix + (" " if index == len(display_files) - 1 else "│ ") | ||
| tree += create_tree( | ||
| path, | ||
| new_prefix, | ||
| max_depth=max_depth, | ||
| current_depth=current_depth + 1, | ||
| max_files_per_type=max_files_per_type, | ||
| ) | ||
| sub_items = [] | ||
| if os.path.isdir(release_path): | ||
| sub_items.append("release") | ||
| if os.path.isdir(debug_path): | ||
| sub_items.append("debug") | ||
| sub_count = len(sub_items) | ||
| j = 0 | ||
| if os.path.isdir(release_path): | ||
| j += 1 | ||
| release_connector = "└── " if j == sub_count else "├── " | ||
| release_next_prefix = prefix + (" " if j == sub_count else "│ ") | ||
| tree += prefix + release_connector + "release" + "\n" | ||
| # Filter release directory contents | ||
| try: | ||
| release_contents = sorted(os.listdir(release_path)) | ||
| release_filtered = [] | ||
| for r_item in release_contents: | ||
| if not is_release_excluded(r_item): | ||
| release_filtered.append(r_item) | ||
| r_count = len(release_filtered) | ||
| for k, r_item in enumerate(release_filtered): | ||
| r_item_path = os.path.join(release_path, r_item) | ||
| r_connector = "└── " if k == r_count - 1 else "├── " | ||
| display_name = r_item | ||
| if os.path.isdir(r_item_path): | ||
| display_name += "/ [...]" | ||
| tree += release_next_prefix + r_connector + display_name + "\n" | ||
| except (OSError, PermissionError): | ||
| pass | ||
| if os.path.isdir(debug_path): | ||
| j += 1 | ||
| debug_connector = "└── " if j == sub_count else "├── " | ||
| tree += prefix + debug_connector + "debug/ [...]" + "\n" | ||
| return tree | ||
| ignored_dirs = { | ||
| "venv", | ||
| ".venv", | ||
| "dist", | ||
| "build", | ||
| "__pycache__", | ||
| "node_modules", | ||
| ".next", | ||
| "out", | ||
| ".nuxt", | ||
| "public", | ||
| "jspm_packages", | ||
| ".parcel-cache", | ||
| ".vercel", | ||
| "target", | ||
| ".gradle", | ||
| ".mvn", | ||
| "bin", | ||
| "obj", | ||
| "coverage", | ||
| "vendor", | ||
| "storage", | ||
| "cache", | ||
| ".git", | ||
| ".idea", | ||
| ".vscode", | ||
| ".DS_Store", | ||
| "logs", | ||
| "log", | ||
| "tmp", | ||
| "temp", | ||
| ".angular", | ||
| ".bundle", | ||
| "vendor/bundle", | ||
| "htmlcov", | ||
| ".mypy_cache", | ||
| ".pytest_cache", | ||
| } | ||
| def is_shallow(dir_name): | ||
| """Check if directory should be shown as shallow (contents not scanned)""" | ||
| if dir_name in SHALLOW_DIRS: | ||
| return True | ||
| # Define important file extensions | ||
| important_extensions = { | ||
| # General programming and scripting languages | ||
| ".py", | ||
| ".js", | ||
| ".ts", | ||
| ".tsx", | ||
| ".jsx", | ||
| ".java", | ||
| ".cpp", | ||
| ".c", | ||
| ".h", | ||
| ".hpp", | ||
| ".go", | ||
| ".rb", | ||
| ".rs", | ||
| ".php", | ||
| ".sh", | ||
| ".pl", | ||
| ".swift", | ||
| ".kt", | ||
| ".kts", | ||
| ".dart", | ||
| ".scala", | ||
| ".lua", | ||
| ".r", | ||
| ".jl", | ||
| ".cs", | ||
| ".csx", | ||
| ".m", | ||
| ".mm", | ||
| ".bat", | ||
| ".cmd", | ||
| } | ||
| # Check for wildcard patterns | ||
| if dir_name.endswith(".egg-info"): | ||
| return True | ||
| if dir_name.endswith(".dist-info"): | ||
| return True | ||
| return False | ||
| def is_excluded(item_name): | ||
| """Check if item should be completely excluded""" | ||
| for pattern in EXCLUDE_PATTERNS: | ||
| if pattern.startswith("*") and pattern.endswith("*"): | ||
| # Contains pattern | ||
| if pattern[1:-1] in item_name: | ||
| return True | ||
| elif pattern.startswith("*"): | ||
| # Ends with pattern | ||
| if item_name.endswith(pattern[1:]): | ||
| return True | ||
| elif pattern.endswith("*"): | ||
| # Starts with pattern | ||
| if item_name.startswith(pattern[:-1]): | ||
| return True | ||
| else: | ||
| # Exact match | ||
| if item_name == pattern: | ||
| return True | ||
| return False | ||
| def is_release_excluded(item_name): | ||
| """Check if item should be excluded from target/release directory""" | ||
| return item_name in RELEASE_EXCLUDE_PATTERNS |
+11
-27
@@ -5,4 +5,4 @@ from setuptools import setup, find_packages | ||
| name="scriptmonkey", | ||
| version="1.4.4", | ||
| description="A Python package that generates complex software projects and fixes errors in your code using OpenAI's GPT API.", | ||
| version="2.0.0", | ||
| description="A simple CLI utility for copying files and directory trees to clipboard for easy sharing with LLMs.", | ||
| long_description=open("README.md", "r").read(), | ||
@@ -15,3 +15,3 @@ long_description_content_type="text/markdown", | ||
| packages=find_packages(), | ||
| install_requires=["openai", "pydantic", "tqdm", "python-dotenv", "rich", "pyperclip"], | ||
| install_requires=["rich", "pyperclip"], | ||
| python_requires=">=3.6", | ||
@@ -29,30 +29,14 @@ entry_points={ | ||
| keywords=[ | ||
| "openai", | ||
| "GPT", | ||
| "AI", | ||
| "project generation", | ||
| "multi-file project", | ||
| "complex project generator", | ||
| "code error fixing", | ||
| "code automation", | ||
| "GPT API", | ||
| "error correction", | ||
| "code assistant", | ||
| "AI coding tools", | ||
| "script monkey", | ||
| "automation", | ||
| "clipboard", | ||
| "file sharing", | ||
| "directory tree", | ||
| "development tools", | ||
| "python tools", | ||
| "code analysis", | ||
| "machine learning", | ||
| "project bootstrap", | ||
| "custom code generation", | ||
| "python package", | ||
| "cursor", | ||
| "devin", | ||
| "copilot", | ||
| "automatic code correction", | ||
| "LLM helper", | ||
| "code sharing", | ||
| "project structure", | ||
| "file utility", | ||
| "developer tools", | ||
| ], | ||
| package_data={"scriptmonkey.openai_client": ["prompts/*.txt"]}, | ||
| include_package_data=True, | ||
| ) |
| import os | ||
| from rich.console import Console | ||
| from .utils.tree import create_tree | ||
| from .utils.file_handler import read_file | ||
| from .utils.ui import render_response_with_syntax_highlighting | ||
| from .utils.parsers import remove_code_block_lines | ||
| from .openai_client.client import chatgpt_json, chatgpt | ||
| from .openai_client.basemodels import ProjectStructureResponse | ||
| console = Console() | ||
| def generate_project_structure(description: str) -> ProjectStructureResponse: | ||
| """Generates the project structure based on the user's project description using OpenAI.""" | ||
| instructions = ( | ||
| "Generate a detailed project structure for a multi-level application. The project will be placed directly inside a folder named 'generated_project'." | ||
| "\n- Do NOT include 'generated_project/' as part of the paths. All paths should be relative to the root of the project directory, meaning they should start directly with the file or folder names as if they are inside 'generated_project'." | ||
| "\n- Provide a list of directories and files with their full relative paths." | ||
| "\n- Each directory should end with a '/' to indicate that it is a folder." | ||
| "\n- For each file or directory, include a 'description' that explains its purpose." | ||
| "\n- If the file is a Python code file, also include a 'functions' list. For each function, include:" | ||
| "\n - 'function_name': The name of the function." | ||
| "\n - 'description': A description of what the function does." | ||
| "\n - 'inputs': A list of the function's expected inputs, including data types." | ||
| "\n - 'outputs': A list of the function's expected outputs, including data types." | ||
| "\n- Do not include any extra explanations, commentary, or introductory text. Only provide the structured data as requested." | ||
| ) | ||
| # Call the chatgpt_json function to get structured project plan | ||
| project_structure = chatgpt_json( | ||
| instructions=instructions, content=description, response_format=ProjectStructureResponse | ||
| ) | ||
| return project_structure | ||
| def generate_code_for_file(file_description: dict, project_description: str, project_files: list) -> str: | ||
| """ | ||
| Generates content for a given file based on its description using the chatgpt() function. | ||
| Args: | ||
| file_description (dict): The description of the file for which content is being generated. | ||
| project_description (str): A high-level description of the project's purpose and goals. | ||
| project_files (list): List of all project files for context. | ||
| Returns: | ||
| str: The generated content for the file. | ||
| """ | ||
| # Gather context about the project goal and other files | ||
| context = gather_project_context(project_description, project_files) | ||
| # Extract the file extension to inform the content type | ||
| file_extension = os.path.splitext(file_description["path"])[1].lower().strip(".") | ||
| # Dynamically adjust the content type description based on the file extension | ||
| if file_extension: | ||
| content_type_description = f"{file_extension.upper()} file content" | ||
| else: | ||
| content_type_description = "text content" | ||
| # Prepare instructions for OpenAI to generate content based on the file description and type | ||
| instructions = ( | ||
| f"Write the complete content for a {content_type_description} that fulfills the following requirements. " | ||
| "Consider the context of the entire project when generating the content and make use of imports where available and appropriate." | ||
| "Use relevant imports, references, and appropriate formatting or structure where necessary. Do not add extra commentary or explanation. " | ||
| "Make sure to return the content directly, without wrapping it in any code fences like triple quotes or backticks ." | ||
| "i.e. DO NOT include any triple backtrick wrappers at all for any code, (e.g. ```python<content here>```) just return the code as plain text." | ||
| f"\n\nFile Description: {file_description['description']}" | ||
| f"\n\n{context}\n" | ||
| ) | ||
| # Include functions for code files (if provided) | ||
| if file_description.get("functions"): | ||
| instructions += "\n\nFunctions:\n" | ||
| for function in file_description["functions"]: | ||
| instructions += ( | ||
| f"- {function['function_name']}: {function['description']} " | ||
| f"(Inputs: {function['inputs']}, Outputs: {function['outputs']})\n" | ||
| ) | ||
| # Call the chatgpt function to generate the content | ||
| generated_content = chatgpt(prompt=instructions) | ||
| # Clean up any unintended code blocks | ||
| generated_content = remove_code_block_lines(generated_content) | ||
| return generated_content | ||
| def build_project( | ||
| project_structure_response: dict, project_description: str, base_directory: str = "./generated_project" | ||
| ): | ||
| """Creates the directories and files for the project and generates code content for all file types.""" | ||
| # Extract the list of project files for context | ||
| project_files = project_structure_response["files"] | ||
| # Iterate through each file in the project structure | ||
| for project_file in project_files: | ||
| file_path = os.path.join(base_directory, project_file["path"].lstrip("/")) | ||
| # Check if it's a directory or file (directories end with '/') | ||
| if file_path.endswith("/"): | ||
| os.makedirs(file_path, exist_ok=True) | ||
| print(f"🐒 ScriptMonkey created directory: {file_path}") | ||
| else: | ||
| os.makedirs(os.path.dirname(file_path), exist_ok=True) | ||
| # Generate content for all files, including Python, HTML, JSON, CSS, etc. | ||
| generated_content = generate_code_for_file(project_file, project_description, project_files) | ||
| # Write the generated content to the file if it doesn't already exist | ||
| if not os.path.exists(file_path): | ||
| with open(file_path, "w") as f: | ||
| f.write(generated_content) | ||
| print(f"🐒 ScriptMonkey created file with generated content at: '{file_path}'.") | ||
| else: | ||
| print(f"File already exists, skipping: {file_path}") | ||
| def gather_project_context(project_description: str, project_files: list) -> str: | ||
| """ | ||
| Gathers a summary of the project goal and all existing files with their key functions or classes. | ||
| Args: | ||
| project_description (str): A high-level description of the project's purpose and goals. | ||
| project_files (list): List of project file descriptions. | ||
| Returns: | ||
| str: A summary of the project goal and existing modules, classes, and functions. | ||
| """ | ||
| context = f"Project Goal: {project_description}\n\n" | ||
| context += "Project Context:\n" | ||
| for file in project_files: | ||
| if file["functions"]: | ||
| context += f"- In '{file['path']}', the following functions are defined:\n" | ||
| for function in file["functions"]: | ||
| context += f" - {function['function_name']}: {function['description']} (Inputs: {function['inputs']}, Outputs: {function['outputs']})\n" | ||
| else: | ||
| context += f"- '{file['path']}' is defined with no specific functions listed.\n" | ||
| return context | ||
| def generate_readme(description: str, project_structure: dict) -> str: | ||
| """Generates a README.md content based on the project description and structure.""" | ||
| instructions = ( | ||
| "Write a complete README.md file based on the following project details. " | ||
| "The README should include the project overview, installation instructions, usage guide, file structure summary, key features, and configuration details. " | ||
| "Make sure the README is well-structured and formatted using Markdown without wrapping the entire README in backticks or any other non-readme commentary." | ||
| "Do not include any commentary, explanations, or text outside of the README content." | ||
| f"\n\nProject Description: {description}\n" | ||
| f"\nProject Structure: {project_structure}\n" | ||
| ) | ||
| readme_content = chatgpt(prompt=instructions) | ||
| readme_content = readme_content.strip("```markdown").strip("```") | ||
| return readme_content | ||
| def ask_gpt_with_files(question, file_paths, include_tree=False): | ||
| """ | ||
| Constructs a detailed and flexible prompt for ChatGPT using a question and optionally including content from specified files. | ||
| """ | ||
| prompt = ( | ||
| f"### Question:\n" | ||
| f"{question}\n\n" | ||
| "If I have included any files below, you can use them for additional context for this question. " | ||
| "Please analyze the provided files below (if available) as needed and reference them when forming your answer. " | ||
| "If the answer involves code, please format any code examples using Markdown with properly labeled language-specific code blocks. " | ||
| "Your response should be in Markdown format to preserve readability.\n\n" | ||
| ) | ||
| if file_paths: | ||
| prompt += "### Files Provided:\n" | ||
| for path in file_paths: | ||
| try: | ||
| content = read_file(path) | ||
| prompt += ( | ||
| f"## File: {path}\n" | ||
| f"The content of the file '{path}' is included below. Use this as context for answering the question:\n\n" | ||
| f"```\n{content}\n```\n\n" | ||
| ) | ||
| except FileNotFoundError: | ||
| console.print(f"[bold yellow]Warning: {path} not found. Skipping this file.[/bold yellow]") | ||
| except Exception as e: | ||
| console.print(f"[bold red]Error reading {path}: {e}[/bold red]") | ||
| else: | ||
| prompt += ( | ||
| "No specific files have been provided, so please base your response solely on the question above. " | ||
| "If the response includes any code examples or technical explanations, please use Markdown formatting with language-specific code blocks for clarity.\n" | ||
| ) | ||
| # Include the directory tree if the flag is set | ||
| if include_tree: | ||
| start_directory = os.getcwd() | ||
| tree = create_tree(start_directory) | ||
| prompt += "### Directory Tree:\n" | ||
| prompt += f"The directory tree of the current working directory is included below (up to a depth of 6 levels):\n\n```\n{tree}\n```\n\n" | ||
| console.print("- - Directory Tree - -") | ||
| console.print(tree) | ||
| # Output the constructed prompt to the console for transparency | ||
| console.rule("🐒 ScriptMonkey is Thinking 🐒") | ||
| console.rule() | ||
| # Use the OpenAI API to get a response | ||
| try: | ||
| response = chatgpt(prompt=prompt) | ||
| # Display the response using rich markdown and detect code blocks | ||
| console.rule("🐒 ANSWER 🐒") | ||
| render_response_with_syntax_highlighting(response) | ||
| console.print("\n") | ||
| console.rule() | ||
| except Exception as e: | ||
| console.print(f"[bold red]Error using OpenAI API: {e}[/bold red]") |
| from .prompting import DefaultPrompts | ||
| from .client import chatgpt_json, chatgpt | ||
| default_prompts = DefaultPrompts() |
| from pydantic import BaseModel | ||
| from typing import List, Optional | ||
| class FunctionDetails(BaseModel): | ||
| function_name: str | ||
| description: str | ||
| inputs: Optional[List[str]] # List of inputs with data types | ||
| outputs: Optional[List[str]] # List of outputs with data types | ||
| class ProjectFile(BaseModel): | ||
| path: str # The full path to the file or directory | ||
| description: str # High-level purpose of the directory or file | ||
| functions: Optional[List[FunctionDetails]] # List of functions/classes if it's a code file | ||
| class ProjectStructureResponse(BaseModel): | ||
| files: List[ProjectFile] # List of all files and directories in the project | ||
| class ScriptMonkeyResponse(BaseModel): | ||
| problem: str # A description of the error/problem | ||
| solution: str # The solution to the problem | ||
| corrected_code: str # The corrected version of the Python code |
| import os | ||
| from dotenv import load_dotenv | ||
| from pydantic import BaseModel | ||
| import openai | ||
| CONFIG_FILE = os.path.expanduser("~/.scriptmonkey_config") | ||
| # Load environment variables from the .env file if present | ||
| load_dotenv() | ||
| def get_openai_api_key(): | ||
| # Try to get the API key from environment variables | ||
| api_key = os.getenv("OPENAI_API_KEY") | ||
| # If not set, check the config file | ||
| if not api_key and os.path.exists(CONFIG_FILE): | ||
| with open(CONFIG_FILE, "r") as f: | ||
| api_key = f.read().strip() | ||
| # If still not set, prompt the user for it | ||
| if not api_key: | ||
| print("It looks like your OpenAI API key isn't set.") | ||
| api_key = input("🐒 Please paste your OpenAI API key here and press ENTER: ").strip() | ||
| # Save the key to the config file for future use | ||
| with open(CONFIG_FILE, "w") as f: | ||
| f.write(api_key) | ||
| print(f"Your API key has been saved to {CONFIG_FILE} for future use.") | ||
| return api_key | ||
| # Get the OpenAI API key using the function | ||
| OPENAI_API_KEY = get_openai_api_key() | ||
| # Initialize the OpenAI client with the obtained API key | ||
| client = openai.OpenAI(api_key=OPENAI_API_KEY) | ||
| def chatgpt_json(instructions: str, content: str, response_format: BaseModel) -> dict: | ||
| """This function is used to return content from OpenAI Chat Completions API as a structured dictionary response. | ||
| Args: | ||
| instructions (str): Instructions for the LLM (how the LLM should process the input content). | ||
| content (str): Information that the LLM is supposed to process. | ||
| response_format (BaseModel): The output format defined by a Pydantic Basemodel. | ||
| Returns: | ||
| dict: The structured output response from the LLM. | ||
| """ | ||
| completion = client.beta.chat.completions.parse( | ||
| model="gpt-4o-2024-08-06", | ||
| messages=[ | ||
| {"role": "system", "content": instructions}, | ||
| {"role": "user", "content": content}, | ||
| ], | ||
| response_format=response_format, | ||
| ) | ||
| structured_response = completion.choices[0].message.parsed | ||
| return structured_response.model_dump() | ||
| def chatgpt(prompt: str, model="gpt-4o", max_tokens=None): | ||
| """Function for generating responses to text prompts with OpenAI's ChatGPT API | ||
| Args: | ||
| prompt (str): The instructions for ChatGPT to respond to | ||
| model (str, optional): ChatGPT model to use. Defaults to "gpt-4o". | ||
| - List of Available Models: https://platform.openai.com/docs/models/continuous-model-upgrades | ||
| max_tokens (int, optional): Optional, the max tokens to be returned by response. Defaults to no limit (i.e. None). | ||
| Returns: | ||
| str: Returns the response to the prompt as a string value | ||
| """ | ||
| completion = client.chat.completions.create( | ||
| model=model, | ||
| max_tokens=max_tokens, | ||
| messages=[{"role": "user", "content": prompt}], | ||
| ) | ||
| response = completion.choices[0].message.content | ||
| return response |
| import os | ||
| def load_prompt(path: str) -> str: | ||
| path = os.path.join(os.path.dirname(__file__), path) | ||
| with open(path, "r") as file: | ||
| return file.read() | ||
| class DefaultPrompts: | ||
| def __init__(self): | ||
| self.fix_error = load_prompt(path="./prompts/fix_error.txt") |
| You are a Python programming expert that helps solve Python code errors. | ||
| When you fix Python code, your improvements should be well designed and follow Python best practices. | ||
| While you are at it, also make improvements like: | ||
| - Improving the code structure, formatting, and readability. | ||
| - Adding type hints to functions and classes. | ||
| - Organizing import statements. | ||
| - Leave short comments in the code describing the fix, and always start your explanations of fixes with: "SCRIPTMONKEY: " | ||
| NEVER remove any code related to scriptmonkey imports or statements. | ||
| - scriptmonkey.run() should always be the first statement in the file after the imports | ||
| Return the solution in a structured JSON format. |
| Build a Flask web application with user authentication, a PostgreSQL database, and an admin dashboard. |
| import os | ||
| from .openai_client import chatgpt | ||
| from .utils.parsers import remove_code_block_lines | ||
| def generate_code_for_file(file_description: dict, project_description: str, project_files: list) -> str: | ||
| """ | ||
| Generates content for a given file based on its description using the chatgpt() function. | ||
| Args: | ||
| file_description (dict): The description of the file for which content is being generated. | ||
| project_description (str): A high-level description of the project's purpose and goals. | ||
| project_files (list): List of all project files for context. | ||
| Returns: | ||
| str: The generated content for the file. | ||
| """ | ||
| # Gather context about the project goal and other files | ||
| context = gather_project_context(project_description, project_files) | ||
| # Extract the file extension to inform the content type | ||
| file_extension = os.path.splitext(file_description["path"])[1].lower().strip(".") | ||
| # Dynamically adjust the content type description based on the file extension | ||
| if file_extension: | ||
| content_type_description = f"{file_extension.upper()} file content" | ||
| else: | ||
| content_type_description = "text content" | ||
| # Prepare instructions for OpenAI to generate content based on the file description and type | ||
| instructions = ( | ||
| f"Write the complete content for a {content_type_description} that fulfills the following requirements. " | ||
| "Consider the context of the entire project when generating the content and make use of imports where available and appropriate." | ||
| "Use relevant imports, references, and appropriate formatting or structure where necessary. Do not add extra commentary or explanation. " | ||
| "Make sure to return the content directly, without wrapping it in any code fences like triple quotes or backticks ." | ||
| "i.e. DO NOT include any triple backtrick wrappers at all for any code, (e.g. ```python<content here>```) just return the code as plain text." | ||
| f"\n\nFile Description: {file_description['description']}" | ||
| f"\n\n{context}\n" | ||
| ) | ||
| # Include functions for code files (if provided) | ||
| if file_description.get("functions"): | ||
| instructions += "\n\nFunctions:\n" | ||
| for function in file_description["functions"]: | ||
| instructions += ( | ||
| f"- {function['function_name']}: {function['description']} " | ||
| f"(Inputs: {function['inputs']}, Outputs: {function['outputs']})\n" | ||
| ) | ||
| # Call the chatgpt function to generate the content | ||
| generated_content = chatgpt(prompt=instructions) | ||
| # Clean up any unintended code blocks | ||
| generated_content = remove_code_block_lines(generated_content) | ||
| return generated_content | ||
| def create_project_structure( | ||
| project_structure_response: dict, project_description: str, base_directory: str = "./generated_project" | ||
| ): | ||
| """Creates the directories and files for the project and generates code content for all file types.""" | ||
| # Extract the list of project files for context | ||
| project_files = project_structure_response["files"] | ||
| # Iterate through each file in the project structure | ||
| for project_file in project_files: | ||
| file_path = os.path.join(base_directory, project_file["path"].lstrip("/")) | ||
| # Check if it's a directory or file (directories end with '/') | ||
| if file_path.endswith("/"): | ||
| os.makedirs(file_path, exist_ok=True) | ||
| print(f"🐒 ScriptMonkey created directory: {file_path}") | ||
| else: | ||
| os.makedirs(os.path.dirname(file_path), exist_ok=True) | ||
| # Generate content for all files, including Python, HTML, JSON, CSS, etc. | ||
| generated_content = generate_code_for_file(project_file, project_description, project_files) | ||
| # Write the generated content to the file if it doesn't already exist | ||
| if not os.path.exists(file_path): | ||
| with open(file_path, "w") as f: | ||
| f.write(generated_content) | ||
| print(f"🐒 ScriptMonkey created file with generated content at: '{file_path}'.") | ||
| else: | ||
| print(f"File already exists, skipping: {file_path}") | ||
| def gather_project_context(project_description: str, project_files: list) -> str: | ||
| """ | ||
| Gathers a summary of the project goal and all existing files with their key functions or classes. | ||
| Args: | ||
| project_description (str): A high-level description of the project's purpose and goals. | ||
| project_files (list): List of project file descriptions. | ||
| Returns: | ||
| str: A summary of the project goal and existing modules, classes, and functions. | ||
| """ | ||
| context = f"Project Goal: {project_description}\n\n" | ||
| context += "Project Context:\n" | ||
| for file in project_files: | ||
| if file["functions"]: | ||
| context += f"- In '{file['path']}', the following functions are defined:\n" | ||
| for function in file["functions"]: | ||
| context += f" - {function['function_name']}: {function['description']} (Inputs: {function['inputs']}, Outputs: {function['outputs']})\n" | ||
| else: | ||
| context += f"- '{file['path']}' is defined with no specific functions listed.\n" | ||
| return context |
| fence_enums = [ | ||
| "cucumber", | ||
| "abap", | ||
| "ada", | ||
| "ahk", | ||
| "apacheconf", | ||
| "applescript", | ||
| "as", | ||
| "as3", | ||
| "asy", | ||
| "bash", | ||
| "bat", | ||
| "befunge", | ||
| "blitzmax", | ||
| "boo", | ||
| "brainfuck", | ||
| "c", | ||
| "cfm", | ||
| "cheetah", | ||
| "cl", | ||
| "clojure", | ||
| "cmake", | ||
| "coffeescript", | ||
| "console", | ||
| "control", | ||
| "cpp", | ||
| "csharp", | ||
| "css", | ||
| "cython", | ||
| "d", | ||
| "delphi", | ||
| "diff", | ||
| "dpatch", | ||
| "duel", | ||
| "dylan", | ||
| "erb", | ||
| "erl", | ||
| "erlang", | ||
| "evoque", | ||
| "factor", | ||
| "felix", | ||
| "fortran", | ||
| "gas", | ||
| "genshi", | ||
| "gitignore", | ||
| "glsl", | ||
| "gnuplot", | ||
| "go", | ||
| "groff", | ||
| "haml", | ||
| "haskell", | ||
| "html", | ||
| "hx", | ||
| "hybris", | ||
| "ini", | ||
| "io", | ||
| "ioke", | ||
| "irc", | ||
| "jade", | ||
| "java", | ||
| "js", | ||
| "jsp", | ||
| "lhs", | ||
| "llvm", | ||
| "logtalk", | ||
| "lua", | ||
| "make", | ||
| "mako", | ||
| "maql", | ||
| "mason", | ||
| "markdown", | ||
| "modelica", | ||
| "modula2", | ||
| "moocode", | ||
| "mupad", | ||
| "mxml", | ||
| "myghty", | ||
| "nasm", | ||
| "newspeak", | ||
| "objdump", | ||
| "objectivec", | ||
| "objectivej", | ||
| "ocaml", | ||
| "ooc", | ||
| "perl", | ||
| "php", | ||
| "postscript", | ||
| "pot", | ||
| "pov", | ||
| "prolog", | ||
| "properties", | ||
| "protobuf", | ||
| "py3tb", | ||
| "pytb", | ||
| "python", | ||
| "r", | ||
| "rb", | ||
| "rconsole", | ||
| "rebol", | ||
| "redcode", | ||
| "rhtml", | ||
| "rst", | ||
| "sass", | ||
| "scala", | ||
| "scaml", | ||
| "scheme", | ||
| "scss", | ||
| "smalltalk", | ||
| "smarty", | ||
| "sourceslist", | ||
| "splus", | ||
| "sql", | ||
| "sqlite3", | ||
| "squidconf", | ||
| "ssp", | ||
| "tcl", | ||
| "tcsh", | ||
| "tex", | ||
| "text", | ||
| "v", | ||
| "vala", | ||
| "vbnet", | ||
| "velocity", | ||
| "vim", | ||
| "xml", | ||
| "xquery", | ||
| "xslt", | ||
| "yaml", | ||
| ] |
| import os | ||
| import sys | ||
| CONFIG_FILE = os.path.expanduser("~/.scriptmonkey_config") | ||
| # - - - - - API KEY MANAGEMENT - - - - - | ||
| def save_api_key(api_key: str): | ||
| """Save the OpenAI API key to the configuration file and environment variable.""" | ||
| with open(CONFIG_FILE, "w") as file: | ||
| file.write(api_key) | ||
| os.environ["OPENAI_API_KEY"] = api_key | ||
| print(f"✅ OpenAI API key saved to {CONFIG_FILE}.") | ||
| def get_openai_api_key() -> str: | ||
| """Retrieve the OpenAI API key from environment or configuration file.""" | ||
| api_key = os.getenv("OPENAI_API_KEY") | ||
| if not api_key: | ||
| # Check for API key in the configuration file | ||
| if os.path.exists(CONFIG_FILE): | ||
| with open(CONFIG_FILE, "r") as file: | ||
| api_key = file.read().strip() | ||
| # Prompt user for API key if not found | ||
| if not api_key: | ||
| print("🐒 ScriptMonkey requires an OpenAI API key to function.") | ||
| api_key = input("Please enter your OpenAI API key: ") | ||
| if api_key: | ||
| save_api_key(api_key) | ||
| if not api_key: | ||
| print("❌ No API key provided. Exiting ScriptMonkey.") | ||
| sys.exit(1) | ||
| return api_key | ||
| def update_api_key(): | ||
| """Prompt the user to update the OpenAI API key.""" | ||
| api_key = input("Enter the new OpenAI API key: ") | ||
| if api_key: | ||
| save_api_key(api_key) | ||
| print("✅ OpenAI API key updated successfully.") | ||
| else: | ||
| print("❌ No API key provided. The API key was not updated.") | ||
| # - - - - - NEW FEATURES - - - - - |
| def remove_code_block_lines(input_string): | ||
| """Removes any lines from the input string that contain '```'.""" | ||
| lines = input_string.splitlines() | ||
| filtered_lines = [line for line in lines if "```" not in line] | ||
| return "\n".join(filtered_lines) |
| import re | ||
| import os | ||
| import sys | ||
| import time | ||
| import itertools | ||
| import platform | ||
| import threading | ||
| import subprocess | ||
| import tempfile | ||
| from rich.syntax import Syntax | ||
| from rich.console import Console | ||
| from rich.markdown import Markdown | ||
| console = Console() | ||
| class Spinner: | ||
| def __init__(self, message="Processing"): | ||
| self.spinner = itertools.cycle(["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]) | ||
| self.stop_running = threading.Event() | ||
| self.spin_thread = None | ||
| self.message = message | ||
| def spin(self): | ||
| while not self.stop_running.is_set(): | ||
| sys.stdout.write(f"\r{self.message} {next(self.spinner)}") | ||
| sys.stdout.flush() | ||
| time.sleep(0.1) | ||
| sys.stdout.write("\r" + " " * (len(self.message) + 2) + "\r") | ||
| sys.stdout.flush() | ||
| def __enter__(self): | ||
| self.spin_thread = threading.Thread(target=self.spin) | ||
| self.spin_thread.start() | ||
| def __exit__(self, exc_type, exc_val, exc_tb): | ||
| self.stop_running.set() | ||
| self.spin_thread.join() | ||
| def cli_text_editor(mode: str) -> str: | ||
| """ | ||
| Opens the user's default text editor for entering multi-line input. | ||
| The user is provided with instructions within the temporary file, | ||
| adjusted based on the detected editor. | ||
| :mode: enums['BUILD', 'ASK'] | ||
| """ | ||
| if mode == "BUILD": | ||
| purpose = "Project Builder" | ||
| user_prompt = "Please describe your project in detail below." | ||
| elif mode == "ASK": | ||
| purpose = "Prompt Editor" | ||
| user_prompt = "Please write your question down below." | ||
| with tempfile.NamedTemporaryFile(suffix=".txt") as temp_file: | ||
| # Detect the editor from the environment or default based on the OS | ||
| editor = os.environ.get("EDITOR") | ||
| # If no editor is set, choose a default based on the platform | ||
| if not editor: | ||
| if platform.system() == "Windows": | ||
| editor = "notepad" | ||
| else: | ||
| editor = "nano" # Default for Unix-like systems | ||
| # Adjust instructions based on the detected editor | ||
| if "vim" in editor.lower(): | ||
| instructions = ( | ||
| f"!# 🐒 Welcome to ScriptMonkey's {purpose}!\n" | ||
| f"!# {user_prompt}\n" | ||
| "!# Use 'i' to start editing, and when you're done, press 'Esc',\n" | ||
| "!# type ':wq' to save and exit.\n" | ||
| "!# (Lines starting with '!#' will be ignored.)\n\n" | ||
| ) | ||
| elif "nano" in editor.lower(): | ||
| instructions = ( | ||
| f"!# 🐒 Welcome to ScriptMonkey's {purpose}!\n" | ||
| f"!# {user_prompt}\n" | ||
| "!# When you're done, press 'Ctrl+O' to save and 'Ctrl+X' to exit.\n" | ||
| "!# (Lines starting with '!#' will be ignored.)\n\n" | ||
| ) | ||
| elif "notepad" in editor.lower(): | ||
| instructions = ( | ||
| f"!# 🐒 Welcome to ScriptMonkey's {purpose}!\n" | ||
| f"!# {user_prompt}\n" | ||
| "!# When you're done, save and close the Notepad window.\n" | ||
| "!# (Lines starting with '!#' will be ignored.)\n\n" | ||
| ) | ||
| elif "code" in editor.lower(): | ||
| instructions = ( | ||
| f"!# 🐒 Welcome to ScriptMonkey's {purpose}!\n" | ||
| f"!# {user_prompt}\n" | ||
| "!# When you're done, save the file and close the editor window.\n" | ||
| "!# (Lines starting with '!#' will be ignored.)\n\n" | ||
| ) | ||
| else: | ||
| instructions = ( | ||
| f"!# 🐒 Welcome to ScriptMonkey's {purpose}!\n" | ||
| f"!# {user_prompt}\n" | ||
| "!# Save and close the editor when you're done.\n" | ||
| "!# (Lines starting with '!#' will be ignored.)\n\n" | ||
| ) | ||
| # Write the instructions to the temporary file | ||
| temp_file.write(instructions.encode("utf-8")) | ||
| temp_file.flush() | ||
| # Open the temporary file in the detected editor | ||
| subprocess.call([editor, temp_file.name]) | ||
| # Read the user's input, ignoring lines starting with '!#' | ||
| temp_file.seek(0) | ||
| user_input = temp_file.read().decode("utf-8") | ||
| user_input = "\n".join(line for line in user_input.splitlines() if not line.startswith("!#")) | ||
| return user_input.strip() | ||
| def render_response_with_syntax_highlighting(response): | ||
| """ | ||
| Render a ChatGPT response with syntax highlighting for detected code blocks. | ||
| """ | ||
| # Regular expression to detect code blocks with a specified language (e.g., ```python) | ||
| code_block_pattern = re.compile(r"```(\w+)?\n(.*?)```", re.DOTALL) | ||
| last_pos = 0 | ||
| # Iterate over all detected code blocks | ||
| for match in code_block_pattern.finditer(response): | ||
| language = match.group(1) or "text" # Default to 'text' if no language is specified | ||
| code_content = match.group(2) | ||
| # Print any text before the code block as markdown | ||
| if match.start() > last_pos: | ||
| pre_text = response[last_pos : match.start()] | ||
| console.print(Markdown(pre_text)) | ||
| # Print the code block with syntax highlighting | ||
| syntax = Syntax(f"\n{code_content}", language, theme="monokai", line_numbers=False) | ||
| console.print("\n") | ||
| console.print(syntax) | ||
| console.print("\n") | ||
| last_pos = match.end() | ||
| # Print any remaining text after the last code block | ||
| if last_pos < len(response): | ||
| console.print(Markdown(response[last_pos:])) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
24969
-67.34%16
-42.86%345
-65.43%