From a033bb7c93ef74f1a74dcbe3d052a4c37ce68064 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 19:31:04 +0000 Subject: [PATCH] Initial upload: Git AI Documentation Generator v0.1.0 --- src/commands/api_docs.py | 156 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/commands/api_docs.py diff --git a/src/commands/api_docs.py b/src/commands/api_docs.py new file mode 100644 index 0000000..3218387 --- /dev/null +++ b/src/commands/api_docs.py @@ -0,0 +1,156 @@ +"""API documentation generation command.""" + +import re +from pathlib import Path + +import click + +from src.config import Config +from src.git_utils import ( + GitError, + NotGitRepositoryError, + get_file_content_at_ref, + get_repo, + get_unstaged_diff, +) +from src.ollama_client import OllamaClient, OllamaConnectionError +from src.output import console, display_api_docs, display_error, display_info, display_model_info + + +LANGUAGE_EXTENSIONS = { + ".py": "python", + ".js": "javascript", + ".ts": "typescript", + ".go": "go", + ".rs": "rust", + ".java": "java", + ".c": "c", + ".cpp": "cpp", + ".h": "c", +} + +FRAMEWORK_PATTERNS = { + "fastapi": r"from fastapi import|@app\.|FastAPI\(", + "flask": r"from flask import|@app\.route|Flask\(", + "express": r"express\(\)|@.*route\(|app\.get\(|app\.post\(", + "django": r"from django|path\(|urlpatterns", +} + + +def detect_language(file_path: str) -> str | None: + """Detect programming language from file extension. + + Args: + file_path: Path to the file. + + Returns: + Language name or None. + """ + ext = Path(file_path).suffix.lower() + return LANGUAGE_EXTENSIONS.get(ext) + + +def detect_framework(files: list[str]) -> str | None: + """Detect web framework from file contents. + + Args: + files: List of file paths to check. + + Returns: + Framework name or None. + """ + for file_path in files: + try: + with open(file_path) as f: + content = f.read(10000) + for framework, pattern in FRAMEWORK_PATTERNS.items(): + if re.search(pattern, content): + return framework + except OSError: + continue + return None + + +def find_api_files(files: list[str]) -> list[str]: + """Find files that likely contain API endpoints. + + Args: + files: List of file paths. + + Returns: + Filtered list of API-related files. + """ + api_indicators = ["route", "endpoint", "api", "controller", "handler"] + api_files = [] + for file_path in files: + file_lower = file_path.lower() + if any(indicator in file_lower for indicator in api_indicators): + api_files.append(file_path) + return api_files + + +@click.command() +@click.option("--staged/--all", default=False, help="Use staged changes or all changes") +@click.option("--ref", help="Git reference to compare against") +@click.option("--framework", help="Force framework detection") +@click.pass_context +def api_docs(ctx: click.Context, staged: bool, ref: str | None, framework: str | None) -> None: + """Generate API documentation from code changes.""" + config: Config = ctx.obj["config"] + client = OllamaClient(config) + + try: + display_info("Connecting to Ollama...") + client.connect_with_retry() + display_model_info(config.ollama_host, config.model) + except OllamaConnectionError as e: + display_error(str(e)) + return + + try: + repo = get_repo() + except NotGitRepositoryError: + display_error("Current directory is not a git repository") + return + + with console.status("Analyzing code changes..."): + if ref: + try: + changed_files = repo.head.commit.diff(f"{ref}..HEAD") + code_changes = {} + for change in changed_files: + if change.a_path: + try: + old_content = get_file_content_at_ref(repo, change.a_path, ref) + except GitError: + old_content = "" + new_content = (old_content + "\n" + change.diff.decode("utf-8", errors="replace") + if change.diff else old_content) + code_changes[change.a_path] = new_content + except GitError as e: + display_error(str(e)) + return + else: + diff_content = get_unstaged_diff(repo) + if not diff_content.strip(): + display_info("No changes found") + return + changed_files = [f"changed_file_{i}" for i in range(1)] + code_changes = {"changed_file.diff": diff_content} + + if not code_changes: + display_info("No code changes detected") + return + + detected_framework = framework + if not detected_framework: + api_files = find_api_files(list(code_changes.keys())) + if api_files: + detected_framework = detect_framework(api_files) + + with console.status("Generating API documentation..."): + try: + docs = client.generate_api_docs(code_changes, framework=detected_framework) + display_api_docs(docs, detected_framework) + except OllamaConnectionError as e: + display_error(str(e))