Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
426 changes: 426 additions & 0 deletions apps/backend/cli/spec_commands.py

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions apps/backend/spec/templates/builtin/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def __init__(self):
"description": "Support multi-factor authentication",
},
},
placeholders=["PROJECT_NAME", "ORGANIZATION"],
)

def generate(self, params: dict[str, Any]) -> dict[str, Any]:
Expand All @@ -64,8 +65,8 @@ def generate(self, params: dict[str, Any]) -> dict[str, Any]:

return {
"title": "User Authentication",
"description": f"Secure user authentication system using {auth_method.upper()} with support for {providers_str}.",
"rationale": "Provide secure, user-friendly authentication that protects user accounts while enabling easy access to the application.",
"description": f"Secure user authentication system for {{{{PROJECT_NAME}}}} using {auth_method.upper()} with support for {providers_str}.",
"rationale": "Provide secure, user-friendly authentication for {{{{ORGANIZATION}}}} that protects user accounts while enabling easy access to the application.",
"user_stories": [
"As a user, I want to sign up for an account",
"As a user, I want to log in securely",
Expand Down
5 changes: 3 additions & 2 deletions apps/backend/spec/templates/builtin/crud_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def __init__(self):
"description": "Whether list endpoint should support pagination",
},
},
placeholders=["PROJECT_NAME"],
)

def generate(self, params: dict[str, Any]) -> dict[str, Any]:
Expand All @@ -62,8 +63,8 @@ def generate(self, params: dict[str, Any]) -> dict[str, Any]:

return {
"title": f"{resource} CRUD API",
"description": f"RESTful API endpoints for managing {resource_plural} with full CRUD operations.",
"rationale": f"Provide a standard interface for creating, reading, updating, and deleting {resource_plural}. This enables frontend applications and third-party integrations to manage {resource_plural} programmatically.",
"description": f"RESTful API endpoints for {{{{PROJECT_NAME}}}} to manage {resource_plural} with full CRUD operations.",
"rationale": f"Provide a standard interface for creating, reading, updating, and deleting {resource_plural} in {{{{PROJECT_NAME}}}}. This enables frontend applications and third-party integrations to manage {resource_plural} programmatically.",
"user_stories": [
f"As a developer, I want to create new {resource_plural} via API",
f"As a developer, I want to retrieve {resource_plural} with filtering and search",
Expand Down
41 changes: 41 additions & 0 deletions apps/backend/spec/templates/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pathlib import Path
from typing import Any

from .placeholders import PlaceholderParser
from .registry import Template


Expand All @@ -24,6 +25,7 @@ def __init__(self, template: Template):
template: Template to use for generation
"""
self.template = template
self.placeholder_parser = PlaceholderParser()

def generate_spec(
self, params: dict[str, Any], spec_dir: Path | None = None
Expand All @@ -46,6 +48,9 @@ def generate_spec(
# Generate spec content from template
spec_content = self.template.generate(params)

# Replace placeholders in spec content
spec_content = self._replace_placeholders_in_content(spec_content, params)

# Add metadata
spec_content["metadata"] = {
"template": self.template.name,
Expand Down Expand Up @@ -172,3 +177,39 @@ def validate_generated_spec(self, spec_content: dict[str, Any]) -> list[str]:
errors.append("acceptance_criteria cannot be empty")

return errors

def _replace_placeholders_in_content(
self, content: Any, values: dict[str, Any]
) -> Any:
"""
Recursively replace placeholders in spec content.

Args:
content: Content to process (can be dict, list, str, or other)
values: Dictionary of placeholder values

Returns:
Content with placeholders replaced
"""
if isinstance(content, str):
# Replace placeholders in string
try:
return self.placeholder_parser.replace(content, values)
except ValueError:
# If placeholder replacement fails, return original string
# This allows templates to have optional placeholders
return content
elif isinstance(content, dict):
# Recursively process dictionary values
return {
key: self._replace_placeholders_in_content(value, values)
for key, value in content.items()
}
elif isinstance(content, list):
# Recursively process list items
return [
self._replace_placeholders_in_content(item, values) for item in content
]
else:
# Return other types unchanged
return content
Loading
Loading