Spaces:
Running
Running
| """ | |
| Multi-mode interface components for Codebase Agent. | |
| Provides different interaction modes: Chat, Search, Refactor, Generate | |
| """ | |
| import streamlit as st | |
| from typing import Optional, Dict, Any | |
| import os | |
| from pathlib import Path | |
| def get_workspace_root() -> str: | |
| """ | |
| Get the workspace root directory for the indexed codebase. | |
| Returns: | |
| Path to the extracted/processed codebase | |
| """ | |
| # Check if we have a processed data directory | |
| data_dir = Path("data") | |
| if data_dir.exists(): | |
| # Find the extracted folder inside data | |
| for item in data_dir.iterdir(): | |
| if item.is_dir() and not item.name.startswith('.'): | |
| return str(item) | |
| # Fallback to data directory itself | |
| return "data" | |
| def render_mode_selector() -> str: | |
| """ | |
| Render mode selector and return selected mode. | |
| Returns: | |
| Selected mode: 'chat', 'search', 'refactor', or 'generate' | |
| """ | |
| # Mode selector with icons | |
| mode = st.radio( | |
| "", | |
| ["π¬ Chat", "π Search", "π§ Refactor", "β¨ Generate"], | |
| horizontal=True, | |
| key="mode_selector", | |
| help="Select interaction mode" | |
| ) | |
| # Map display name to mode key | |
| mode_map = { | |
| "π¬ Chat": "chat", | |
| "π Search": "search", | |
| "π§ Refactor": "refactor", | |
| "β¨ Generate": "generate" | |
| } | |
| return mode_map[mode] | |
| def render_chat_mode(chat_engine): | |
| """ | |
| Render standard chat interface. | |
| Args: | |
| chat_engine: ChatEngine instance | |
| """ | |
| st.markdown("### π¬ Chat with Your Codebase") | |
| # Show suggested prompts if no history | |
| if not st.session_state.get("messages", []): | |
| st.markdown("π‘ **Try asking:**") | |
| # Row 1 - 2 suggestions | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| if st.button("π Explain project structure", key="suggest_0", use_container_width=True): | |
| st.session_state.pending_prompt = "Explain the project structure and main components" | |
| st.rerun() | |
| with col2: | |
| if st.button("π List all functions", key="suggest_1", use_container_width=True): | |
| st.session_state.pending_prompt = "List all the main functions and their purpose" | |
| st.rerun() | |
| # Row 2 - 2 suggestions | |
| col3, col4 = st.columns(2) | |
| with col3: | |
| if st.button("β‘ Generate code", key="suggest_2", use_container_width=True): | |
| st.session_state.pending_prompt = "Generate a new utility function for this project" | |
| st.rerun() | |
| with col4: | |
| if st.button("π§ Suggest improvements", key="suggest_3", use_container_width=True): | |
| st.session_state.pending_prompt = "What improvements would you suggest for this code?" | |
| st.rerun() | |
| # Return True to continue with normal chat flow | |
| return True | |
| def render_search_mode(): | |
| """ | |
| Render MCP code search interface. | |
| """ | |
| st.markdown("### π Search Codebase") | |
| st.caption("Find patterns across your entire codebase using regex") | |
| # Get workspace root | |
| workspace = get_workspace_root() | |
| st.info(f"π Searching in: `{workspace}`") | |
| # Search input | |
| col1, col2 = st.columns([3, 1]) | |
| with col1: | |
| pattern = st.text_input( | |
| "Search Pattern", | |
| placeholder="e.g., class (or def.*login)", | |
| help="Enter a regex pattern to search for" | |
| ) | |
| with col2: | |
| is_regex = st.checkbox("Regex", value=True, help="Use regex pattern matching") | |
| # File pattern filter | |
| file_pattern = st.text_input( | |
| "File Pattern", | |
| value="**/*.py", | |
| help="Glob pattern for files to search (e.g., **/*.py, src/**/*.js)" | |
| ) | |
| # Context lines | |
| context_lines = st.slider("Context Lines", 0, 5, 2, help="Number of lines to show before/after match") | |
| # Search button | |
| if st.button("π Search", type="primary", use_container_width=True): | |
| if not pattern: | |
| st.warning("Please enter a search pattern") | |
| return | |
| with st.spinner("Searching codebase..."): | |
| try: | |
| from code_chatbot.mcp.mcp_client import MCPClient | |
| client = MCPClient(workspace_root=workspace) | |
| results = client.search_code( | |
| pattern=pattern, | |
| file_pattern=file_pattern, | |
| context_lines=context_lines, | |
| is_regex=is_regex | |
| ) | |
| if results: | |
| st.success(f"β Found {len(results)} matches") | |
| # Display results | |
| for i, result in enumerate(results[:20], 1): # Limit to 20 results | |
| with st.expander(f"π {result.file_path}:L{result.line_number}"): | |
| # Show context before | |
| if result.context_before: | |
| st.code("\n".join(result.context_before), language="python") | |
| # Highlight matching line | |
| st.markdown(f"**β Line {result.line_number}:**") | |
| st.code(result.line_content, language="python") | |
| # Show context after | |
| if result.context_after: | |
| st.code("\n".join(result.context_after), language="python") | |
| if len(results) > 20: | |
| st.info(f"Showing first 20 of {len(results)} results") | |
| else: | |
| st.info("No matches found. Try a different pattern.") | |
| except Exception as e: | |
| st.error(f"Search failed: {e}") | |
| st.exception(e) | |
| def render_refactor_mode(): | |
| """ | |
| Render MCP refactoring interface. | |
| """ | |
| st.markdown("### π§ Refactor Code") | |
| st.caption("Perform automated refactorings across your codebase") | |
| # Get workspace root | |
| workspace = get_workspace_root() | |
| st.info(f"π Refactoring in: `{workspace}`") | |
| # Refactoring type selector | |
| refactor_type = st.selectbox( | |
| "Refactoring Type", | |
| ["Custom Regex", "Common Patterns"], | |
| help="Choose refactoring approach" | |
| ) | |
| if refactor_type == "Custom Regex": | |
| # Custom regex refactoring | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| search_pattern = st.text_input( | |
| "Search Pattern", | |
| placeholder="e.g., print\\((.*)\\)", | |
| help="Regex pattern to find" | |
| ) | |
| with col2: | |
| replace_pattern = st.text_input( | |
| "Replace Pattern", | |
| placeholder="e.g., logger.info(\\1)", | |
| help="Replacement (supports capture groups like \\1)" | |
| ) | |
| file_pattern = st.text_input( | |
| "File Pattern", | |
| value="**/*.py", | |
| help="Files to process" | |
| ) | |
| dry_run = st.checkbox("Dry Run (Preview Only)", value=True, help="Preview changes without applying") | |
| if st.button("π§ Refactor", type="primary", use_container_width=True): | |
| if not search_pattern or not replace_pattern: | |
| st.warning("Please enter both search and replace patterns") | |
| return | |
| with st.spinner("Processing refactoring..."): | |
| try: | |
| from code_chatbot.mcp.mcp_client import MCPClient | |
| client = MCPClient(workspace_root=workspace) | |
| result = client.refactor_code( | |
| search_pattern=search_pattern, | |
| replace_pattern=replace_pattern, | |
| file_pattern=file_pattern, | |
| dry_run=dry_run | |
| ) | |
| if result.success: | |
| mode_text = "Preview" if dry_run else "Applied" | |
| st.success(f"β Refactoring {mode_text}: {result.files_changed} files, {result.total_replacements} replacements") | |
| # Show changes | |
| if result.changes: | |
| for change in result.changes[:10]: # Limit to 10 files | |
| with st.expander(f"π {change['file_path']} ({change['replacements']} replacements)"): | |
| if change.get('preview'): | |
| st.code(change['preview'], language="diff") | |
| if len(result.changes) > 10: | |
| st.info(f"Showing first 10 of {len(result.changes)} changed files") | |
| else: | |
| st.info("No matches found for the given pattern") | |
| if dry_run and result.files_changed > 0: | |
| st.info("π‘ Uncheck 'Dry Run' to apply these changes") | |
| else: | |
| st.error(f"Refactoring failed: {result.error}") | |
| except Exception as e: | |
| st.error(f"Refactoring failed: {e}") | |
| st.exception(e) | |
| else: | |
| # Common patterns | |
| st.markdown("#### Common Refactoring Patterns") | |
| common_patterns = { | |
| "print() β logging": { | |
| "search": r"print\((.*)\)", | |
| "replace": r"logger.info(\1)", | |
| "description": "Replace print statements with logging" | |
| }, | |
| "assertEqual β assert ==": { | |
| "search": r"assertEqual\(([^,]+),\s*([^)]+)\)", | |
| "replace": r"assert \1 == \2", | |
| "description": "Convert unittest to pytest assertions" | |
| }, | |
| "Remove trailing whitespace": { | |
| "search": r"[ \t]+$", | |
| "replace": "", | |
| "description": "Clean up trailing whitespace" | |
| } | |
| } | |
| pattern_choice = st.selectbox( | |
| "Select Pattern", | |
| list(common_patterns.keys()) | |
| ) | |
| selected = common_patterns[pattern_choice] | |
| st.info(selected["description"]) | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.code(f"Search: {selected['search']}", language="regex") | |
| with col2: | |
| st.code(f"Replace: {selected['replace']}", language="regex") | |
| dry_run = st.checkbox("Dry Run (Preview Only)", value=True, key="common_dry_run") | |
| if st.button("Apply Refactoring", type="primary", use_container_width=True): | |
| with st.spinner("Processing..."): | |
| try: | |
| from code_chatbot.mcp.mcp_client import MCPClient | |
| client = MCPClient(workspace_root=workspace) | |
| result = client.refactor_code( | |
| search_pattern=selected["search"], | |
| replace_pattern=selected["replace"], | |
| file_pattern="**/*.py", | |
| dry_run=dry_run | |
| ) | |
| if result.success: | |
| st.success(f"β {result.files_changed} files, {result.total_replacements} replacements") | |
| if result.changes: | |
| for change in result.changes[:5]: | |
| with st.expander(f"π {change['file_path']}"): | |
| st.code(change.get('preview', 'No preview'), language="diff") | |
| else: | |
| st.error(f"Failed: {result.error}") | |
| except Exception as e: | |
| st.error(f"Failed: {e}") | |
| def render_generate_mode(chat_engine): | |
| """ | |
| Render code generation interface using ChatEngine. | |
| Args: | |
| chat_engine: ChatEngine instance | |
| """ | |
| st.markdown("### β¨ Generate New Features") | |
| st.caption("Use AI to scaffold complete features from descriptions") | |
| # Feature description | |
| feature_desc = st.text_area( | |
| "Describe the feature you want to build", | |
| placeholder="Example: Create a user authentication system with JWT tokens, login/logout endpoints, password hashing with bcrypt, and session management", | |
| height=120, | |
| help="Be as detailed as possible" | |
| ) | |
| # Options | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| include_tests = st.checkbox("Generate Tests", value=True) | |
| with col2: | |
| include_docs = st.checkbox("Generate Docs", value=True) | |
| with col3: | |
| include_examples = st.checkbox("Include Examples", value=True) | |
| # Framework selection | |
| framework = st.selectbox( | |
| "Framework/Stack", | |
| ["Auto-detect from codebase", "FastAPI", "Flask", "Django", "Express.js", "React", "Vue.js"], | |
| help="Technology stack for the feature" | |
| ) | |
| if st.button("π Generate Feature", type="primary", use_container_width=True): | |
| if not feature_desc: | |
| st.warning("Please describe the feature you want to build") | |
| return | |
| if not chat_engine: | |
| st.error("β οΈ Chat engine not initialized. Please index your codebase first.") | |
| return | |
| with st.spinner("π€ Generating feature... (this may take 30-60 seconds)"): | |
| try: | |
| # Build comprehensive AI Engineer prompt | |
| prompt = f"""You are a **Senior AI/Software Engineer** with 15+ years of experience building production systems at top tech companies. Your expertise spans system design, security, scalability, and clean code architecture. | |
| ## π― MISSION | |
| Analyze the existing codebase and generate a **production-ready, enterprise-grade** implementation for the requested feature. | |
| --- | |
| ## π FEATURE REQUEST | |
| {feature_desc} | |
| --- | |
| ## βοΈ CONFIGURATION | |
| | Setting | Value | | |
| |---------|-------| | |
| | **Framework** | {framework} | | |
| | **Include Tests** | {include_tests} | | |
| | **Include Documentation** | {include_docs} | | |
| | **Include Examples** | {include_examples} | | |
| --- | |
| ## π§ YOUR APPROACH (Follow This Process) | |
| ### Phase 1: Architecture Analysis | |
| Before writing code, analyze the existing codebase to understand: | |
| - **Project structure** and conventions | |
| - **Naming patterns** (snake_case, camelCase, etc.) | |
| - **Import style** and module organization | |
| - **Error handling** patterns | |
| - **Logging** approach | |
| - **Configuration** management style | |
| ### Phase 2: Design the Solution | |
| - Choose appropriate **design patterns** (Factory, Repository, Service Layer, etc.) | |
| - Plan **database schema** changes if needed | |
| - Define **API contracts** (request/response schemas) | |
| - Consider **edge cases** and error scenarios | |
| - Plan for **scalability** and performance | |
| ### Phase 3: Implementation | |
| Generate code that includes: | |
| 1. **ποΈ Architecture Overview** | |
| - High-level system diagram (ASCII or Mermaid) | |
| - Component relationships and data flow | |
| 2. **π File Structure** | |
| ``` | |
| feature_name/ | |
| βββ __init__.py | |
| βββ models.py # Data models/schemas | |
| βββ service.py # Business logic | |
| βββ routes.py # API endpoints (if applicable) | |
| βββ utils.py # Helper functions | |
| βββ tests/ | |
| βββ test_service.py | |
| βββ test_routes.py | |
| ``` | |
| 3. **π» Complete Code** for each file with: | |
| - **Type hints** on all functions | |
| - **Docstrings** with Args, Returns, Raises | |
| - **Input validation** and sanitization | |
| - **Error handling** with custom exceptions | |
| - **Logging** at appropriate levels | |
| - **Security** considerations (auth, injection prevention, etc.) | |
| 4. **π§ͺ Test Suite** (if enabled): | |
| - Unit tests with pytest | |
| - Edge case coverage | |
| - Mock external dependencies | |
| - Minimum 80% code coverage target | |
| 5. **π Documentation** (if enabled): | |
| - API documentation with examples | |
| - Usage guide | |
| - Configuration options | |
| 6. **π Integration Guide**: | |
| - Step-by-step setup instructions | |
| - Environment variables needed | |
| - Dependencies to install | |
| - How to integrate with existing code | |
| --- | |
| ## π CODE FILE FORMAT | |
| For each file, use this exact format: | |
| ### `path/to/filename.py` | |
| ```python | |
| \"\"\" | |
| Module docstring explaining purpose. | |
| \"\"\" | |
| # imports here | |
| # code here with full implementation | |
| ``` | |
| --- | |
| ## β QUALITY CHECKLIST | |
| Ensure your code: | |
| - [ ] Follows existing codebase conventions | |
| - [ ] Has no hardcoded values (use config/env vars) | |
| - [ ] Handles all error cases gracefully | |
| - [ ] Is thread-safe if applicable | |
| - [ ] Has no security vulnerabilities | |
| - [ ] Is optimized for performance | |
| - [ ] Is maintainable and readable | |
| --- | |
| ## π¨ STYLE REQUIREMENTS | |
| - Clean, readable code over clever code | |
| - Self-documenting function/variable names | |
| - Comments for complex logic only | |
| - Consistent formatting with project style | |
| - DRY (Don't Repeat Yourself) principle | |
| Now generate the complete, production-ready implementation:""" | |
| # Use chat engine | |
| answer, sources = chat_engine.chat(prompt) | |
| st.success("β Feature generated!") | |
| # Display generated content | |
| st.markdown("---") | |
| st.markdown("#### π Generated Feature") | |
| st.markdown(answer) | |
| # Show sources if available | |
| if sources: | |
| st.markdown("---") | |
| with st.expander("π Reference Files Used"): | |
| for source in sources: | |
| if isinstance(source, dict): | |
| st.write(f"- `{source.get('file_path', 'Unknown')}`") | |
| else: | |
| st.write(f"- `{source}`") | |
| except Exception as e: | |
| st.error(f"Generation failed: {e}") | |
| st.exception(e) | |
| # Export functions | |
| __all__ = [ | |
| 'render_mode_selector', | |
| 'render_chat_mode', | |
| 'render_search_mode', | |
| 'render_refactor_mode', | |
| 'render_generate_mode' | |
| ] | |