code-puppy 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_puppy-0.0.2/.gitignore +12 -0
- code_puppy-0.0.2/PKG-INFO +133 -0
- code_puppy-0.0.2/README.md +108 -0
- code_puppy-0.0.2/code_agent/__init__.py +0 -0
- code_puppy-0.0.2/code_agent/agent.py +19 -0
- code_puppy-0.0.2/code_agent/agent_prompts.py +52 -0
- code_puppy-0.0.2/code_agent/main.py +234 -0
- code_puppy-0.0.2/code_agent/models/__init__.py +4 -0
- code_puppy-0.0.2/code_agent/models/codesnippet.py +20 -0
- code_puppy-0.0.2/code_agent/tools/__init__.py +4 -0
- code_puppy-0.0.2/code_agent/tools/command_runner.py +187 -0
- code_puppy-0.0.2/code_agent/tools/common.py +3 -0
- code_puppy-0.0.2/code_agent/tools/file_modifications.py +264 -0
- code_puppy-0.0.2/code_agent/tools/file_operations.py +350 -0
- code_puppy-0.0.2/code_agent/tools/web_search.py +41 -0
- code_puppy-0.0.2/pyproject.toml +49 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-puppy
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Code generation agent similar to Windsurf or Cursor
|
|
5
|
+
Author: Windsurf Engineering Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Requires-Dist: bs4>=0.0.2
|
|
16
|
+
Requires-Dist: httpx>=0.24.1
|
|
17
|
+
Requires-Dist: logfire>=0.7.1
|
|
18
|
+
Requires-Dist: pydantic-ai>=0.1.0
|
|
19
|
+
Requires-Dist: pydantic>=2.4.0
|
|
20
|
+
Requires-Dist: pytest-cov>=6.1.1
|
|
21
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
22
|
+
Requires-Dist: rich>=13.4.2
|
|
23
|
+
Requires-Dist: ruff>=0.11.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Code Generation Agent
|
|
27
|
+
|
|
28
|
+
## Overview
|
|
29
|
+
|
|
30
|
+
This project is a sophisticated AI-powered code generation agent, designed to understand programming tasks, generate high-quality code, and explain its reasoning similar to tools like Windsurf and Cursor.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Multi-language support**: Capable of generating code in various programming languages.
|
|
35
|
+
- **Interactive CLI**: A command-line interface for interactive use.
|
|
36
|
+
- **Detailed explanations**: Provides insights into generated code to understand its logic and structure.
|
|
37
|
+
- **Easy Integration**: Embed it seamlessly into Python projects.
|
|
38
|
+
|
|
39
|
+
## New Feature
|
|
40
|
+
- **Real-time collaboration**: Allows multiple users to collaboratively edit and review code generation tasks in real-time.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
> **NOTE:** This project uses [astral-sh/uv](https://github.com/astral-sh/uv) for all dependency management and builds. Please install [uv](https://github.com/astral-sh/uv) before continuing.
|
|
45
|
+
|
|
46
|
+
1. **Clone the repository**:
|
|
47
|
+
```bash
|
|
48
|
+
git clone <repository_url>
|
|
49
|
+
cd <repository_name>
|
|
50
|
+
```
|
|
51
|
+
2. **Install dependencies**:
|
|
52
|
+
```bash
|
|
53
|
+
uv pip install -e .
|
|
54
|
+
```
|
|
55
|
+
3. **(optional)** If contributing, install additional development dependencies:
|
|
56
|
+
```bash
|
|
57
|
+
uv pip install -r dev-requirements.txt # If present
|
|
58
|
+
```
|
|
59
|
+
4. **Configure environment variables**:
|
|
60
|
+
- Create an `.env` file in the root, using `.env.example` as a template, to store required API keys.
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### Command Line Interface
|
|
65
|
+
|
|
66
|
+
Run specific tasks or engage in interactive mode:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Execute a task directly
|
|
70
|
+
uv run python main.py "write me a C++ hello world program in /tmp/main.cpp then compile it and run it"
|
|
71
|
+
|
|
72
|
+
# Enter interactive mode
|
|
73
|
+
uv run python main.py --interactive
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Python API
|
|
77
|
+
|
|
78
|
+
Utilize the agent programmatically within your Python scripts:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import asyncio
|
|
82
|
+
from code_agent.agent_tools import generate_code
|
|
83
|
+
|
|
84
|
+
async def main():
|
|
85
|
+
task = "Your task description"
|
|
86
|
+
response = await generate_code(None, task)
|
|
87
|
+
|
|
88
|
+
if response.success:
|
|
89
|
+
for snippet in response.snippets:
|
|
90
|
+
print(f"Language: {snippet.language}")
|
|
91
|
+
print(snippet.code)
|
|
92
|
+
print(snippet.explanation)
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
asyncio.run(main())
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Explore the `examples` directory for elaborated utilization samples.
|
|
99
|
+
|
|
100
|
+
## Project Structure
|
|
101
|
+
|
|
102
|
+
- **`code_agent/agent.py`** - Core functionalities of the agent.
|
|
103
|
+
- **`code_agent/agent_tools.py`** - Tools and utilities for code generation.
|
|
104
|
+
- **`code_agent/agent_prompts.py`** - Templates and prompts used by the system.
|
|
105
|
+
- **`code_agent/models/`** - Data models for defining code and responses.
|
|
106
|
+
- **`examples/`** - Example scripts showcasing agent capabilities.
|
|
107
|
+
- **`main.py`** - Entry point for command-line interactions.
|
|
108
|
+
|
|
109
|
+
## Contributing
|
|
110
|
+
|
|
111
|
+
Contributions are welcome! Please follow these steps:
|
|
112
|
+
|
|
113
|
+
1. Fork the repository.
|
|
114
|
+
2. Create a new branch (`git checkout -b feature/xyz`).
|
|
115
|
+
3. Commit your changes (`git commit -m 'Add feature'`).
|
|
116
|
+
4. Push to the branch (`git push origin feature/xyz`).
|
|
117
|
+
5. Open a Pull Request.
|
|
118
|
+
|
|
119
|
+
## Requirements
|
|
120
|
+
|
|
121
|
+
- Python 3.9+
|
|
122
|
+
- [uv](https://github.com/astral-sh/uv) (for dependency management & builds)
|
|
123
|
+
- OpenAI API key (for GPT models)
|
|
124
|
+
- Optionally: Gemini API key (for Google's Gemini models)
|
|
125
|
+
|
|
126
|
+
## Troubleshooting
|
|
127
|
+
|
|
128
|
+
- Ensure all dependencies are installed correctly via uv and the environment is properly configured.
|
|
129
|
+
- Check that API keys are valid and not expired.
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Code Generation Agent
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This project is a sophisticated AI-powered code generation agent, designed to understand programming tasks, generate high-quality code, and explain its reasoning similar to tools like Windsurf and Cursor.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Multi-language support**: Capable of generating code in various programming languages.
|
|
10
|
+
- **Interactive CLI**: A command-line interface for interactive use.
|
|
11
|
+
- **Detailed explanations**: Provides insights into generated code to understand its logic and structure.
|
|
12
|
+
- **Easy Integration**: Embed it seamlessly into Python projects.
|
|
13
|
+
|
|
14
|
+
## New Feature
|
|
15
|
+
- **Real-time collaboration**: Allows multiple users to collaboratively edit and review code generation tasks in real-time.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
> **NOTE:** This project uses [astral-sh/uv](https://github.com/astral-sh/uv) for all dependency management and builds. Please install [uv](https://github.com/astral-sh/uv) before continuing.
|
|
20
|
+
|
|
21
|
+
1. **Clone the repository**:
|
|
22
|
+
```bash
|
|
23
|
+
git clone <repository_url>
|
|
24
|
+
cd <repository_name>
|
|
25
|
+
```
|
|
26
|
+
2. **Install dependencies**:
|
|
27
|
+
```bash
|
|
28
|
+
uv pip install -e .
|
|
29
|
+
```
|
|
30
|
+
3. **(optional)** If contributing, install additional development dependencies:
|
|
31
|
+
```bash
|
|
32
|
+
uv pip install -r dev-requirements.txt # If present
|
|
33
|
+
```
|
|
34
|
+
4. **Configure environment variables**:
|
|
35
|
+
- Create an `.env` file in the root, using `.env.example` as a template, to store required API keys.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Command Line Interface
|
|
40
|
+
|
|
41
|
+
Run specific tasks or engage in interactive mode:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Execute a task directly
|
|
45
|
+
uv run python main.py "write me a C++ hello world program in /tmp/main.cpp then compile it and run it"
|
|
46
|
+
|
|
47
|
+
# Enter interactive mode
|
|
48
|
+
uv run python main.py --interactive
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Python API
|
|
52
|
+
|
|
53
|
+
Utilize the agent programmatically within your Python scripts:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
from code_agent.agent_tools import generate_code
|
|
58
|
+
|
|
59
|
+
async def main():
|
|
60
|
+
task = "Your task description"
|
|
61
|
+
response = await generate_code(None, task)
|
|
62
|
+
|
|
63
|
+
if response.success:
|
|
64
|
+
for snippet in response.snippets:
|
|
65
|
+
print(f"Language: {snippet.language}")
|
|
66
|
+
print(snippet.code)
|
|
67
|
+
print(snippet.explanation)
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
asyncio.run(main())
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Explore the `examples` directory for elaborated utilization samples.
|
|
74
|
+
|
|
75
|
+
## Project Structure
|
|
76
|
+
|
|
77
|
+
- **`code_agent/agent.py`** - Core functionalities of the agent.
|
|
78
|
+
- **`code_agent/agent_tools.py`** - Tools and utilities for code generation.
|
|
79
|
+
- **`code_agent/agent_prompts.py`** - Templates and prompts used by the system.
|
|
80
|
+
- **`code_agent/models/`** - Data models for defining code and responses.
|
|
81
|
+
- **`examples/`** - Example scripts showcasing agent capabilities.
|
|
82
|
+
- **`main.py`** - Entry point for command-line interactions.
|
|
83
|
+
|
|
84
|
+
## Contributing
|
|
85
|
+
|
|
86
|
+
Contributions are welcome! Please follow these steps:
|
|
87
|
+
|
|
88
|
+
1. Fork the repository.
|
|
89
|
+
2. Create a new branch (`git checkout -b feature/xyz`).
|
|
90
|
+
3. Commit your changes (`git commit -m 'Add feature'`).
|
|
91
|
+
4. Push to the branch (`git push origin feature/xyz`).
|
|
92
|
+
5. Open a Pull Request.
|
|
93
|
+
|
|
94
|
+
## Requirements
|
|
95
|
+
|
|
96
|
+
- Python 3.9+
|
|
97
|
+
- [uv](https://github.com/astral-sh/uv) (for dependency management & builds)
|
|
98
|
+
- OpenAI API key (for GPT models)
|
|
99
|
+
- Optionally: Gemini API key (for Google's Gemini models)
|
|
100
|
+
|
|
101
|
+
## Troubleshooting
|
|
102
|
+
|
|
103
|
+
- Ensure all dependencies are installed correctly via uv and the environment is properly configured.
|
|
104
|
+
- Check that API keys are valid and not expired.
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pydantic
|
|
3
|
+
from pydantic_ai import Agent
|
|
4
|
+
from code_agent.agent_prompts import SYSTEM_PROMPT
|
|
5
|
+
|
|
6
|
+
# Check if we have a valid API key
|
|
7
|
+
api_key = os.environ.get("OPENAI_API_KEY", "")
|
|
8
|
+
|
|
9
|
+
class AgentResponse(pydantic.BaseModel):
|
|
10
|
+
"""Represents a response from the agent."""
|
|
11
|
+
output_message: str = pydantic.Field(..., description="The final output message to display to the user")
|
|
12
|
+
awaiting_user_input: bool = pydantic.Field(False, description="True if user input is needed to continue the task")
|
|
13
|
+
|
|
14
|
+
# Create agent with tool usage explicitly enabled
|
|
15
|
+
code_generation_agent = Agent(
|
|
16
|
+
model='openai:gpt-4.1-mini',
|
|
17
|
+
system_prompt=SYSTEM_PROMPT,
|
|
18
|
+
output_type=AgentResponse,
|
|
19
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
SYSTEM_PROMPT = """
|
|
2
|
+
You are a code-agent assistant with the ability to use tools to help users complete coding tasks. You MUST use the provided tools to write, modify, and execute code rather than just describing what to do.
|
|
3
|
+
|
|
4
|
+
Be super informal - we're here to have fun. Writing software is super fun. Don't be scared of being a little bit sarcastic too.
|
|
5
|
+
Be very pedantic about code principles like DRY, YAGNI, and SOLID.
|
|
6
|
+
Be super pedantic about code quality and best practices.
|
|
7
|
+
Be fun and playful. Don't be too serious.
|
|
8
|
+
|
|
9
|
+
Individual files should be very short and concise, at most around 250 lines if possible. If they get longer,
|
|
10
|
+
consider refactoring the code and splitting it into multiple files.
|
|
11
|
+
|
|
12
|
+
Always obey the Zen of Python, even if you are not writing Python code.
|
|
13
|
+
|
|
14
|
+
When given a coding task:
|
|
15
|
+
1. Analyze the requirements carefully
|
|
16
|
+
2. Execute the plan by using appropriate tools
|
|
17
|
+
3. Provide clear explanations for your implementation choices
|
|
18
|
+
4. Continue autonomously whenever possible to achieve the task.
|
|
19
|
+
|
|
20
|
+
YOU MUST USE THESE TOOLS to complete tasks (do not just describe what should be done - actually do it):
|
|
21
|
+
|
|
22
|
+
File Operations:
|
|
23
|
+
- list_files(directory=".", recursive=True): ALWAYS use this to explore directories before trying to read/modify files
|
|
24
|
+
- read_file(file_path, start_line=0, end_line=None): ALWAYS use this to read existing files before modifying them. Don't read less than 500 lines at a time.
|
|
25
|
+
- create_file(file_path, content=""): Use this to create new files with content
|
|
26
|
+
- modify_file(file_path, proposed_changes, replace_content): Use this to replace specific content in files
|
|
27
|
+
- delete_snippet_from_file(file_path, snippet): Use this to remove specific code snippets from files
|
|
28
|
+
- delete_file(file_path): Use this to remove files when needed
|
|
29
|
+
|
|
30
|
+
System Operations:
|
|
31
|
+
- run_shell_command(command, cwd=None, timeout=60): Use this to execute commands, run tests, or start services
|
|
32
|
+
- web_search(query): Use this to search the web for information
|
|
33
|
+
- web_crawl(url): Use this to crawl a website for information
|
|
34
|
+
|
|
35
|
+
Reasoning & Explanation:
|
|
36
|
+
- share_your_reasoning(reasoning, next_steps=None): Use this to explicitly share your thought process and planned next steps
|
|
37
|
+
|
|
38
|
+
Important rules:
|
|
39
|
+
- You MUST use tools to accomplish tasks - DO NOT just output code or descriptions
|
|
40
|
+
- Before every other tool use, you must use "share_your_reasoning" to explain your thought process and planned next steps
|
|
41
|
+
- Check if files exist before trying to modify or delete them
|
|
42
|
+
- After using system operations tools, always explain the results
|
|
43
|
+
- You're encouraged to loop between share_your_reasoning, file tools, and run_shell_command to test output in order to write programs
|
|
44
|
+
- Aim to continue operations independently unless user input is definitively required.
|
|
45
|
+
|
|
46
|
+
Your solutions should be production-ready, maintainable, and follow best practices for the chosen language.
|
|
47
|
+
|
|
48
|
+
Return your final response as a structured output having the following fields:
|
|
49
|
+
* output_message: The final output message to display to the user
|
|
50
|
+
* awaiting_user_input: True if user input is needed to continue the task. If you get an error, you might consider asking the user for help.
|
|
51
|
+
|
|
52
|
+
"""
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import argparse
|
|
3
|
+
import os
|
|
4
|
+
import readline
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.markdown import Markdown
|
|
8
|
+
from rich.console import ConsoleOptions, RenderResult
|
|
9
|
+
from rich.markdown import CodeBlock
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
from rich.syntax import Syntax
|
|
12
|
+
|
|
13
|
+
# Initialize rich console for pretty output
|
|
14
|
+
from code_agent.tools.common import console
|
|
15
|
+
from code_agent.agent import code_generation_agent
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Define a function to get the secret file path
|
|
19
|
+
def get_secret_file_path():
|
|
20
|
+
hidden_directory = os.path.join(os.path.expanduser("~"), ".agent_secret")
|
|
21
|
+
if not os.path.exists(hidden_directory):
|
|
22
|
+
os.makedirs(hidden_directory)
|
|
23
|
+
return os.path.join(hidden_directory, "history.txt")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def main():
|
|
27
|
+
global shutdown_flag
|
|
28
|
+
|
|
29
|
+
# Load environment variables from .env file
|
|
30
|
+
load_dotenv()
|
|
31
|
+
|
|
32
|
+
# Set up argument parser
|
|
33
|
+
parser = argparse.ArgumentParser(
|
|
34
|
+
description="Code Generation Agent - Similar to Windsurf or Cursor"
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--interactive", "-i", action="store_true", help="Run in interactive mode"
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument("command", nargs="*", help="Run a single command")
|
|
40
|
+
args = parser.parse_args()
|
|
41
|
+
|
|
42
|
+
history_file_path = get_secret_file_path()
|
|
43
|
+
|
|
44
|
+
if args.command:
|
|
45
|
+
# Join the list of command arguments into a single string command
|
|
46
|
+
command = " ".join(args.command)
|
|
47
|
+
try:
|
|
48
|
+
while not shutdown_flag:
|
|
49
|
+
response = await code_generation_agent.run(command)
|
|
50
|
+
console.print(response.output_message)
|
|
51
|
+
if response.awaiting_user_input:
|
|
52
|
+
console.print(
|
|
53
|
+
"[bold red]The agent requires further input. Interactive mode is recommended for such tasks."
|
|
54
|
+
)
|
|
55
|
+
except AttributeError as e:
|
|
56
|
+
console.print(f"[bold red]AttributeError:[/bold red] {str(e)}")
|
|
57
|
+
console.print(
|
|
58
|
+
"[bold yellow]\u26a0 The response might not be in the expected format, missing attributes like 'output_message'."
|
|
59
|
+
)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
console.print(f"[bold red]Unexpected Error:[/bold red] {str(e)}")
|
|
62
|
+
elif args.interactive:
|
|
63
|
+
await interactive_mode(history_file_path)
|
|
64
|
+
else:
|
|
65
|
+
parser.print_help()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Add the file handling functionality for interactive mode
|
|
69
|
+
async def interactive_mode(history_file_path: str) -> None:
|
|
70
|
+
"""Run the agent in interactive mode."""
|
|
71
|
+
console.print("[bold green]Code Generation Agent[/bold green] - Interactive Mode")
|
|
72
|
+
console.print("Type 'exit' or 'quit' to exit the interactive mode.")
|
|
73
|
+
console.print("Type 'clear' to reset the conversation history.")
|
|
74
|
+
|
|
75
|
+
message_history = []
|
|
76
|
+
|
|
77
|
+
# Set up readline history file in home directory
|
|
78
|
+
history_file = os.path.expanduser("~/.code_agent_history.txt")
|
|
79
|
+
history_dir = os.path.dirname(history_file)
|
|
80
|
+
|
|
81
|
+
# Ensure history directory exists
|
|
82
|
+
if history_dir and not os.path.exists(history_dir):
|
|
83
|
+
try:
|
|
84
|
+
os.makedirs(history_dir, exist_ok=True)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
console.print(
|
|
87
|
+
f"[yellow]Warning: Could not create history directory: {e}[/yellow]"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Try to read history file
|
|
91
|
+
try:
|
|
92
|
+
if os.path.exists(history_file):
|
|
93
|
+
readline.read_history_file(history_file)
|
|
94
|
+
except (FileNotFoundError, OSError) as e:
|
|
95
|
+
console.print(f"[yellow]Warning: Could not read history file: {e}[/yellow]")
|
|
96
|
+
|
|
97
|
+
readline.set_history_length(100)
|
|
98
|
+
|
|
99
|
+
while True:
|
|
100
|
+
console.print("[bold blue]Enter your coding task:[/bold blue]")
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
# Simple single-line input
|
|
104
|
+
task = input(">>> ")
|
|
105
|
+
|
|
106
|
+
# Add to readline history if not empty
|
|
107
|
+
if task.strip():
|
|
108
|
+
readline.add_history(task)
|
|
109
|
+
|
|
110
|
+
# Save history
|
|
111
|
+
try:
|
|
112
|
+
readline.write_history_file(history_file)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
console.print(
|
|
115
|
+
f"[yellow]Warning: Could not write history file: {e}[/yellow]"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
except (KeyboardInterrupt, EOFError):
|
|
119
|
+
# Handle Ctrl+C or Ctrl+D
|
|
120
|
+
console.print("\n[yellow]Input cancelled[/yellow]")
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
# Check for exit commands
|
|
124
|
+
if task.strip().lower() in ["exit", "quit"]:
|
|
125
|
+
console.print("[bold green]Goodbye![/bold green]")
|
|
126
|
+
break
|
|
127
|
+
|
|
128
|
+
# Check for clear command
|
|
129
|
+
if task.strip().lower() == "clear":
|
|
130
|
+
message_history = []
|
|
131
|
+
console.print("[bold yellow]Conversation history cleared![/bold yellow]")
|
|
132
|
+
console.print(
|
|
133
|
+
"[dim]The agent will not remember previous interactions.[/dim]\n"
|
|
134
|
+
)
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
if task.strip():
|
|
138
|
+
console.print(f"\n[bold blue]Processing task:[/bold blue] {task}\n")
|
|
139
|
+
|
|
140
|
+
# Write to the secret file for permanent history
|
|
141
|
+
with open(history_file_path, "a") as history_file:
|
|
142
|
+
history_file.write(f"{task}\n")
|
|
143
|
+
|
|
144
|
+
# Counter for consecutive auto-continue invocations
|
|
145
|
+
auto_continue_count = 0
|
|
146
|
+
max_auto_continues = 10
|
|
147
|
+
is_done = False
|
|
148
|
+
|
|
149
|
+
while not is_done and auto_continue_count <= max_auto_continues:
|
|
150
|
+
try:
|
|
151
|
+
prettier_code_blocks()
|
|
152
|
+
|
|
153
|
+
# Only show "asking" message for initial query or if not auto-continuing
|
|
154
|
+
if auto_continue_count == 0:
|
|
155
|
+
console.log(f"Asking: {task}...", style="cyan")
|
|
156
|
+
else:
|
|
157
|
+
console.log(
|
|
158
|
+
f"Auto-continuing ({auto_continue_count}/{max_auto_continues})...",
|
|
159
|
+
style="cyan",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Store agent's full response
|
|
163
|
+
agent_response = None
|
|
164
|
+
|
|
165
|
+
result = await code_generation_agent.run(
|
|
166
|
+
task, message_history=message_history
|
|
167
|
+
)
|
|
168
|
+
# Get the structured response
|
|
169
|
+
agent_response = result.output
|
|
170
|
+
console.print(agent_response.output_message)
|
|
171
|
+
|
|
172
|
+
# Update message history with all messages from this interaction
|
|
173
|
+
message_history = result.new_messages()
|
|
174
|
+
if agent_response:
|
|
175
|
+
# Check if the agent needs user input
|
|
176
|
+
if agent_response.awaiting_user_input:
|
|
177
|
+
console.print(
|
|
178
|
+
"\n[bold yellow]\u26a0 Agent needs your input to continue.[/bold yellow]"
|
|
179
|
+
)
|
|
180
|
+
is_done = True # Exit the loop to get user input
|
|
181
|
+
# Otherwise, auto-continue if we haven't reached the limit
|
|
182
|
+
elif auto_continue_count < max_auto_continues:
|
|
183
|
+
auto_continue_count += 1
|
|
184
|
+
task = "please continue"
|
|
185
|
+
console.print(
|
|
186
|
+
"\n[yellow]Agent continuing automatically...[/yellow]"
|
|
187
|
+
)
|
|
188
|
+
else:
|
|
189
|
+
# Reached max auto-continues
|
|
190
|
+
console.print(
|
|
191
|
+
f"\n[bold yellow]\u26a0 Reached maximum of {max_auto_continues} automatic continuations.[/bold yellow]"
|
|
192
|
+
)
|
|
193
|
+
console.print(
|
|
194
|
+
"[dim]You can enter a new request or type 'please continue' to resume.[/dim]"
|
|
195
|
+
)
|
|
196
|
+
is_done = True
|
|
197
|
+
|
|
198
|
+
# Show context status
|
|
199
|
+
console.print(
|
|
200
|
+
f"[dim]Context: {len(message_history)} messages in history[/dim]\n"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
except Exception:
|
|
204
|
+
console.print_exception(show_locals=True)
|
|
205
|
+
is_done = True
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def prettier_code_blocks():
|
|
209
|
+
class SimpleCodeBlock(CodeBlock):
|
|
210
|
+
def __rich_console__(
|
|
211
|
+
self, console: Console, options: ConsoleOptions
|
|
212
|
+
) -> RenderResult:
|
|
213
|
+
code = str(self.text).rstrip()
|
|
214
|
+
yield Text(self.lexer_name, style="dim")
|
|
215
|
+
syntax = Syntax(
|
|
216
|
+
code,
|
|
217
|
+
self.lexer_name,
|
|
218
|
+
theme=self.theme,
|
|
219
|
+
background_color="default",
|
|
220
|
+
line_numbers=True,
|
|
221
|
+
)
|
|
222
|
+
yield syntax
|
|
223
|
+
yield Text(f"/{self.lexer_name}", style="dim")
|
|
224
|
+
|
|
225
|
+
Markdown.elements["fence"] = SimpleCodeBlock
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def main_entry():
|
|
229
|
+
"""Entry point for the installed CLI tool."""
|
|
230
|
+
asyncio.run(main())
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
if __name__ == "__main__":
|
|
234
|
+
main_entry()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CodeSnippet(BaseModel):
|
|
6
|
+
"""Model representing a code snippet with explanation."""
|
|
7
|
+
|
|
8
|
+
language: str
|
|
9
|
+
code: str
|
|
10
|
+
explanation: Optional[str] = None
|
|
11
|
+
imports: Optional[List[str]] = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CodeResponse(BaseModel):
|
|
15
|
+
"""Model representing a response with code snippets and explanation."""
|
|
16
|
+
|
|
17
|
+
snippets: List[CodeSnippet]
|
|
18
|
+
overall_explanation: Optional[str] = None
|
|
19
|
+
success: bool = True
|
|
20
|
+
error_message: Optional[str] = None
|