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") | |
| st.caption("Ask questions about your code, get explanations, and more") | |
| # Show suggested prompts if no history | |
| if not st.session_state.get("messages", []): | |
| st.markdown("#### π‘ Try asking:") | |
| suggestions = [ | |
| "Explain how authentication works", | |
| "Find all database queries", | |
| "What are the main entry points?", | |
| "Show me the API endpoints", | |
| "Explain the data flow" | |
| ] | |
| cols = st.columns(len(suggestions)) | |
| for i, suggestion in enumerate(suggestions): | |
| with cols[i]: | |
| if st.button(suggestion, key=f"suggest_{i}", use_container_width=True): | |
| st.session_state.pending_prompt = suggestion | |
| 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_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_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_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 prompt | |
| prompt = f"""Generate a complete implementation for this feature: | |
| **Feature Request:** | |
| {feature_desc} | |
| **Requirements:** | |
| - Framework: {framework} | |
| - Include tests: {include_tests} | |
| - Include documentation: {include_docs} | |
| - Include examples: {include_examples} | |
| **Please provide:** | |
| 1. A clear file structure showing all files to create | |
| 2. Complete, production-ready code for each file | |
| 3. Clear comments explaining the code | |
| 4. Setup/installation instructions | |
| 5. Usage examples | |
| Format each file like this: | |
| ### `path/to/filename.py` | |
| ```python | |
| # Code here | |
| ``` | |
| Make sure the code follows best practices and matches the existing codebase style.""" | |
| # 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' | |
| ] | |