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:00 +00:00
parent aa7d59a805
commit 08b59ff9a6

View File

@@ -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())