Add utils, templates, config, interactive, and github modules
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
2026-02-05 08:45:42 +00:00
parent 1f5d2ea540
commit 8fea9ced0c

View File

@@ -0,0 +1,313 @@
"""Template system for README generation."""
import os
from pathlib import Path
from typing import Optional
from jinja2 import Environment, FileSystemLoader, BaseLoader
from ..models import Project
class TemplateRenderer:
"""Renders README templates with project context."""
BUILTIN_TEMPLATES = {
"base": """# {{ title }}
{% if description %}
{{ description }}
{% endif %}
{% if badges %}
{{ badges }}
{% endif %}
{% if table_of_contents %}
## Table of Contents
- [Overview](#overview)
{% if installation_steps %}- [Installation](#installation){% endif %}
{% if usage_examples %}- [Usage](#usage){% endif %}
{% if features %}- [Features](#features){% endif %}
{% if api_functions %}- [API Reference](#api-reference){% endif %}
{% if contributing_guidelines %}- [Contributing](#contributing){% endif %}
{% if license_info %}- [License](#license){% endif %}
{% endif %}
## Overview
{{ project_overview }}
{% if tech_stack %}
## Supported Languages
This project uses:
{% for tech in tech_stack %}
- **{{ tech }}**
{% endfor %}
{% endif %}
{% if installation_steps %}
## Installation
```bash
{{ installation_steps|join('\\n') }}
```
{% if dependencies %}
### Dependencies
{% for dep in dependencies %}
- `{{ dep.name }}`{% if dep.version %} v{{ dep.version }}{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% if usage_examples %}
## Usage
### Basic Usage
```python
{{ usage_examples[0] }}
```
{% endif %}
{% if features %}
## Features
{% for feature in features %}
- {{ feature }}
{% endfor %}
{% endif %}
{% if api_functions %}
## API Reference
{% for func in api_functions %}
### `{{ func.name }}`
{% if func.docstring %}
{{ func.docstring }}
{% endif %}
**Parameters:** `{{ func.parameters|join(', ') }}`
{% endfor %}
{% endif %}
{% if contributing_guidelines %}
## Contributing
{{ contributing_guidelines }}
{% endif %}
{% if license_info %}
## License
{{ license_info }}
{% endif %}
---
*Generated by Auto README Generator on {{ generated_at }}*
""",
}
def __init__(self, custom_template_dir: Optional[Path] = None):
"""Initialize the template renderer."""
self.env = Environment(
loader=FileSystemLoader(str(custom_template_dir)) if custom_template_dir else BaseLoader(),
trim_blocks=True,
lstrip_blocks=True,
)
self.custom_template_dir = custom_template_dir
def render(
self,
project: Project,
template_name: str = "base",
**extra_context,
) -> str:
"""Render a README template with project context."""
context = self._build_context(project, **extra_context)
if template_name in self.BUILTIN_TEMPLATES:
template = self.env.from_string(self.BUILTIN_TEMPLATES[template_name])
elif self.custom_template_dir:
try:
template = self.env.get_template(template_name)
except Exception:
template = self.env.from_string(self.BUILTIN_TEMPLATES["base"])
else:
template = self.env.from_string(self.BUILTIN_TEMPLATES["base"])
return template.render(**context)
def _build_context(self, project: Project, **extra_context) -> dict:
"""Build the template context from project data."""
context = {
"title": project.config.name if project.config else project.root_path.name,
"description": project.config.description if project.config else None,
"project_overview": self._generate_overview(project),
"badges": None,
"tech_stack": self._get_tech_stack(project),
"installation_steps": project.installation_steps or self._generate_installation_steps(project),
"usage_examples": project.usage_examples or self._generate_usage_examples(project),
"features": project.features or self._detect_features(project),
"api_functions": project.all_functions()[:20],
"contributing_guidelines": self._generate_contributing_guidelines(project),
"license_info": project.config.license if project.config else None,
"generated_at": project.generated_at.strftime("%Y-%m-%d"),
"table_of_contents": True,
"dependencies": project.dependencies,
}
context.update(extra_context)
return context
def _generate_overview(self, project: Project) -> str:
"""Generate a project overview."""
lines = [
f"A project of type **{project.project_type.value}** located at `{project.root_path}`.",
]
lines.append(f"Contains {len(project.files)} files.")
if project.config and project.config.description:
lines.insert(0, project.config.description)
return " ".join(lines)
def _get_tech_stack(self, project: Project) -> list[str]:
"""Extract technology stack from dependencies."""
tech = [project.project_type.value.title()]
dep_names = {dep.name.lower() for dep in project.dependencies}
framework_map = {
"fastapi": "FastAPI",
"flask": "Flask",
"django": "Django",
"click": "Click",
"express": "Express.js",
"react": "React",
"vue": "Vue.js",
"angular": "Angular",
"gin": "Gin",
"echo": "Echo",
"actix-web": "Actix Web",
"rocket": "Rocket",
}
for dep, framework in framework_map.items():
if dep in dep_names:
tech.append(framework)
return list(dict.fromkeys(tech))
def _generate_installation_steps(self, project: Project) -> list[str]:
"""Generate installation steps based on project type."""
steps = []
if project.project_type.value == "python":
steps = [
"pip install -r requirements.txt",
"pip install -e .",
]
elif project.project_type.value in ("javascript", "typescript"):
steps = ["npm install", "npm run build"]
elif project.project_type.value == "go":
steps = ["go mod download", "go build"]
elif project.project_type.value == "rust":
steps = ["cargo build --release"]
if project.git_info and project.git_info.is_repo:
steps.insert(0, f"git clone {project.git_info.remote_url or 'https://github.com/user/repo.git'}")
return steps
def _generate_usage_examples(self, project: Project) -> list[str]:
"""Generate usage examples based on project type."""
if project.project_type.value == "python":
return ["from project_name import main", "main()"]
elif project.project_type.value in ("javascript", "typescript"):
return ["import { main } from 'project-name'", "main()"]
elif project.project_type.value == "go":
return ["go run main.go"]
elif project.project_type.value == "rust":
return ["cargo run --release"]
return ["# Check the docs for usage information"]
def _detect_features(self, project: Project) -> list[str]:
"""Detect project features from structure."""
features = []
if any(f.file_type.name == "TEST" for f in project.files):
features.append("Test suite included")
if project.git_info and project.git_info.is_repo:
features.append("Git repository")
if project.dependencies:
features.append(f"Uses {len(project.dependencies)} dependencies")
if project.all_classes():
features.append(f"Contains {len(project.all_classes())} classes")
if project.all_functions():
features.append(f"Contains {len(project.all_functions())} functions")
return features if features else ["Auto-generated documentation"]
def _generate_contributing_guidelines(self, project: Project) -> str:
"""Generate contributing guidelines."""
guidelines = [
"1. Fork the repository",
"2. Create a feature branch (`git checkout -b feature/amazing-feature`)",
"3. Commit your changes (`git commit -m 'Add some amazing feature'`)",
"4. Push to the branch (`git push origin feature/amazing-feature`)",
"5. Open a Pull Request",
]
if project.project_type.value == "python":
guidelines.extend([
"",
"For Python development:",
"- Run tests with `pytest`",
"- Format code with `black` and `isort`",
"- Check types with `mypy`",
])
elif project.project_type.value in ("javascript", "typescript"):
guidelines.extend([
"",
"For JavaScript/TypeScript development:",
"- Run tests with `npm test`",
"- Format code with `npm run format`",
"- Lint with `npm run lint`",
])
return "\n".join(guidelines)
class TemplateManager:
"""Manages template operations."""
def __init__(self, template_dir: Optional[Path] = None):
"""Initialize the template manager."""
self.template_dir = template_dir
self.renderer = TemplateRenderer(custom_template_dir=template_dir)
def list_templates(self) -> list[str]:
"""List available templates."""
return list(self.renderer.BUILTIN_TEMPLATES.keys())
def get_template_path(self, name: str) -> Optional[Path]:
"""Get the path to a template."""
if name in self.renderer.BUILTIN_TEMPLATES:
return None
if self.template_dir:
return self.template_dir / name
return None