From cbbf714deb700948dd5acbb01d3736ae720b259a Mon Sep 17 00:00:00 2001 From: Rich Hankins Date: Wed, 21 Jan 2026 22:53:18 +0000 Subject: [PATCH 1/3] Updated public examples --- examples/python-sdk/README.md | 6 +- examples/python-sdk/README_PROMPT_TO_CODE.md | 219 ------- examples/python-sdk/acp_demo.py | 12 +- examples/python-sdk/acp_example_usage.py | 20 +- examples/python-sdk/basic_usage.py | 59 +- examples/python-sdk/cli_analyzer.py | 23 +- .../python-sdk/demo_session_continuity.py | 12 +- .../docs/AUTOMATIC_TYPE_INFERENCE.md | 83 +-- .../python-sdk/docs/CLAUDE_CODE_CLIENT.md | 37 -- examples/python-sdk/docs/FUNCTION_CALLING.md | 47 +- examples/python-sdk/docs/PROMPT_TO_CODE.md | 80 ++- .../python-sdk/docs/SESSION_CONTINUITY.md | 6 + examples/python-sdk/list_prs.py | 10 +- examples/python-sdk/prompt_to_code.py | 311 ++++++++++ examples/python-sdk/prompt_to_code_v2.py | 578 ++++++++++++++++++ examples/python-sdk/session_usage.py | 18 +- .../python-sdk/success_criteria_example.py | 2 +- .../02_event_listener_builtin.py | 2 +- 18 files changed, 1084 insertions(+), 441 deletions(-) delete mode 100644 examples/python-sdk/README_PROMPT_TO_CODE.md create mode 100644 examples/python-sdk/prompt_to_code.py create mode 100644 examples/python-sdk/prompt_to_code_v2.py diff --git a/examples/python-sdk/README.md b/examples/python-sdk/README.md index d1d7aab..67643fa 100644 --- a/examples/python-sdk/README.md +++ b/examples/python-sdk/README.md @@ -95,8 +95,8 @@ See the [context README](context/README.md) for prerequisites and detailed usage ## Prompt-to-SDK Conversion -### `example_complex_prompt.txt` -A sample complex prompt that demonstrates multiple stages, conditions, and iterations. Use this to test the `/prompt-to-sdk` command. +### `example_prompt.txt` +A sample complex prompt that demonstrates multiple stages, conditions, and iterations. Use this to test the prompt-to-code converter. **Example prompt structure:** ``` @@ -118,7 +118,7 @@ Finally: - Create fix suggestions for top 3 ``` -See [README_PROMPT_TO_CODE.md](README_PROMPT_TO_CODE.md) for more details on prompt-to-code conversion. +See [PROMPT_TO_CODE.md](docs/PROMPT_TO_CODE.md) for more details on prompt-to-code conversion. ## Workflow Patterns diff --git a/examples/python-sdk/README_PROMPT_TO_CODE.md b/examples/python-sdk/README_PROMPT_TO_CODE.md deleted file mode 100644 index 4faee2b..0000000 --- a/examples/python-sdk/README_PROMPT_TO_CODE.md +++ /dev/null @@ -1,219 +0,0 @@ -# Prompt to Code Converter Examples - -This directory contains examples for using the `prompt_to_code.py` tool. - -## Quick Start - -1. **Create a prompt file** describing your workflow: - -```bash -cat > my_workflow.txt << 'EOF' -Analyze all Python files in the src/ directory, identify any security -vulnerabilities or code quality issues, create a detailed report in -markdown format, and if there are any critical security issues found, -generate fix suggestions for each one with specific code changes needed. -EOF -``` - -2. **Convert the prompt to an SDK program**: - -```bash -python ../prompt_to_code.py my_workflow.txt -``` - -3. **Review and run the generated program**: - -```bash -# Review the generated code -cat generated_sdk_program.py - -# Run it -python generated_sdk_program.py -``` - -## Example Prompts - -### Example 1: Security Analysis (included) - -See `example_prompt.txt` for a simple security analysis workflow. - -```bash -python ../prompt_to_code.py example_prompt.txt --output security_analyzer.py -``` - -### Example 2: Test Generation Workflow - -Create a file `test_generation.txt`: - -``` -First, scan all TypeScript files in the components/ directory and identify -which ones are missing unit tests. For each file without tests, create a -comprehensive test file with at least 80% coverage. Then run all the new -tests and if any fail, fix the issues. Finally, generate a summary report -of test coverage improvements. -``` - -Convert it: - -```bash -python ../prompt_to_code.py test_generation.txt --output test_generator.py -``` - -### Example 3: Documentation Generator - -Create a file `doc_generator.txt`: - -``` -Analyze all public functions and classes in the src/ directory. For each -one that's missing a docstring or has an incomplete docstring, generate -comprehensive documentation including description, parameters, return -values, and usage examples. Then validate that all docstrings follow -Google style guide format. -``` - -Convert it: - -```bash -python ../prompt_to_code.py doc_generator.txt --output doc_generator.py -``` - -### Example 4: Code Refactoring Pipeline - -Create a file `refactor_pipeline.txt`: - -``` -Find all functions in the codebase that are longer than 50 lines. For -each one, analyze if it can be broken down into smaller functions. If -yes, refactor it into multiple well-named functions with clear -responsibilities. After each refactoring, run the existing tests to -ensure nothing broke. Keep a log of all refactorings performed. -``` - -Convert it: - -```bash -python ../prompt_to_code.py refactor_pipeline.txt --output refactor_pipeline.py -``` - -## Tips for Writing Good Prompts - -### ✅ Good Prompts - -Good prompts clearly describe: -- **Sequential stages**: What happens first, second, third? -- **Data flow**: What information passes between steps? -- **Conditions**: When should different actions be taken? -- **Iterations**: What collections need processing? -- **Outputs**: What should be created? - -Example: -``` -First, list all Python files in src/. For each file, check if it has -type hints. If not, add type hints. Then run mypy to validate. If -there are errors, fix them. Finally, create a report of all changes. -``` - -This shows: -- Clear stages (list → check → add → validate → fix → report) -- Iteration (for each file) -- Conditions (if not, if errors) -- Output (report) - -### ❌ Less Effective Prompts - -Avoid vague prompts like: -``` -Make the code better -``` - -This doesn't provide enough structure for conversion. - -## Advanced Usage - -### Custom Model - -Use a specific model for conversion: - -```bash -python ../prompt_to_code.py my_prompt.txt --model claude-3-5-sonnet-latest -``` - -### Custom Workspace - -Specify a workspace directory: - -```bash -python ../prompt_to_code.py my_prompt.txt --workspace /path/to/project -``` - -### Custom Output Location - -Save to a specific file: - -```bash -python ../prompt_to_code.py my_prompt.txt --output ~/my_scripts/workflow.py -``` - -## What Gets Generated - -The tool generates a complete Python program with: - -1. **Proper shebang and docstring** -2. **All necessary imports** (Agent, dataclasses, typing, etc.) -3. **Dataclass definitions** for structured data -4. **Agent initialization** with appropriate settings -5. **Workflow implementation** using SDK patterns: - - Sessions for context continuity - - Typed results for decision-making - - Loops for iteration - - Error handling -6. **Main function** that can be run directly -7. **Helpful comments** explaining each stage - -## Modifying Generated Programs - -After generation, you can: - -1. **Add error handling**: -```python -try: - result = agent("Some operation", int) -except AugmentCLIError as e: - print(f"Error: {e}") -``` - -2. **Add logging**: -```python -import logging -logging.basicConfig(level=logging.INFO) -logging.info(f"Processing {len(files)} files") -``` - -3. **Add function calling**: -```python -def run_tests(file: str) -> dict: - """Run tests for a file.""" - # Your implementation - return {"passed": 10, "failed": 0} - -result = agent.run( - "Run tests and analyze results", - return_type=dict, - functions=[run_tests] -) -``` - -4. **Add event listeners**: -```python -from auggie_sdk import LoggingAgentListener - -listener = LoggingAgentListener(verbose=True) -agent = Auggie(listener=listener) -``` - -## See Also - -- [Main Documentation](../docs/PROMPT_TO_CODE.md) -- [Agent Documentation](../docs/AGENT_EVENT_LISTENER.md) -- [Session Continuity](../docs/SESSION_CONTINUITY.md) -- [Function Calling](../docs/FUNCTION_CALLING.md) diff --git a/examples/python-sdk/acp_demo.py b/examples/python-sdk/acp_demo.py index 889a5ac..ecf723d 100644 --- a/examples/python-sdk/acp_demo.py +++ b/examples/python-sdk/acp_demo.py @@ -16,28 +16,32 @@ class DemoListener(AgentEventListener): """Simple listener that prints events in a user-friendly way.""" - def on_agent_message(self, text: str) -> None: + def on_agent_message_chunk(self, text: str) -> None: + """Called when the agent sends a message chunk.""" print(text, end="", flush=True) - def on_tool_call_start( + def on_tool_call( self, tool_call_id: str, title: str, kind: Optional[str] = None, status: Optional[str] = None, ) -> None: + """Called when the agent starts a tool call.""" print(f"\n 🔧 Using tool: {title}", flush=True) - def on_tool_call_update( + def on_tool_response( self, tool_call_id: str, status: Optional[str] = None, content: Optional[Any] = None, ) -> None: + """Called when a tool response is received.""" if status == "completed": print(" ✓ Tool completed", flush=True) def on_agent_thought(self, text: str) -> None: + """Called when the agent shares a thought.""" pass @@ -65,7 +69,7 @@ def main(): print("3️⃣ Sending message that triggers tool calls: 'Read the README.md'\n") print(" Agent response: ", end="") client.send_message( - "Read the file experimental/guy/auggie_sdk/README.md and tell me what it's about in one sentence.", + "Read the README.md file in the current directory and tell me what it's about in one sentence.", timeout=30.0, ) print("\n ✓ Got response (with tool call events shown above)\n") diff --git a/examples/python-sdk/acp_example_usage.py b/examples/python-sdk/acp_example_usage.py index b64b1f0..1a5d14c 100644 --- a/examples/python-sdk/acp_example_usage.py +++ b/examples/python-sdk/acp_example_usage.py @@ -8,18 +8,18 @@ 4. Clearing context """ -from auggie_sdk.acp import AuggieACPClient, AgentEventListener, ModelName +from auggie_sdk.acp import AuggieACPClient, AgentEventListener from typing import Optional, Any class MyEventListener(AgentEventListener): """Example event listener that prints all agent events.""" - def on_agent_message(self, text: str) -> None: + def on_agent_message_chunk(self, text: str) -> None: """Called when the agent sends a message chunk.""" print(f"[AGENT MESSAGE] {text}", end="", flush=True) - def on_tool_call_start( + def on_tool_call( self, tool_call_id: str, title: str, @@ -32,14 +32,14 @@ def on_tool_call_start( print(f" Kind: {kind}") print(f" Status: {status}") - def on_tool_call_update( + def on_tool_response( self, tool_call_id: str, status: Optional[str] = None, content: Optional[Any] = None, ) -> None: - """Called when a tool call is updated.""" - print(f"[TOOL CALL UPDATE] {tool_call_id}") + """Called when a tool response is received.""" + print(f"[TOOL RESPONSE] {tool_call_id}") print(f" Status: {status}") if content: print(f" Content: {str(content)[:100]}...") # Truncate long content @@ -58,7 +58,7 @@ def example_basic_usage(): # Create client without listener # You can optionally specify model and workspace_root: # client = AuggieACPClient( - # model=ModelName.SONNET_4_5, # Use enum for type safety + # model="sonnet4.5", # Model string # workspace_root="/path/to/workspace" # ) client = AuggieACPClient() @@ -95,7 +95,7 @@ def example_with_listener(): print(f"Agent started! Session ID: {client.session_id}\n") # Send a message that will trigger tool calls - message = "Please read the file experimental/guy/auggie_sdk/README.md and summarize it in one sentence." + message = "Please read the README.md file in the current directory and summarize it in one sentence." print(f"Sending: {message}\n") response = client.send_message(message, timeout=30.0) print(f"\n\nFinal Response: {response}\n") @@ -191,9 +191,9 @@ def example_model_and_workspace(): import os # Create client with specific model and workspace - # You can use the ModelName enum for type safety: + # Use model string directly: client = AuggieACPClient( - model=ModelName.SONNET_4_5, # Use enum for type safety + model="sonnet4.5", # Model string workspace_root=os.getcwd(), # Specify workspace root ) diff --git a/examples/python-sdk/basic_usage.py b/examples/python-sdk/basic_usage.py index b743be2..4936a9d 100755 --- a/examples/python-sdk/basic_usage.py +++ b/examples/python-sdk/basic_usage.py @@ -39,55 +39,51 @@ def main(): # Example 1: Automatic type inference (no return_type specified) print("\n1. Automatic type inference:") - result, inferred_type = agent.run("What is 15 + 27?") - print(f"Result: {result} (inferred type: {inferred_type.__name__})") + result = agent.run("What is 15 + 27?") + print(f"Result: {result} (inferred type: {type(result).__name__})") # Example 2: Explicit typed response - integer print("\n2. Explicit typed response (integer):") result = agent.run("What is 15 + 27?", int) print(f"Result: {result} (type: {type(result).__name__})") - # Example 3: @ operator syntax - print("\n3. @ operator syntax:") - result = agent(int) @ "What is 8 * 7?" - print(f"Result: {result} (type: {type(result).__name__})") - - # Example 4: List of strings - print("\n4. List of strings:") - colors = agent(list[str]) @ "Give me 5 colors" + # Example 3: List of strings + print("\n3. List of strings:") + colors = agent.run("Give me 5 colors") print(f"Colors: {colors}") - # Example 5: Dataclass result - print("\n5. Dataclass result:") - task = agent(Task) @ "Create a task: 'Write unit tests', medium priority, 4 hours" + # Example 4: Dataclass result + print("\n4. Dataclass result:") + task = agent.run("Create a task: 'Write unit tests', medium priority, 4 hours", return_type=Task) print( f"Task: {task.title}, Priority: {task.priority}, Hours: {task.estimated_hours}" ) - # Example 6: List of dataclasses - print("\n6. List of dataclasses:") - tasks = agent(list[Task]) @ "Create 3 example programming tasks" + # Example 5: List of dataclasses + print("\n5. List of dataclasses:") + tasks = agent.run("Create 3 example programming tasks", return_type=list[Task]) print("Tasks:") - for i, task in enumerate(tasks, 1): + for t in tasks: print( - f" {i}. {task.title} ({task.priority} priority, {task.estimated_hours}h)" + f" - {t.title} ({t.priority} priority, {t.estimated_hours}h)" ) - # Example 7: Enum result - print("\n7. Enum result:") - priority = ( - agent(Priority) @ "What priority should 'fix critical security bug' have?" + # Example 6: Enum result + print("\n6. Enum result:") + priority = agent.run( + "What priority should 'fix critical security bug' have?", + return_type=Priority ) print(f"Priority: {priority} (type: {type(priority).__name__})") - # Example 8: Access model's explanation - print("\n8. Model explanation:") - result = agent(bool) @ "Is Python a compiled language?" + # Example 7: Access model's explanation + print("\n7. Model explanation:") + result = agent.run("Is Python a compiled language?", return_type=bool) print(f"Answer: {result}") print(f"Explanation: {agent.last_model_answer}") - # Example 9: Session context manager - print("\n9. Session context manager:") + # Example 8: Session context manager + print("\n8. Session context manager:") print(" Default behavior (no memory):") agent.run("Create a function called 'greet_user'") agent.run("Now add error handling to that function") # Won't remember greet_user @@ -105,13 +101,12 @@ def main(): "Add comprehensive docstring and type hints" ) # Remembers the function - # Use @ operator in session - functions = ( - session(list[str]) - @ "List all the functions we've worked on in this session" + # Query the session for functions + functions = session.run( + "List all the functions we've worked on in this session" ) print(f" Functions in session: {functions}") - print(f" Session ID: {session.last_session_id}") + print(f" Session ID: {session.session_id}") print(" Continuing the same session automatically:") with agent.session() as session: # Automatically resumes last session diff --git a/examples/python-sdk/cli_analyzer.py b/examples/python-sdk/cli_analyzer.py index 8cf528f..88ea505 100644 --- a/examples/python-sdk/cli_analyzer.py +++ b/examples/python-sdk/cli_analyzer.py @@ -43,9 +43,10 @@ def main(): # Initialize the agent print("🚀 Initializing Augment Agent...") + import os agent = Auggie( - workspace_root="/home/augment/augment2", - model="claude-3-5-sonnet-latest" + workspace_root=os.getcwd(), # Use current working directory + model="sonnet4.5" ) # ============================================================================ @@ -54,10 +55,10 @@ def main(): print("\n📁 Stage 1: Discovering TypeScript files...") try: - ts_files = agent( + ts_files = agent.run( "List all TypeScript files (.ts) in the clients/beachhead/src/cli directory. " "Return just the file paths as a list of strings, relative to the workspace root.", - list[str] + return_type=list[str] ) print(f" Found {len(ts_files)} TypeScript files to analyze") except Exception as e: @@ -83,14 +84,14 @@ def main(): try: # Analyze this specific file - analysis = session( + analysis = session.run( f"Analyze the file {file_path}. Check for:\n" f"1. Whether it has proper error handling (try-catch blocks, error returns)\n" f"2. Whether exported functions have JSDoc comments\n" f"3. Any potential bugs or code smells\n" f"Also rate the importance of this file from 1-10 based on its role.\n" f"Return a FileAnalysis object.", - FileAnalysis + return_type=FileAnalysis ) all_analyses.append(analysis) @@ -145,7 +146,7 @@ def main(): print(f" - Files missing JSDoc: {files_missing_jsdoc}") try: - agent( + agent.run( f"Create a comprehensive analysis report in cli_analysis_report.md with:\n" f"- Summary statistics: {len(ts_files)} files analyzed, {len(all_issues)} issues found\n" f"- {files_missing_error_handling} files missing error handling\n" @@ -169,7 +170,7 @@ def main(): print(" Creating detailed error handling plan...") try: - agent( + agent.run( f"Create a detailed plan for adding error handling to the {files_missing_error_handling} " f"files that are missing it. Save this plan in error_handling_plan.md. " f"Include:\n" @@ -199,7 +200,7 @@ def main(): try: with agent.session() as session: for file_analysis in top_5_files: - session( + session.run( f"Generate JSDoc comment templates for all exported functions in " f"{file_analysis.file_path}. Add these as comments in the file, " f"following TypeScript JSDoc best practices." @@ -237,9 +238,9 @@ def main(): with agent.session() as session: for idx, issue in enumerate(top_3_issues, 1): print(f" [{idx}/3] Creating fix for: {issue.description} in {issue.file_path}") - + try: - session( + session.run( f"Create a specific fix suggestion for this issue:\n" f"File: {issue.file_path}\n" f"Issue: {issue.description}\n" diff --git a/examples/python-sdk/demo_session_continuity.py b/examples/python-sdk/demo_session_continuity.py index 69c1f64..4a81ea5 100644 --- a/examples/python-sdk/demo_session_continuity.py +++ b/examples/python-sdk/demo_session_continuity.py @@ -13,9 +13,17 @@ class SimpleListener(AgentEventListener): """Simple listener that prints agent responses.""" - def on_agent_message_chunk(self, text: str): + def on_agent_message_chunk(self, text: str) -> None: print(text, end="", flush=True) + def on_tool_call(self, tool_call_id: str, title: str, kind=None, status=None) -> None: + """Called when a tool call starts.""" + pass + + def on_tool_response(self, tool_call_id: str, status=None, content=None) -> None: + """Called when a tool response is received.""" + pass + def demo_session_continuity(): """Demonstrate automatic session continuity.""" @@ -26,7 +34,7 @@ def demo_session_continuity(): # Create client with listener for real-time output client = AuggieACPClient( - model="claude-3-5-sonnet-latest", listener=SimpleListener() + model="sonnet4.5", listener=SimpleListener() ) print("Starting ACP client (creates a persistent session)...") diff --git a/examples/python-sdk/docs/AUTOMATIC_TYPE_INFERENCE.md b/examples/python-sdk/docs/AUTOMATIC_TYPE_INFERENCE.md index dd8bc1a..1ac21af 100644 --- a/examples/python-sdk/docs/AUTOMATIC_TYPE_INFERENCE.md +++ b/examples/python-sdk/docs/AUTOMATIC_TYPE_INFERENCE.md @@ -49,7 +49,7 @@ def run( **Return values:** - If `return_type` is specified: Returns parsed result of that type -- If `return_type` is None: Returns tuple of `(result, inferred_type)` +- If `return_type` is None: Returns result with automatically inferred type (use `type(result)` to inspect) ## Examples @@ -61,16 +61,16 @@ from auggie_sdk import Auggie agent = Auggie() # Agent automatically determines the type -result, inferred_type = agent.run("What is 2 + 2?") -print(f"Result: {result}, Type: {inferred_type.__name__}") +result = agent.run("What is 2 + 2?") +print(f"Result: {result}, Type: {type(result).__name__}") # Output: Result: 4, Type: int -result, inferred_type = agent.run("List the primary colors") -print(f"Result: {result}, Type: {inferred_type.__name__}") +result = agent.run("List the primary colors") +print(f"Result: {result}, Type: {type(result).__name__}") # Output: Result: ['red', 'blue', 'yellow'], Type: list -result, inferred_type = agent.run("Is Python statically typed?") -print(f"Result: {result}, Type: {inferred_type.__name__}") +result = agent.run("Is Python statically typed?") +print(f"Result: {result}, Type: {type(result).__name__}") # Output: Result: False, Type: bool ``` @@ -93,56 +93,6 @@ task = agent.run("Create a task", return_type=Task) print(f"Task: {task.title}") ``` -## Implementation Details - -### Core Changes - -1. **Added `DEFAULT_INFERENCE_TYPES` constant** in `augment/agent.py`: - ```python - DEFAULT_INFERENCE_TYPES = [int, float, bool, str, list, dict] - ``` - -2. **Modified `run()` method** to use automatic type inference when `return_type` is None: - ```python - if return_type is None: - # Type inference mode with default types - return self._run_with_type_inference( - client, - instruction, - DEFAULT_INFERENCE_TYPES, - effective_timeout, - max_retries, - ) - ``` - -3. **Removed `infer_type` parameter** from the `run()` method signature - -4. **Updated return type annotation** to reflect the new behavior: - ```python - -> Union[T, tuple[T, Type[T]]] - ``` - -### Test Updates - -- Updated all tests that call `agent.run()` without `return_type` to expect tuple returns -- Added helper function `make_type_inference_response()` for creating mock responses -- Renamed tests from `test_run_type_inference_*` to `test_run_automatic_type_inference_*` -- All 49 unit tests passing - -### Documentation Updates - -- Updated README.md with automatic type inference examples -- Added dedicated section explaining the feature -- Updated API reference -- Updated examples in `examples/basic_usage.py` - -## Benefits - -1. **Simpler API**: No need to specify `infer_type` parameter -2. **More Intelligent**: Agent automatically chooses the best type -3. **Backward Compatible**: Explicit `return_type` still works as before -4. **Better UX**: Natural behavior - when you don't specify a type, the agent figures it out - ## Migration Guide ### If you were using `infer_type`: @@ -172,19 +122,8 @@ result, inferred_type = agent.run("Hello") # Returns tuple result, _ = agent.run("Hello") ``` -## Files Modified +## See Also -1. `augment/agent.py` - Core implementation -2. `tests/test_agent.py` - Test updates -3. `README.md` - Documentation -4. `examples/basic_usage.py` - Example updates -5. `pyproject.toml` - Fixed duplicate `[project.optional-dependencies]` section - -## Testing - -All tests pass: -```bash -cd experimental/guy/auggie_sdk -python3 -m pytest tests/test_agent.py -v -# 49 passed in 0.16s -``` +- [Typed Returns](./TYPED_RETURNS.md) +- [Function Calling](./FUNCTION_CALLING.md) +- [Agent Event Listener](./AGENT_EVENT_LISTENER.md) diff --git a/examples/python-sdk/docs/CLAUDE_CODE_CLIENT.md b/examples/python-sdk/docs/CLAUDE_CODE_CLIENT.md index 393f084..7200f49 100644 --- a/examples/python-sdk/docs/CLAUDE_CODE_CLIENT.md +++ b/examples/python-sdk/docs/CLAUDE_CODE_CLIENT.md @@ -193,23 +193,6 @@ Common errors: - **`RuntimeError: npx not found`** - Install Node.js - **`TimeoutError: Claude Code agent failed to start`** - Install the adapter with `npm install -g @zed-industries/claude-code-acp` -## Testing - -Run the E2E tests to verify everything works: - -```bash -# Quick tests (~30 seconds) -pytest tests/test_claude_code_client_e2e.py - -# All tests including slow ones (~5-10 minutes) -pytest tests/test_claude_code_client_e2e.py -m "" - -# Specific test -pytest tests/test_claude_code_client_e2e.py::test_simple_math_query -v -``` - -See [Testing Guide](../tests/README_CLAUDE_CODE_TESTS.md) for details. - ## Comparison with AuggieACPClient | Feature | ClaudeCodeACPClient | AuggieACPClient | @@ -220,26 +203,6 @@ See [Testing Guide](../tests/README_CLAUDE_CODE_TESTS.md) for details. | Models | Claude 3.x family | Multiple providers | | Features | Full Claude Code features | Augment-specific features | -## Architecture - -``` -Python Code - ↓ -ClaudeCodeACPClient (Python) - ↓ -agent-client-protocol (Python package) - ↓ -ACP Protocol (stdio) - ↓ -@zed-industries/claude-code-acp (Node.js adapter) - ↓ -Claude Agent SDK (TypeScript) - ↓ -Claude Code - ↓ -Anthropic API -``` - ## Troubleshooting ### Adapter not found diff --git a/examples/python-sdk/docs/FUNCTION_CALLING.md b/examples/python-sdk/docs/FUNCTION_CALLING.md index fc68ae1..3ee350d 100644 --- a/examples/python-sdk/docs/FUNCTION_CALLING.md +++ b/examples/python-sdk/docs/FUNCTION_CALLING.md @@ -4,34 +4,6 @@ The Augment Python SDK now supports **function calling**, allowing you to provide Python functions that the agent can invoke during execution. This enables the agent to interact with external systems, perform calculations, fetch data, and more. -## Implementation Summary - -### What Was Added - -1. **Function Schema Generation** (`augment/function_tools.py`) - - Converts Python functions with type hints to JSON schemas - - Extracts parameter descriptions from docstrings - - Supports Google-style and NumPy-style docstrings - - Handles optional parameters, default values, and various Python types - -2. **Agent Integration** (`augment/agent.py`) - - Added `functions` parameter to `Agent.run()` method - - Function schemas are added to the instruction prompt - - Integrated into normal code path (no separate function calling flow) - - Added `_handle_function_calls()` helper to process function calls in responses - - Added `_parse_function_calls()` to extract function calls from agent responses - - Automatic function execution and result passing back to agent - -3. **Documentation** - - Updated README.md with function calling examples - - Updated examples/README.md with proper usage - - Updated .augment/commands/prompt-to-sdk.md - -4. **Examples and Tests** - - Created `examples/function_calling_example.py` with comprehensive examples - - Created `examples/simple_function_test.py` for quick testing - - Created `test_function_calling.py` with unit tests - ## How It Works ### 1. Function Definition @@ -203,15 +175,12 @@ result = agent.run( Run the tests: ```bash -# Unit tests -cd experimental/guy/auggie_sdk -python3 -m pytest test_function_calling.py - -# Simple integration test -python3 examples/simple_function_test.py +# Install the SDK +pip install auggie-sdk -# Full examples -python3 examples/function_calling_example.py +# Run the function calling example +cd examples/python-sdk +python3 function_calling_example.py ``` ## Implementation Details @@ -261,3 +230,9 @@ Potential improvements: - Better error recovery - Function call validation before execution - Support for more complex type hints (TypedDict, Literal, etc.) + +## See Also + +- [Automatic Type Inference](./AUTOMATIC_TYPE_INFERENCE.md) +- [Agent Event Listener](./AGENT_EVENT_LISTENER.md) +- [Prompt to Code](./PROMPT_TO_CODE.md) diff --git a/examples/python-sdk/docs/PROMPT_TO_CODE.md b/examples/python-sdk/docs/PROMPT_TO_CODE.md index cb18947..95be15b 100644 --- a/examples/python-sdk/docs/PROMPT_TO_CODE.md +++ b/examples/python-sdk/docs/PROMPT_TO_CODE.md @@ -24,8 +24,7 @@ Converting these to SDK programs gives you: Make sure the Augment SDK is installed: ```bash -cd experimental/guy/auggie_sdk -pip install -e . +pip install auggie-sdk ``` ## Usage @@ -255,6 +254,26 @@ This clearly shows: - Conditional logic (if any fail) - Data dependencies (which files need tests) +### More Example Prompts + +**Documentation Generator:** +``` +Analyze all public functions and classes in the src/ directory. For each +one that's missing a docstring or has an incomplete docstring, generate +comprehensive documentation including description, parameters, return +values, and usage examples. Then validate that all docstrings follow +Google style guide format. +``` + +**Code Refactoring Pipeline:** +``` +Find all functions in the codebase that are longer than 50 lines. For +each one, analyze if it can be broken down into smaller functions. If +yes, refactor it into multiple well-named functions with clear +responsibilities. After each refactoring, run the existing tests to +ensure nothing broke. Keep a log of all refactorings performed. +``` + ### Less Ideal Prompt Example ``` @@ -263,6 +282,22 @@ Make the codebase better with tests and stuff. This is too vague and doesn't provide enough structure for conversion. +## What Gets Generated + +The tool generates a complete Python program with: + +1. **Proper shebang and docstring** +2. **All necessary imports** (Agent, dataclasses, typing, etc.) +3. **Dataclass definitions** for structured data +4. **Agent initialization** with appropriate settings +5. **Workflow implementation** using SDK patterns: + - Sessions for context continuity + - Typed results for decision-making + - Loops for iteration + - Error handling +6. **Main function** that can be run directly +7. **Helpful comments** explaining each stage + ## After Generation Once you have your generated SDK program: @@ -273,6 +308,47 @@ Once you have your generated SDK program: 4. **Iterate** - Modify the program based on results 5. **Reuse it** - Run the same workflow whenever needed +### Modifying Generated Programs + +After generation, you can enhance the code: + +**Add error handling:** +```python +try: + result = agent("Some operation", int) +except AugmentCLIError as e: + print(f"Error: {e}") +``` + +**Add logging:** +```python +import logging +logging.basicConfig(level=logging.INFO) +logging.info(f"Processing {len(files)} files") +``` + +**Add function calling:** +```python +def run_tests(file: str) -> dict: + """Run tests for a file.""" + # Your implementation + return {"passed": 10, "failed": 0} + +result = agent.run( + "Run tests and analyze results", + return_type=dict, + functions=[run_tests] +) +``` + +**Add event listeners:** +```python +from auggie_sdk import LoggingAgentListener + +listener = LoggingAgentListener(verbose=True) +agent = Auggie(listener=listener) +``` + ## See Also - [Agent Documentation](./AGENT_EVENT_LISTENER.md) diff --git a/examples/python-sdk/docs/SESSION_CONTINUITY.md b/examples/python-sdk/docs/SESSION_CONTINUITY.md index 095913f..cab8ddc 100644 --- a/examples/python-sdk/docs/SESSION_CONTINUITY.md +++ b/examples/python-sdk/docs/SESSION_CONTINUITY.md @@ -266,3 +266,9 @@ with AuggieACPClient(workspace_root="/path/to/workspace") as client: | **Best For** | One-off requests | Multiple related requests | **Key Takeaway:** The ACP client's long-running architecture makes session resume unnecessary - it's a feature, not a bug! 🎉 + +## See Also + +- [Agent Event Listener](./AGENT_EVENT_LISTENER.md) +- [Claude Code Client](./CLAUDE_CODE_CLIENT.md) +- [Prompt to Code](./PROMPT_TO_CODE.md) diff --git a/examples/python-sdk/list_prs.py b/examples/python-sdk/list_prs.py index 137f79e..221d97b 100644 --- a/examples/python-sdk/list_prs.py +++ b/examples/python-sdk/list_prs.py @@ -1,6 +1,5 @@ import sys from dataclasses import dataclass -from enum import Enum from pathlib import Path # Add the parent directory to the path so we can import auggie_sdk @@ -19,14 +18,15 @@ class PR: def main(): - a = Agent(workspace_root=Path.cwd()) + agent = Auggie(workspace_root=Path.cwd()) - if not a(bool) @ "are you connected to github?": + is_connected = agent.run("are you connected to github?", return_type=bool) + if not is_connected: raise Exception("Not connected to github") - prs = a(list[PR]) @ "List the last 3 PRs in the current repo" + prs = agent.run("List the last 3 PRs in the current repo", return_type=list[PR]) - summaries = [a(str) @ f"summarize PR {pr}" for pr in prs] + summaries = [agent.run(f"summarize PR {pr}", return_type=str) for pr in prs] for pr, summary in zip(prs, summaries): print("Title:", pr.title) diff --git a/examples/python-sdk/prompt_to_code.py b/examples/python-sdk/prompt_to_code.py new file mode 100644 index 0000000..6cc4194 --- /dev/null +++ b/examples/python-sdk/prompt_to_code.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python3 +""" +Convert a complex prompt into an Augment SDK program. + +This tool analyzes a complex, multi-stage prompt and converts it into a +well-structured Python program using the Augment SDK. This allows complex +workflows with conditions, loops, and multiple stages to be expressed as +code rather than a single monolithic prompt. + +Usage: + python prompt_to_code.py [--output OUTPUT_FILE] [--model MODEL] + +Examples: + python prompt_to_code.py my_prompt.txt + python prompt_to_code.py my_prompt.txt --output generated_script.py + python prompt_to_code.py my_prompt.txt --model claude-3-5-sonnet-latest +""" + +import argparse +import sys +from pathlib import Path +from dataclasses import dataclass +from typing import Optional + +# Add the parent directory to the path so we can import auggie_sdk +sys.path.insert(0, str(Path(__file__).parent)) + +from auggie_sdk import Auggie +from auggie_sdk.exceptions import AugmentCLIError, AugmentParseError + + +CONVERSION_PROMPT = """ +Analyze this complex prompt and convert it into a well-structured Python program using the Augment SDK. + +## The Prompt to Convert: + +{prompt_content} + +## Your Task: + +1. **Analyze the prompt structure** and identify: + - Sequential stages that must happen in order + - Conditional logic based on previous results + - Loops/iterations over collections + - Data dependencies between steps + - Validation/error handling needs + - Context requirements (when steps need to remember previous work) + +2. **Design the SDK program** using these patterns: + + **IMPORTANT: Agent API** + - Use `agent.run(prompt)` or `agent.run(prompt, return_type=Type)` to run tasks + - DO NOT call `agent()` directly - Agent is not callable + - Use `agent.session()` for multi-step workflows with shared context + + **For Sequential Stages with Context:** + ```python + with agent.session() as session: + session.run("Step 1: Create the base structure") + session.run("Step 2: Add features to what we just created") + session.run("Step 3: Test everything we built") + ``` + + **For Conditional Logic:** + ```python + result = agent.run("Check if the file exists", return_type=bool) + if result: + agent.run("Process the existing file") + else: + agent.run("Create a new file first") + ``` + + **For Loops/Iterations:** + ```python + files = agent.run("List all Python files in src/", return_type=list[str]) + for file in files: + agent.run(f"Review {{file}} for security issues") + ``` + + **For Data Dependencies:** + ```python + @dataclass + class FileInfo: + path: str + size: int + type: str + + files = agent.run("Analyze all config files", return_type=list[FileInfo]) + for file in files: + if file.size > 1000: + agent.run(f"Optimize {{file.path}} - it's {{file.size}} bytes") + ``` + + **For Function Calling:** + ```python + def run_tests(test_file: str) -> dict: + \"\"\"Run tests and return results.\"\"\" + # Your test execution logic + return {{"passed": 10, "failed": 2, "file": test_file}} + + result = agent.run( + "Run all tests and analyze failures", + return_type=dict, + functions=[run_tests] + ) + ``` + +3. **Generate a complete, runnable Python program** that: + - Imports necessary modules (from auggie_sdk import Agent, dataclasses, typing, etc.) + - Defines any dataclasses for structured data + - Creates the agent with: `agent = Auggie(workspace_root=".")` or `agent = Auggie()` + - IMPORTANT: Agent() takes optional keyword arguments only: workspace_root, model, listener + - DO NOT pass positional arguments to Agent() + - Implements the workflow using the patterns above + - Includes error handling and logging + - Adds comments explaining each stage + - Provides a main() function that can be run directly + - Includes a proper shebang and docstring + +4. **Guidelines:** + - Use sessions when multiple related steps build on each other + - Use typed results when you need to make decisions or iterate + - Use function calling when the agent needs to interact with external systems + - Break into multiple calls for each logical stage + - Keep as single call only for simple, atomic operations + +5. **Output Format:** + Return ONLY the complete Python program code, with no additional explanation. + The code should be ready to save to a file and run immediately. + Start with #!/usr/bin/env python3 and include a proper docstring. +""" + + +@dataclass +class ConversionResult: + """Result of converting a prompt to SDK code.""" + + code: str + success: bool + error: Optional[str] = None + + +def read_prompt_file(file_path: str) -> str: + """Read the prompt from a file.""" + try: + path = Path(file_path) + if not path.exists(): + raise FileNotFoundError(f"Prompt file not found: {file_path}") + + return path.read_text(encoding="utf-8") + except Exception as e: + raise RuntimeError(f"Failed to read prompt file: {e}") + + +def convert_prompt_to_code( + prompt_content: str, + model: str = "claude-3-5-sonnet-latest", + workspace_root: Optional[str] = None, +) -> ConversionResult: + """ + Convert a prompt to an Augment SDK program. + + Args: + prompt_content: The prompt text to convert + model: The model to use for conversion + workspace_root: Optional workspace root directory + + Returns: + ConversionResult with the generated code or error + """ + try: + # Create agent for conversion + agent = Auggie(model=model, workspace_root=workspace_root or str(Path.cwd())) + + # Format the conversion prompt + full_prompt = CONVERSION_PROMPT.format(prompt_content=prompt_content) + + # Get the generated code + print("🤖 Analyzing prompt and generating SDK program...") + code = agent.run(full_prompt, return_type=str, timeout=120) + + return ConversionResult(code=code, success=True) + + except AugmentParseError as e: + return ConversionResult( + code="", success=False, error=f"Failed to parse agent response: {e}" + ) + except AugmentCLIError as e: + return ConversionResult( + code="", success=False, error=f"Agent execution failed: {e}" + ) + except Exception as e: + return ConversionResult(code="", success=False, error=f"Unexpected error: {e}") + + +def save_generated_code(code: str, output_file: Optional[str] = None) -> str: + """ + Save the generated code to a file. + + Args: + code: The generated Python code + output_file: Optional output file path. If None, generates a name. + + Returns: + The path where the code was saved + """ + if output_file: + output_path = Path(output_file) + else: + # Generate a default output filename + output_path = Path("generated_sdk_program.py") + counter = 1 + while output_path.exists(): + output_path = Path(f"generated_sdk_program_{counter}.py") + counter += 1 + + output_path.write_text(code, encoding="utf-8") + + # Make the file executable + output_path.chmod(0o755) + + return str(output_path) + + +def main(): + """Main entry point for the prompt-to-code converter.""" + parser = argparse.ArgumentParser( + description="Convert a complex prompt into an Augment SDK program", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s my_prompt.txt + %(prog)s my_prompt.txt --output generated_script.py + %(prog)s my_prompt.txt --model claude-3-5-sonnet-latest + """, + ) + + parser.add_argument( + "prompt_file", help="Path to the file containing the prompt to convert" + ) + + parser.add_argument( + "-o", + "--output", + help="Output file path for the generated program (default: auto-generated)", + default=None, + ) + + parser.add_argument( + "-m", + "--model", + help="Model to use for conversion (default: claude-3-5-sonnet-latest)", + default="claude-3-5-sonnet-latest", + ) + + parser.add_argument( + "-w", + "--workspace", + help="Workspace root directory (default: current directory)", + default=None, + ) + + args = parser.parse_args() + + try: + # Read the prompt file + print(f"📖 Reading prompt from: {args.prompt_file}") + prompt_content = read_prompt_file(args.prompt_file) + print(f"✅ Read {len(prompt_content)} characters") + + # Convert the prompt + result = convert_prompt_to_code( + prompt_content, model=args.model, workspace_root=args.workspace + ) + + if not result.success: + print(f"❌ Conversion failed: {result.error}", file=sys.stderr) + return 1 + + # Save the generated code + output_path = save_generated_code(result.code, args.output) + print(f"✅ Generated SDK program saved to: {output_path}") + + # Print usage instructions + print("\n" + "=" * 60) + print("📝 Usage Instructions:") + print("=" * 60) + print(f"1. Review the generated code: {output_path}") + print(f"2. Run the program: python {output_path}") + print("3. Modify as needed for your specific use case") + print("\n💡 The generated program uses the Augment SDK.") + print(" Make sure it's installed: pip install auggie-sdk") + + return 0 + + except FileNotFoundError as e: + print(f"❌ {e}", file=sys.stderr) + return 1 + except RuntimeError as e: + print(f"❌ {e}", file=sys.stderr) + return 1 + except KeyboardInterrupt: + print("\n⚠️ Conversion interrupted by user", file=sys.stderr) + return 130 + except Exception as e: + print(f"❌ Unexpected error: {e}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/examples/python-sdk/prompt_to_code_v2.py b/examples/python-sdk/prompt_to_code_v2.py new file mode 100644 index 0000000..032fa7c --- /dev/null +++ b/examples/python-sdk/prompt_to_code_v2.py @@ -0,0 +1,578 @@ +#!/usr/bin/env python3 +""" +Convert a complex prompt into an Augment SDK program using a multi-stage workflow. + +This version uses the SDK itself to orchestrate the conversion process: +1. Spec: Create a detailed specification for the program +2. Implement: Generate the SDK code +3. Test: Validate the code (syntax, types, dry-run) +4. Iterate: Fix issues until tests pass +5. Cleanup: Final polish and documentation + +Usage: + python prompt_to_code_v2.py [--output OUTPUT_FILE] [--model MODEL] + +Examples: + python prompt_to_code_v2.py my_prompt.txt + python prompt_to_code_v2.py my_prompt.txt --output generated_script.py + python prompt_to_code_v2.py my_prompt.txt --model claude-3-5-sonnet-latest +""" + +import argparse +import ast +import json +import subprocess +import sys +import tempfile +from dataclasses import dataclass +from pathlib import Path +from typing import Optional, List + +# Add the parent directory to the path so we can import auggie_sdk +sys.path.insert(0, str(Path(__file__).parent)) + +from auggie_sdk import Auggie + + +@dataclass +class ProgramSpec: + """Specification for the SDK program to be generated.""" + + purpose: str + input_requirements: List[str] + output_requirements: List[str] + stages: List[str] + data_structures: List[str] + test_strategy: str + safety_considerations: str + + +@dataclass +class ValidationResult: + """Result of validating generated code.""" + + success: bool + syntax_valid: bool + type_check_passed: bool + imports_valid: bool + dry_run_passed: bool + errors: List[str] + warnings: List[str] + + +@dataclass +class ConversionResult: + """Result of the prompt-to-code conversion.""" + + success: bool + code: Optional[str] + spec: Optional[ProgramSpec] + validation: Optional[ValidationResult] + iterations: int + error: Optional[str] + + +def validate_python_syntax(code: str) -> tuple[bool, List[str]]: + """ + Validate Python syntax by parsing the code. + + Returns: + (is_valid, errors) + """ + errors = [] + try: + ast.parse(code) + return True, [] + except SyntaxError as e: + errors.append(f"Syntax error at line {e.lineno}: {e.msg}") + return False, errors + + +def check_imports(code: str) -> tuple[bool, List[str]]: + """ + Check if all imports in the code are valid. + + Returns: + (all_valid, warnings) + """ + warnings = [] + + # List of allowed imports (common Python stdlib + augment) + allowed_imports = { + "augment", + "dataclasses", + "typing", + "json", + "pathlib", + "datetime", + "sys", + "os", + "argparse", + "logging", + "tempfile", + "subprocess", + "collections", + "re", + "time", + "shutil", + "glob", + "itertools", + "functools", + "enum", + "abc", + "contextlib", + "io", + "traceback", + } + + try: + tree = ast.parse(code) + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + # Check if it's a known safe import + if alias.name not in allowed_imports: + warnings.append(f"Warning: Unexpected import '{alias.name}'") + elif isinstance(node, ast.ImportFrom): + if node.module and node.module not in allowed_imports: + warnings.append(f"Warning: Unexpected import from '{node.module}'") + + return True, warnings + except Exception as e: + warnings.append(f"Warning: Error checking imports: {e}") + return True, warnings + + +def run_type_check(code: str, workspace_root: str) -> tuple[bool, List[str]]: + """ + Run mypy type checking on the code. + + Returns: + (passed, warnings) + """ + warnings = [] + + # Save code to a temporary file + with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: + f.write(code) + temp_file = f.name + + try: + # Run mypy + result = subprocess.run( + ["mypy", "--ignore-missing-imports", "--no-error-summary", temp_file], + capture_output=True, + text=True, + timeout=30, + ) + + if result.returncode != 0: + warnings.extend(result.stdout.strip().split("\n")) + return False, warnings + + return True, [] + except FileNotFoundError: + # mypy not installed, skip type checking (this is OK) + return True, [] + except Exception as e: + warnings.append(f"Warning: Error running type check: {e}") + return True, warnings + finally: + Path(temp_file).unlink(missing_ok=True) + + +def dry_run_code(code: str, workspace_root: str) -> tuple[bool, List[str]]: + """ + Perform a dry-run of the code to check for runtime issues. + This creates a safe sandbox environment. + + Returns: + (passed, warnings) + """ + warnings = [] + + # For now, we'll just check that the code can be imported + # A full dry-run would require mocking the Agent and all tools + try: + # Check for potentially dangerous operations + tree = ast.parse(code) + for node in ast.walk(tree): + # Check for file operations + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Attribute): + if node.func.attr in ["unlink", "rmdir", "remove", "rmtree"]: + warnings.append( + "Warning: Code contains file deletion operations - ensure they're safe" + ) + + return True, warnings + except Exception as e: + warnings.append(f"Warning: Error during dry-run: {e}") + return True, warnings + + +def validate_code(code: str, workspace_root: str) -> ValidationResult: + """ + Validate the generated code through multiple checks. + + Args: + code: The Python code to validate + workspace_root: Workspace directory for context + + Returns: + ValidationResult with detailed validation info + """ + all_errors = [] + all_warnings = [] + + # 1. Syntax check + syntax_valid, syntax_errors = validate_python_syntax(code) + all_errors.extend(syntax_errors) + + # 2. Import check + imports_valid, import_warnings = check_imports(code) + all_warnings.extend(import_warnings) + + # 3. Type check + type_check_passed, type_warnings = run_type_check(code, workspace_root) + all_warnings.extend(type_warnings) + + # 4. Dry run + dry_run_passed, dry_run_warnings = dry_run_code(code, workspace_root) + all_warnings.extend(dry_run_warnings) + + success = syntax_valid and len(all_errors) == 0 + + return ValidationResult( + success=success, + syntax_valid=syntax_valid, + type_check_passed=type_check_passed, + imports_valid=imports_valid, + dry_run_passed=dry_run_passed, + errors=all_errors, + warnings=all_warnings, + ) + + +def convert_prompt_to_code_v2( + prompt_content: str, + model: str = "claude-3-5-sonnet-latest", + workspace_root: str = ".", + max_iterations: int = 3, + timeout: int = 300, +) -> ConversionResult: + """ + Convert a prompt to SDK code using a multi-stage workflow. + + Args: + prompt_content: The prompt to convert + model: AI model to use + workspace_root: Workspace directory + max_iterations: Maximum number of fix iterations + timeout: Timeout for each agent call in seconds + + Returns: + ConversionResult with the generated code and metadata + """ + print("🤖 Starting multi-stage prompt-to-code conversion...") + + agent = Auggie(model=model, workspace_root=workspace_root) + + try: + with agent.session() as session: + # Stage 1: Create specification + print("\n📋 Stage 1: Creating program specification...") + spec = session.run( + f"""Analyze this prompt and create a detailed specification for an SDK program: + +{prompt_content} + +Create a specification that includes: +1. Purpose: What the program should accomplish +2. Input requirements: What data/files it needs +3. Output requirements: What it should produce +4. Stages: Sequential steps in the workflow +5. Data structures: Any dataclasses or types needed +6. Test strategy: How to validate it works (WITHOUT modifying real user data) +7. Safety considerations: What could go wrong and how to prevent it + +Return the specification as a JSON object.""", + return_type=dict, + timeout=timeout, + ) + + print("✅ Specification created:") + print(f" Purpose: {spec.get('purpose', 'N/A')}") + print(f" Stages: {len(spec.get('stages', []))}") + test_strategy = spec.get("test_strategy", "N/A") + if isinstance(test_strategy, str): + print(f" Test strategy: {test_strategy[:80]}...") + else: + print(f" Test strategy: {test_strategy}") + + spec_obj = ProgramSpec( + purpose=spec.get("purpose", ""), + input_requirements=spec.get("input_requirements", []), + output_requirements=spec.get("output_requirements", []), + stages=spec.get("stages", []), + data_structures=spec.get("data_structures", []), + test_strategy=spec.get("test_strategy", ""), + safety_considerations=spec.get("safety_considerations", ""), + ) + + # Stage 2: Implement + print("\n💻 Stage 2: Implementing SDK program...") + + implementation_prompt = f"""Based on this specification, implement a complete Python program using the Augment SDK: + +SPECIFICATION: +{json.dumps(spec, indent=2)} + +ORIGINAL PROMPT: +{prompt_content} + +REQUIREMENTS: +1. Use `from auggie_sdk import Agent` for imports +2. Initialize agent with: `agent = Auggie()` or `agent = Auggie(workspace_root=".")` +3. Use `agent.run(prompt)` or `agent.run(prompt, return_type=Type)` - DO NOT call agent() directly +4. Use `agent.session()` for multi-step workflows with shared context +5. Include proper error handling +6. Add a main() function that can be run directly +7. Include docstrings and comments +8. Follow the test strategy from the spec to ensure safety +9. Start with #!/usr/bin/env python3 and a module docstring + +Generate ONLY the complete Python code, no explanations.""" + + code = session.run(implementation_prompt, return_type=str, timeout=timeout) + + print(f"✅ Initial implementation generated ({len(code)} chars)") + + # Stage 3: Test and iterate + print("\n🧪 Stage 3: Testing and iteration...") + + iteration = 0 + validation = None + + while iteration < max_iterations: + iteration += 1 + print(f"\n Iteration {iteration}/{max_iterations}:") + + # Validate the code + validation = validate_code(code, workspace_root) + + print(f" - Syntax valid: {'✅' if validation.syntax_valid else '❌'}") + print( + f" - Type check: {'✅' if validation.type_check_passed else '⚠️'}" + ) + print( + f" - Imports valid: {'✅' if validation.imports_valid else '⚠️'}" + ) + print(f" - Dry run: {'✅' if validation.dry_run_passed else '❌'}") + + if validation.errors: + print(f" - Errors: {len(validation.errors)}") + for error in validation.errors[:3]: + print(f" • {error}") + + if validation.warnings: + print(f" - Warnings: {len(validation.warnings)}") + + if validation.success: + print(" ✅ All validations passed!") + break + + # Fix issues + if iteration < max_iterations: + print(" 🔧 Fixing issues...") + + fix_prompt = f"""The generated code has validation errors. Please fix them: + +ERRORS: +{chr(10).join(validation.errors)} + +WARNINGS: +{chr(10).join(validation.warnings)} + +CURRENT CODE: +```python +{code} +``` + +Fix all errors and return the corrected code. Return ONLY the complete Python code, no explanations.""" + + code = session.run(fix_prompt, return_type=str, timeout=timeout) + print(f" ✅ Code updated ({len(code)} chars)") + + # Stage 4: Cleanup and polish + if validation and validation.success: + print("\n✨ Stage 4: Final cleanup and polish...") + + cleanup_prompt = f"""Polish this working SDK program for production use: + +{code} + +Improvements to make: +1. Ensure all docstrings are clear and complete +2. Add helpful comments for complex logic +3. Ensure error messages are user-friendly +4. Verify the code follows Python best practices +5. Make sure the test strategy from the spec is implemented + +Return ONLY the polished Python code, no explanations.""" + + code = session.run(cleanup_prompt, return_type=str, timeout=timeout) + print(" ✅ Code polished and ready!") + + # Final validation + final_validation = validate_code(code, workspace_root) + + print("\n" + "=" * 80) + print("CONVERSION COMPLETE") + print("=" * 80) + print(f"Success: {'✅' if final_validation.success else '❌'}") + print(f"Iterations: {iteration}") + print(f"Code size: {len(code)} characters") + + if final_validation.errors: + print(f"\n⚠️ Remaining errors: {len(final_validation.errors)}") + for error in final_validation.errors: + print(f" • {error}") + + if final_validation.warnings: + print(f"\n⚠️ Warnings: {len(final_validation.warnings)}") + for warning in final_validation.warnings[:5]: + print(f" • {warning}") + + return ConversionResult( + success=final_validation.success, + code=code, + spec=spec_obj, + validation=final_validation, + iterations=iteration, + error=None + if final_validation.success + else "; ".join(final_validation.errors), + ) + + except Exception as e: + print(f"\n❌ Conversion failed: {e}") + import traceback + + traceback.print_exc() + + return ConversionResult( + success=False, + code=None, + spec=None, + validation=None, + iterations=0, + error=str(e), + ) + + +def main(): + """Main entry point for the CLI.""" + parser = argparse.ArgumentParser( + description="Convert a complex prompt into an Augment SDK program (v2 with multi-stage workflow)" + ) + parser.add_argument( + "prompt_file", + type=str, + help="Path to the file containing the prompt to convert", + ) + parser.add_argument( + "--output", + "-o", + type=str, + help="Output file for the generated SDK program (default: stdout)", + ) + parser.add_argument( + "--model", + "-m", + type=str, + default="claude-3-5-sonnet-latest", + help="AI model to use (default: claude-3-5-sonnet-latest)", + ) + parser.add_argument( + "--workspace-root", + "-w", + type=str, + default=".", + help="Workspace root directory (default: current directory)", + ) + parser.add_argument( + "--max-iterations", + type=int, + default=3, + help="Maximum number of fix iterations (default: 3)", + ) + parser.add_argument( + "--timeout", + type=int, + default=300, + help="Timeout for each agent call in seconds (default: 300)", + ) + + args = parser.parse_args() + + # Read the prompt + prompt_file = Path(args.prompt_file) + if not prompt_file.exists(): + print(f"❌ Error: Prompt file not found: {prompt_file}", file=sys.stderr) + sys.exit(1) + + prompt_content = prompt_file.read_text() + + # Convert + result = convert_prompt_to_code_v2( + prompt_content=prompt_content, + model=args.model, + workspace_root=args.workspace_root, + max_iterations=args.max_iterations, + timeout=args.timeout, + ) + + if not result.success: + print(f"\n❌ Conversion failed: {result.error}", file=sys.stderr) + sys.exit(1) + + # Output the code + if args.output: + output_file = Path(args.output) + output_file.write_text(result.code) + print(f"\n✅ Generated SDK program saved to: {output_file}") + else: + print("\n" + "=" * 80) + print("GENERATED SDK PROGRAM") + print("=" * 80) + print(result.code) + + # Save metadata + if args.output: + metadata_file = Path(args.output).with_suffix(".json") + metadata = { + "success": result.success, + "iterations": result.iterations, + "spec": { + "purpose": result.spec.purpose, + "stages": result.spec.stages, + "test_strategy": result.spec.test_strategy, + } + if result.spec + else None, + "validation": { + "syntax_valid": result.validation.syntax_valid, + "type_check_passed": result.validation.type_check_passed, + "errors": result.validation.errors, + "warnings": result.validation.warnings, + } + if result.validation + else None, + } + metadata_file.write_text(json.dumps(metadata, indent=2)) + print(f"📊 Metadata saved to: {metadata_file}") + + +if __name__ == "__main__": + main() diff --git a/examples/python-sdk/session_usage.py b/examples/python-sdk/session_usage.py index 7a9a76b..45d1f94 100644 --- a/examples/python-sdk/session_usage.py +++ b/examples/python-sdk/session_usage.py @@ -51,14 +51,17 @@ def main(): print("✅ Created tests (agent remembers the volume function)") # Third call with typed result - also remembers previous context - functions = session(list[Function]) @ "List all the functions we've created in this conversation" + functions = session.run( + "List all the functions we've created in this conversation", + return_type=list[Function] + ) print(f"✅ Retrieved {len(functions)} functions from session memory:") for func in functions: print(f" - {func.name}: {func.description}") - print(f" Session ID from CLI: {session.last_session_id}") + print(f" Session ID from CLI: {session.session_id}") - print(f" Agent now remembers last session: {agent.last_session_id}") + print(f" Agent now remembers last session: {agent.session_id}") print("\n🎯 MIXED USAGE - Sessions for related work, independent calls for unrelated:") print("=" * 50) @@ -79,18 +82,21 @@ def main(): session.run("Create a function to check if a string is a palindrome") # This will only know about string functions, not the math functions from before - string_functions = session(list[Function]) @ "List the string utility functions we created" + string_functions = session.run( + "List the string utility functions we created", + return_type=list[Function] + ) print(f"✅ String session has {len(string_functions)} functions:") for func in string_functions: print(f" - {func.name}: {func.description}") - print(f" New session ID: {session.last_session_id}") + print(f" New session ID: {session.session_id}") print("\n✨ Session context manager provides clean separation of concerns!") print(" - CLI automatically creates session IDs") print(" - agent.session() automatically resumes last session if available") print(" - agent.session(id) explicitly specifies a session ID") - print(" - Agent remembers last_session_id for automatic continuation") + print(" - Agent remembers session_id for automatic continuation") if __name__ == "__main__": diff --git a/examples/python-sdk/success_criteria_example.py b/examples/python-sdk/success_criteria_example.py index 259e845..30c89aa 100644 --- a/examples/python-sdk/success_criteria_example.py +++ b/examples/python-sdk/success_criteria_example.py @@ -13,7 +13,7 @@ 5. If criteria not met after max rounds, raises AugmentVerificationError """ -from auggie_sdk import Agent, AugmentVerificationError +from auggie_sdk import Auggie, AugmentVerificationError def main(): diff --git a/examples/python-sdk/user_examples/02_event_listener_builtin.py b/examples/python-sdk/user_examples/02_event_listener_builtin.py index 4c2fcaa..ac6eb57 100644 --- a/examples/python-sdk/user_examples/02_event_listener_builtin.py +++ b/examples/python-sdk/user_examples/02_event_listener_builtin.py @@ -1,6 +1,6 @@ """Built-in Logger Example from user_guide.md""" -from auggie_sdk import Agent, LoggingAgentListener +from auggie_sdk import Auggie, LoggingAgentListener # Use the built-in logger for easy debugging agent = Auggie(listener=LoggingAgentListener(verbose=True)) From 9d3ad5740912bb9e8667b2d5aad3666af482e26d Mon Sep 17 00:00:00 2001 From: Rich Hankins Date: Thu, 22 Jan 2026 00:36:59 +0000 Subject: [PATCH 2/3] Updated examples based on feedback --- examples/python-sdk/prompt_to_code.py | 12 ++++----- examples/python-sdk/prompt_to_code_v2.py | 33 +++++++++++++++--------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/examples/python-sdk/prompt_to_code.py b/examples/python-sdk/prompt_to_code.py index 6cc4194..5ba88e6 100644 --- a/examples/python-sdk/prompt_to_code.py +++ b/examples/python-sdk/prompt_to_code.py @@ -13,7 +13,7 @@ Examples: python prompt_to_code.py my_prompt.txt python prompt_to_code.py my_prompt.txt --output generated_script.py - python prompt_to_code.py my_prompt.txt --model claude-3-5-sonnet-latest + python prompt_to_code.py my_prompt.txt --model sonnet4.5 """ import argparse @@ -106,7 +106,7 @@ def run_tests(test_file: str) -> dict: ``` 3. **Generate a complete, runnable Python program** that: - - Imports necessary modules (from auggie_sdk import Agent, dataclasses, typing, etc.) + - Imports necessary modules (from auggie_sdk import Auggie, dataclasses, typing, etc.) - Defines any dataclasses for structured data - Creates the agent with: `agent = Auggie(workspace_root=".")` or `agent = Auggie()` - IMPORTANT: Agent() takes optional keyword arguments only: workspace_root, model, listener @@ -154,7 +154,7 @@ def read_prompt_file(file_path: str) -> str: def convert_prompt_to_code( prompt_content: str, - model: str = "claude-3-5-sonnet-latest", + model: str = "sonnet4.5", workspace_root: Optional[str] = None, ) -> ConversionResult: """ @@ -231,7 +231,7 @@ def main(): Examples: %(prog)s my_prompt.txt %(prog)s my_prompt.txt --output generated_script.py - %(prog)s my_prompt.txt --model claude-3-5-sonnet-latest + %(prog)s my_prompt.txt --model sonnet4.5 """, ) @@ -249,8 +249,8 @@ def main(): parser.add_argument( "-m", "--model", - help="Model to use for conversion (default: claude-3-5-sonnet-latest)", - default="claude-3-5-sonnet-latest", + help="Model to use for conversion (default: sonnet4.5)", + default="sonnet4.5", ) parser.add_argument( diff --git a/examples/python-sdk/prompt_to_code_v2.py b/examples/python-sdk/prompt_to_code_v2.py index 032fa7c..39c92c0 100644 --- a/examples/python-sdk/prompt_to_code_v2.py +++ b/examples/python-sdk/prompt_to_code_v2.py @@ -15,7 +15,7 @@ Examples: python prompt_to_code_v2.py my_prompt.txt python prompt_to_code_v2.py my_prompt.txt --output generated_script.py - python prompt_to_code_v2.py my_prompt.txt --model claude-3-5-sonnet-latest + python prompt_to_code_v2.py my_prompt.txt --model sonnet4.5 """ import argparse @@ -97,8 +97,9 @@ def check_imports(code: str) -> tuple[bool, List[str]]: """ warnings = [] - # List of allowed imports (common Python stdlib + augment) + # List of allowed imports (common Python stdlib + augment SDK) allowed_imports = { + "auggie_sdk", "augment", "dataclasses", "typing", @@ -123,6 +124,7 @@ def check_imports(code: str) -> tuple[bool, List[str]]: "contextlib", "io", "traceback", + "importlib", } try: @@ -130,12 +132,16 @@ def check_imports(code: str) -> tuple[bool, List[str]]: for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: - # Check if it's a known safe import - if alias.name not in allowed_imports: + # Check root module name for imports like "auggie_sdk.acp" + root_module = alias.name.split('.')[0] + if root_module not in allowed_imports: warnings.append(f"Warning: Unexpected import '{alias.name}'") elif isinstance(node, ast.ImportFrom): - if node.module and node.module not in allowed_imports: - warnings.append(f"Warning: Unexpected import from '{node.module}'") + if node.module: + # Check root module name for submodule imports + root_module = node.module.split('.')[0] + if root_module not in allowed_imports: + warnings.append(f"Warning: Unexpected import from '{node.module}'") return True, warnings except Exception as e: @@ -256,7 +262,7 @@ def validate_code(code: str, workspace_root: str) -> ValidationResult: def convert_prompt_to_code_v2( prompt_content: str, - model: str = "claude-3-5-sonnet-latest", + model: str = "sonnet4.5", workspace_root: str = ".", max_iterations: int = 3, timeout: int = 300, @@ -332,7 +338,7 @@ def convert_prompt_to_code_v2( {prompt_content} REQUIREMENTS: -1. Use `from auggie_sdk import Agent` for imports +1. Use `from auggie_sdk import Auggie` for imports 2. Initialize agent with: `agent = Auggie()` or `agent = Auggie(workspace_root=".")` 3. Use `agent.run(prompt)` or `agent.run(prompt, return_type=Type)` - DO NOT call agent() directly 4. Use `agent.session()` for multi-step workflows with shared context @@ -491,8 +497,8 @@ def main(): "--model", "-m", type=str, - default="claude-3-5-sonnet-latest", - help="AI model to use (default: claude-3-5-sonnet-latest)", + default="sonnet4.5", + help="AI model to use (default: sonnet4.5)", ) parser.add_argument( "--workspace-root", @@ -540,8 +546,11 @@ def main(): # Output the code if args.output: output_file = Path(args.output) - output_file.write_text(result.code) - print(f"\n✅ Generated SDK program saved to: {output_file}") + if result.code: + output_file.write_text(result.code) + print(f"\n✅ Generated SDK program saved to: {output_file}") + else: + print(f"\n⚠️ No code generated to save", file=sys.stderr) else: print("\n" + "=" * 80) print("GENERATED SDK PROGRAM") From 7432f87ef9c964039f5b6558628f0f2bce12637b Mon Sep 17 00:00:00 2001 From: Rich Hankins Date: Thu, 22 Jan 2026 01:07:34 +0000 Subject: [PATCH 3/3] Update based on feedback --- examples/python-sdk/prompt_to_code.py | 3 --- examples/python-sdk/prompt_to_code_v2.py | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/python-sdk/prompt_to_code.py b/examples/python-sdk/prompt_to_code.py index 5ba88e6..3b98d24 100644 --- a/examples/python-sdk/prompt_to_code.py +++ b/examples/python-sdk/prompt_to_code.py @@ -22,9 +22,6 @@ from dataclasses import dataclass from typing import Optional -# Add the parent directory to the path so we can import auggie_sdk -sys.path.insert(0, str(Path(__file__).parent)) - from auggie_sdk import Auggie from auggie_sdk.exceptions import AugmentCLIError, AugmentParseError diff --git a/examples/python-sdk/prompt_to_code_v2.py b/examples/python-sdk/prompt_to_code_v2.py index 39c92c0..a27b4f0 100644 --- a/examples/python-sdk/prompt_to_code_v2.py +++ b/examples/python-sdk/prompt_to_code_v2.py @@ -28,9 +28,6 @@ from pathlib import Path from typing import Optional, List -# Add the parent directory to the path so we can import auggie_sdk -sys.path.insert(0, str(Path(__file__).parent)) - from auggie_sdk import Auggie @@ -99,6 +96,7 @@ def check_imports(code: str) -> tuple[bool, List[str]]: # List of allowed imports (common Python stdlib + augment SDK) allowed_imports = { + "__future__", "auggie_sdk", "augment", "dataclasses",