From 543058584e175f8c2e0ad58caf9e4a13ae981c87 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sat, 31 Jan 2026 15:27:58 +0000 Subject: [PATCH] Add CLI commands and Gitea Actions workflow --- local_code_assistant/commands/explain.py | 201 +++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 local_code_assistant/commands/explain.py diff --git a/local_code_assistant/commands/explain.py b/local_code_assistant/commands/explain.py new file mode 100644 index 0000000..58b84ed --- /dev/null +++ b/local_code_assistant/commands/explain.py @@ -0,0 +1,201 @@ +"""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) \ No newline at end of file