"""Explain command for Local Code Assistant.""" from pathlib import Path from typing import Optional import click from rich.console import Console from rich.markdown import Markdown from rich.panel import Panel from local_code_assistant.commands.base import BaseCommand from local_code_assistant.prompts.templates import LanguageConfig, PromptTemplates from local_code_assistant.services.ollama import OllamaService console = Console() class ExplainCommand(BaseCommand): """Command for explaining code.""" def __init__(self, ollama: OllamaService, config): """Initialize explain command. Args: ollama: Ollama service instance. config: Configuration service instance. """ super().__init__(ollama, config) self.supported_languages = LanguageConfig.get_supported_languages() def run( self, code: str, language: Optional[str] = None, format_markdown: bool = False ) -> str: """Execute code explanation. Args: code: Code to explain. language: Programming language (auto-detected if not provided). format_markdown: Whether to format as markdown. Returns: Explanation text. """ if not self.ollama.check_connection(): raise click.ClickException( "Cannot connect to Ollama. Make sure it's running." ) if language is None: language = self._detect_language(code) if language not in self.supported_languages: language = "text" console.print("[dim]Analyzing code...[/dim]") full_prompt = PromptTemplates.code_explanation( language=language, code=code ) system_prompt = PromptTemplates.build_system_prompt( "You are explaining code to a developer. Be clear and concise." ) try: explanation = self.ollama.generate( prompt=full_prompt, model=self._get_model(), system=system_prompt, temperature=self._get_temperature(0.3) ) self._display_output(explanation, format_markdown) return explanation except Exception as e: raise click.ClickException(f"Explanation failed: {str(e)}") from e def run_file( self, file: Path, format_markdown: bool = False ) -> str: """Explain code from a file. Args: file: Path to file. format_markdown: Whether to format as markdown. Returns: Explanation text. """ if not file.exists(): raise click.ClickException(f"File not found: {file}") code = file.read_text() language = self._detect_language_from_file(file) return self.run(code, language, format_markdown) def _detect_language(self, code: str) -> str: """Detect programming language from code content. Args: code: Source code. Returns: Detected language. """ if "def " in code or "import " in code or "class " in code: return "python" elif "function " in code or "const " in code or "let " in code: return "javascript" elif "interface " in code or "type " in code or ": string" in code: return "typescript" elif "func " in code or "package " in code: return "go" elif "fn " in code or "let " in code or "struct " in code: return "rust" return "text" def _detect_language_from_file(self, file: Path) -> str: """Detect language from file extension. Args: file: File path. Returns: Language name. """ suffix = file.suffix.lower() language_map = { ".py": "python", ".js": "javascript", ".ts": "typescript", ".tsx": "typescript", ".go": "go", ".rs": "rust" } return language_map.get(suffix, "text") def _display_output(self, explanation: str, as_markdown: bool): """Display explanation. Args: explanation: Explanation text. as_markdown: Whether to render as markdown. """ if as_markdown: console.print(Panel(Markdown(explanation), title="Explanation")) else: console.print(Panel(explanation, title="Explanation")) @click.command() @click.argument("file", type=click.Path(exists=True, path_type=Path)) @click.option( "--language", "-l", type=click.Choice(LanguageConfig.get_supported_languages()), default=None, help="Programming language (auto-detected if not specified)" ) @click.option( "--markdown/--no-markdown", default=False, help="Format output as markdown" ) @click.pass_context def explain_cmd( ctx: click.Context, file: Path, language: Optional[str], markdown: bool ): """Explain code from a file. Example: local-code-assistant explain script.py local-code-assistant explain app.ts --markdown \f Args: ctx: Click context. file: Path to file to explain. language: Programming language. markdown: Format as markdown. """ config = ctx.obj["config"] ollama_service = OllamaService(config) ctx.obj["ollama_service"] = ollama_service command = ExplainCommand(ollama_service, config) command.run_file(file, markdown)