From a3facd807592f712f2dbc602e2450e0ac605edd9 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Thu, 29 Jan 2026 12:42:41 +0000 Subject: [PATCH] Add shellgen core and backends modules --- app/shellgen/core/generator.py | 128 +++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 app/shellgen/core/generator.py diff --git a/app/shellgen/core/generator.py b/app/shellgen/core/generator.py new file mode 100644 index 0000000..60e33c8 --- /dev/null +++ b/app/shellgen/core/generator.py @@ -0,0 +1,128 @@ +"""Command generation logic.""" + +from dataclasses import dataclass +from typing import Optional + +from .prompts import PromptTemplates + + +@dataclass +class GenerationResult: + """Result of command generation.""" + + command: str + explanation: str + confidence: float + + +class CommandGenerator: + """Generate shell commands from natural language descriptions.""" + + def __init__(self, backend): + """Initialize the command generator. + + Args: + backend: LLM backend instance for generating commands. + """ + self.backend = backend + self.templates = PromptTemplates() + + def generate( + self, + description: str, + shell: str = "bash", + context: Optional[str] = None, + ) -> GenerationResult: + """Generate a shell command from natural language. + + Args: + description: Natural language description of what to do. + shell: Target shell (bash or zsh). + context: Optional context (current directory, files, etc.). + + Returns: + GenerationResult with command, explanation, and confidence. + """ + prompt = self.templates.generate_prompt(description, shell, context) + + response = self.backend.generate(prompt) + + command = self._extract_command(response) + explanation = self._extract_explanation(response) + confidence = self._estimate_confidence(response) + + return GenerationResult( + command=command.strip(), + explanation=explanation.strip(), + confidence=confidence, + ) + + def _extract_command(self, response: str) -> str: + """Extract the shell command from LLM response.""" + lines = response.strip().split("\n") + command = "" + + for line in lines: + line = line.strip() + if line.startswith("```"): + continue + if line.startswith("Command:") or line.startswith("```"): + if "```" in line: + parts = line.split("```") + for part in parts: + if part.strip() and not part.strip().startswith("Command"): + command = part.strip() + break + else: + command = line.replace("Command:", "").strip() + elif line and not line.startswith("#") and not line.startswith("//"): + if command: + command += " " + line + else: + command = line + + if not command: + for line in lines: + if line.startswith("$") or line.startswith("!"): + command = line[1:].strip() + break + + return command if command else response.strip() + + def _extract_explanation(self, response: str) -> str: + """Extract the explanation from LLM response.""" + lines = response.strip().split("\n") + explanation = [] + in_explanation = False + + for line in lines: + line_lower = line.lower().strip() + if "explanation" in line_lower or "what this does" in line_lower: + in_explanation = True + continue + if line.startswith("```"): + in_explanation = False + continue + if in_explanation: + explanation.append(line) + elif line.startswith("#") or line.startswith("//"): + explanation.append(line.lstrip("#/").strip()) + + return " ".join(explanation) if explanation else "Generated command" + + def _estimate_confidence(self, response: str) -> float: + """Estimate confidence score based on response characteristics.""" + confidence = 0.5 + + if "```" in response: + confidence += 0.2 + if len(response) > 50: + confidence += 0.1 + if "sure" in response.lower() or "certain" in response.lower(): + confidence += 0.1 + if "?" in response: + confidence -= 0.2 + if "not sure" in response.lower() or "uncertain" in response.lower(): + confidence -= 0.3 + + return max(0.0, min(1.0, confidence))