Initial upload: PatternForge CLI tool with pattern detection and boilerplate generation
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-02-02 22:26:01 +00:00
parent 08b59ff9a6
commit 1d8772636f

View File

@@ -0,0 +1,151 @@
import json
from pathlib import Path
from typing import Any
from patternforge.config import Config
from patternforge.template import TemplateManager
class BoilerplateGenerator:
def __init__(self, config: Config) -> None:
self.config = config
self.template_manager = TemplateManager(config)
def _to_snake_case(self, name: str) -> str:
import re
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
def _to_camel_case(self, name: str) -> str:
components = name.replace("-", "_").split("_")
return components[0].lower() + "".join(x.title() for x in components[1:])
def _to_pascal_case(self, name: str) -> str:
camel = self._to_camel_case(name)
if not camel:
return ""
return camel[0].upper() + camel[1:]
def _infer_context(
self,
template_name: str,
entity_name: str | None,
) -> dict[str, Any]:
template = self.template_manager.get_template(template_name)
if not template:
raise ValueError(f"Template not found: {template_name}")
name = entity_name or template_name
patterns = template.get("patterns", {})
primary_naming = patterns.get("summary", {}).get("primary_naming", "snake_case")
context: dict[str, Any] = {
"project_name": "generated-project",
"entity_name": name,
"class_name": "",
"function_name": "",
"fields": [],
"methods": [],
"params": "",
"docstring": f"Generated {name}",
"class_docstring": f"Auto-generated class for {name}",
}
if primary_naming == "PascalCase":
context["class_name"] = self._to_pascal_case(name)
context["function_name"] = self._to_camel_case(name)
elif primary_naming == "camelCase":
context["class_name"] = self._to_pascal_case(name)
context["function_name"] = self._to_camel_case(name)
else:
context["class_name"] = self._to_pascal_case(name)
context["function_name"] = name
context["fields"] = [
f"{self._to_camel_case(name)}Id",
f"{self._to_camel_case(name)}Name",
"createdAt",
"updatedAt",
]
context["methods"] = [
{"name": "validate", "params": "", "docstring": "Validate the entity"},
{"name": "to_dict", "params": "", "docstring": "Convert to dictionary"},
{"name": "to_json", "params": "", "docstring": "Convert to JSON string"},
]
context["init_params"] = ", ".join(
f"{self._to_camel_case(name)}{field}"
for field in context["fields"]
)
context["params"] = ", ".join(
f"{self._to_camel_case(name)}{field}" for field in context["fields"][:2]
)
return context
def _get_file_extension(self, template_name: str) -> str:
template = self.template_manager.get_template(template_name)
if not template:
return ".txt"
lang = template.get("language", "")
extensions = {
"python": ".py",
"javascript": ".js",
"typescript": ".ts",
"java": ".java",
"cpp": ".cpp",
"c": ".c",
"rust": ".rs",
"go": ".go",
"ruby": ".rb",
}
return extensions.get(lang, ".txt")
def generate(
self,
template_name: str,
output_dir: str,
data_file: str | None = None,
entity_name: str | None = None,
) -> list[Path]:
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
context = self._infer_context(template_name, entity_name)
if data_file:
with open(data_file) as f:
data = json.load(f)
context.update(data)
try:
content = self.template_manager.render_template(template_name, context)
except ValueError as e:
raise RuntimeError(f"Failed to render template: {e}")
ext = self._get_file_extension(template_name)
filename = f"{entity_name or template_name}{ext}"
file_path = output_path / filename
file_path.write_text(content)
return [file_path]
def generate_multiple(
self,
template_name: str,
output_dir: str,
entities: list[dict[str, Any]],
) -> list[Path]:
generated: list[Path] = []
for entity in entities:
name = entity.get("name", "untitled")
paths = self.generate(
template_name,
output_dir,
entity.get("data_file"),
name,
)
generated.extend(paths)
return generated