From 08b59ff9a66b7a62abcb1ac8c7be3f7059eb4bcc Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Mon, 2 Feb 2026 22:26:00 +0000 Subject: [PATCH] Initial upload: PatternForge CLI tool with pattern detection and boilerplate generation --- src/patternforge/template.py | 233 +++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 src/patternforge/template.py diff --git a/src/patternforge/template.py b/src/patternforge/template.py new file mode 100644 index 0000000..f8276a8 --- /dev/null +++ b/src/patternforge/template.py @@ -0,0 +1,233 @@ +import json +from datetime import datetime +from pathlib import Path +from typing import Any + +import yaml +from jinja2 import Environment, FileSystemLoader, TemplateSyntaxError + +from patternforge.config import Config + + +class TemplateManager: + def __init__(self, config: Config) -> None: + self.config = config + self._ensure_templates_dir() + + def _ensure_templates_dir(self) -> None: + self.config.templates_dir.mkdir(parents=True, exist_ok=True) + + def _get_template_path(self, name: str) -> Path: + return self.config.templates_dir / f"{name}.yaml" + + def _get_pattern_path(self, name: str) -> Path: + return self.config.patterns_dir / f"{name}.yaml" + + def create_template( + self, + name: str, + pattern_file: str, + custom_template: str | None = None, + description: str = "", + ) -> None: + pattern_path = Path(pattern_file) + if not pattern_path.exists(): + raise FileNotFoundError(f"Pattern file not found: {pattern_file}") + + with open(pattern_path) as f: + patterns = yaml.safe_load(f) + + template_content = "" + if custom_template: + with open(custom_template) as f: + template_content = f.read() + else: + template_content = self._generate_default_template(patterns) + + template_data = { + "name": name, + "description": description, + "created": datetime.now().isoformat(), + "language": patterns.get("language", "unknown"), + "patterns": patterns, + "template": template_content, + } + + template_path = self._get_template_path(name) + with open(template_path, "w") as f: + yaml.dump(template_data, f, default_flow_style=False) + + def _generate_default_template(self, patterns: dict[str, Any]) -> str: + language = patterns.get("language", "python") + + if language in ["python"]: + return '''# Generated by PatternForge + # Based on analyzed patterns from {{ project_name }} + +{% if class_name %} +class {{ class_name }}: + """{{ class_docstring }}""" + + def __init__(self{% if init_params %}, {{ init_params }}{% endif %}): +{% for field in fields %} + self.{{ field }} = {{ field }} +{% endfor %} +{%- if methods %} +{% for method in methods %} + def {{ method.name }}(self{% if method.params %}, {{ method.params }}{% endif %}): + """{{ method.docstring }}""" + pass +{% endfor %} +{%- endif %} +{% elif function_name %} +def {{ function_name }}({% if params %}{{ params }}{% endif %}): + """{{ docstring }}""" + pass +{% else %} +# {{ entity_name }} - generated boilerplate +{% endif %} +''' + elif language in ["javascript", "typescript"]: + return '''// Generated by PatternForge +{% if class_name %} +export class {{ class_name }} { + {% for field in fields %} + private {{ field }}; + {% endfor %} + + constructor({% if params %}{{ params }}{% endif %}) { +{% for field in fields %} + this.{{ field }} = {{ field }}; +{% endfor %} + } +{% if methods %} +{% for method in methods %} + {{ method.name }}({% if method.params %}{{ method.params }}{% endif %}) { + // {{ method.docstring }} + } +{% endfor %} +{%- endif %} +} +{% elif function_name %} +export function {{ function_name }}({% if params %}{{ params }}{% endif %}) { + // {{ docstring }} +} +{% else %} +// {{ entity_name }} - generated boilerplate +{% endif %} +''' + else: + return '''// Generated by PatternForge +// {{ entity_name }} + +{% if class_name %} +class {{ class_name }} { +{% for field in fields %} + {{ field }}; +{% endfor %} + + constructor({% if params %}{{ params }}{% endif %}) { +{% for field in fields %} + this.{{ field }} = {{ field }}; +{% endfor %} + } +} +{% else %} +// {{ entity_name }} +{% endif %} +''' + + def list_templates(self) -> list[dict[str, Any]]: + templates: list[dict[str, Any]] = [] + if not self.config.templates_dir.exists(): + return templates + for f in self.config.templates_dir.glob("*.yaml"): + with open(f) as fp: + data = yaml.safe_load(fp) + if data: + templates.append({ + "name": data.get("name", f.stem), + "description": data.get("description", ""), + "created": data.get("created", ""), + "language": data.get("language", ""), + }) + return templates + + def get_template(self, name: str) -> dict[str, Any] | None: + path = self._get_template_path(name) + if not path.exists(): + return None + with open(path) as f: + return yaml.safe_load(f) + + def remove_template(self, name: str) -> bool: + path = self._get_template_path(name) + if path.exists(): + path.unlink() + return True + return False + + def render_template( + self, + name: str, + context: dict[str, Any], + ) -> str: + template_data = self.get_template(name) + if not template_data: + raise ValueError(f"Template not found: {name}") + + template_str = template_data.get("template", "") + try: + env = Environment( + loader=FileSystemLoader(str(self.config.templates_dir)), + trim_blocks=True, + lstrip_blocks=True, + ) + template = env.from_string(template_str) + return template.render(**context) + except TemplateSyntaxError as e: + raise ValueError(f"Template syntax error: {e}") + + def export_patterns( + self, + source: str, + destination: str, + format: str = "yaml", + ) -> None: + src = Path(source) + dst = Path(destination) + dst.parent.mkdir(parents=True, exist_ok=True) + + if src.is_file(): + with open(src) as f: + data = yaml.safe_load(f) + if format == "json": + with open(dst, "w") as f: + json.dump(data, f, indent=2) + else: + with open(dst, "w") as f: + yaml.dump(data, f, default_flow_style=False) + else: + for f in src.glob("*.yaml"): + with open(f) as fp: + data = yaml.safe_load(fp) + out_path = dst / f.name + if format == "json": + with open(out_path.with_suffix(".json"), "w") as fp: + json.dump(data, fp, indent=2) + else: + with open(out_path, "w") as fp: + yaml.dump(data, fp, default_flow_style=False) + + def import_patterns(self, source: str) -> None: + src = Path(source) + if src.is_file(): + name = src.stem + import_path = self._get_pattern_path(name) + import_path.parent.mkdir(parents=True, exist_ok=True) + import_path.write_bytes(src.read_bytes()) + else: + for f in src.glob("*.yaml"): + import_path = self._get_pattern_path(f.stem) + import_path.parent.mkdir(parents=True, exist_ok=True) + import_path.write_bytes(f.read_bytes())