Add CLI commands and Gitea Actions workflow
This commit is contained in:
201
local_code_assistant/commands/explain.py
Normal file
201
local_code_assistant/commands/explain.py
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user