Skip to content

Commit ff90825

Browse files
committed
refactor: simplify code and improve type safety across multiple modules
The changes include: - Simplified issue conversion in AI linting fixer - Fixed key access in specialist manager using lambda function - Added utility methods to BaseGenerator for templates and JSON handling - Updated DeploymentGenerator to use consistent return types - Improved type safety in DockerGenerator and Phase1ExtensionImplementor - Added duration formatting helper for implementation roadmap - Fixed various typing issues and added missing type annotations - Applied consistent code formatting across multiple files
1 parent 7b6df4e commit ff90825

33 files changed

Lines changed: 477 additions & 215 deletions

engine/codeflow_engine/actions/ai_linting_fixer/issue_converter.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,17 @@ def convert_detection_issue_to_model_issue(detection_issue: "LintingIssue") -> "
1919
from codeflow_engine.actions.ai_linting_fixer.models import \
2020
LintingIssue as ModelLintingIssue
2121

22-
# Build a candidate dict with superset keys
23-
candidate = {
24-
"file_path": detection_issue.file_path,
25-
"line_number": getattr(detection_issue, "line_number", None),
26-
"error_code": detection_issue.error_code,
27-
"message": detection_issue.message,
28-
"line_content": getattr(detection_issue, "line_content", ""),
29-
# Prefer column_number; also support models that use "column"
30-
"column_number": getattr(detection_issue, "column_number", None),
31-
"column": getattr(detection_issue, "column_number", None),
32-
}
33-
# Filter by the target model's constructor fields
34-
allowed = set(getattr(ModelLintingIssue, "__annotations__", {}).keys() or [])
35-
payload = {k: v for k, v in candidate.items() if k in allowed and v is not None}
36-
return ModelLintingIssue(**payload)
22+
line_number = getattr(detection_issue, "line_number", 0)
23+
column_number = getattr(detection_issue, "column_number", 0)
24+
return ModelLintingIssue(
25+
file_path=detection_issue.file_path,
26+
line_number=line_number,
27+
column_number=column_number,
28+
error_code=detection_issue.error_code,
29+
message=detection_issue.message,
30+
line_content=getattr(detection_issue, "line_content", ""),
31+
column=column_number,
32+
)
3733

3834

3935
def convert_detection_issues_to_model_issues(

engine/codeflow_engine/actions/ai_linting_fixer/specialists/specialist_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def get_specialist_for_issues(self, issues: list[LintingIssue]) -> BaseSpecialis
5757
scores[agent_type] = score
5858

5959
# Select the specialist with the highest score
60-
best_agent_type = max(scores, key=scores.get)
60+
best_agent_type = max(scores, key=lambda agent_type: scores[agent_type])
6161
return self.specialists[best_agent_type]
6262

6363
def get_specialist_by_type(self, agent_type: AgentType) -> BaseSpecialist:

engine/codeflow_engine/actions/prototype_enhancement/generators/base_generator.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
Provides the BaseGenerator class that all specialized generators inherit from.
55
"""
66

7-
from dataclasses import asdict
87
from abc import ABC, abstractmethod
8+
from dataclasses import asdict
9+
import json
910
from pathlib import Path
1011
from typing import TYPE_CHECKING, Any, TypeVar
1112

@@ -80,6 +81,34 @@ def _render_template(
8081
"""
8182
return self.template_manager.render(template_key, variables, variants)
8283

84+
def _template_exists(self, template_key: str) -> bool:
85+
"""Check whether a template key exists in the registry."""
86+
registry = getattr(self.template_manager, "template_registry", None)
87+
if registry is None:
88+
return False
89+
get_metadata = getattr(registry, "get_metadata", None)
90+
if callable(get_metadata):
91+
return get_metadata(template_key) is not None
92+
return False
93+
94+
def _read_json_file(self, file_path: str) -> dict[str, Any] | None:
95+
"""Read a JSON file into a dictionary."""
96+
path = Path(file_path)
97+
if not path.exists():
98+
return None
99+
try:
100+
with path.open(encoding="utf-8") as file:
101+
data = json.load(file)
102+
except (OSError, json.JSONDecodeError):
103+
return None
104+
return data if isinstance(data, dict) else None
105+
106+
def _write_json_file(self, file_path: str, data: dict[str, Any]) -> None:
107+
"""Write a dictionary to a JSON file."""
108+
path = Path(file_path)
109+
path.parent.mkdir(parents=True, exist_ok=True)
110+
path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
111+
83112
def _get_platform_variables(self) -> dict[str, Any]:
84113
"""Get platform-specific variables for template rendering.
85114

engine/codeflow_engine/actions/prototype_enhancement/generators/deployment_generator.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77

88
import logging
99
from pathlib import Path
10+
from typing import TYPE_CHECKING
1011
from typing import Any
1112

1213
from codeflow_engine.actions.prototype_enhancement.generators.base_generator import BaseGenerator
1314

15+
if TYPE_CHECKING:
16+
from codeflow_engine.actions.prototype_enhancement.generators.template_utils import (
17+
TemplateManager,
18+
)
19+
1420

1521
logger = logging.getLogger(__name__)
1622

@@ -20,98 +26,97 @@ class DeploymentGenerator(BaseGenerator):
2026
Generates deployment configuration files for various platforms.
2127
"""
2228

23-
def __init__(self, template_dir: Path | None = None):
29+
def __init__(
30+
self,
31+
template_manager: "TemplateManager",
32+
**kwargs: Any,
33+
) -> None:
2434
"""
2535
Initialize the deployment generator.
2636
2737
Args:
28-
template_dir: Optional directory containing custom templates
38+
template_manager: Template manager used for rendering deployment templates
2939
"""
30-
super().__init__(template_dir)
40+
super().__init__(template_manager, kwargs.get("platform_config"))
3141
self.template_name = "deployment"
3242

33-
def generate(
34-
self,
35-
platform: str,
36-
output_dir: Path,
37-
context: dict[str, Any] | None = None,
38-
) -> list[Path]:
43+
def generate(self, output_dir: str, **kwargs: Any) -> list[str]:
3944
"""
4045
Generate deployment configuration files.
4146
4247
Args:
43-
platform: Target platform (e.g., 'replit', 'vercel', 'render')
4448
output_dir: Directory to write generated files to
45-
context: Additional context for template rendering
49+
**kwargs: Additional generation options including platform and context
4650
4751
Returns:
48-
List of Path objects to generated files
52+
List of generated file paths
4953
"""
50-
if context is None:
51-
context = {}
54+
platform = str(kwargs.get("platform", ""))
55+
context = dict(kwargs.get("context") or {})
56+
output_path = Path(output_dir)
5257

5358
# Add platform-specific context
5459
context["platform"] = platform
5560

5661
# Generate platform-specific deployment files
5762
if platform == "replit":
58-
return self._generate_replit_deployment(output_dir, context)
63+
return self._generate_replit_deployment(output_path, context)
5964
if platform == "vercel":
60-
return self._generate_vercel_deployment(output_dir, context)
65+
return self._generate_vercel_deployment(output_path, context)
6166
if platform == "render":
62-
return self._generate_render_deployment(output_dir, context)
67+
return self._generate_render_deployment(output_path, context)
6368
logger.warning(f"Unsupported deployment platform: {platform}")
6469
return []
6570

6671
def _generate_replit_deployment(
6772
self, output_dir: Path, context: dict[str, Any]
68-
) -> list[Path]:
73+
) -> list[str]:
6974
"""Generate Replit-specific deployment files."""
70-
generated_files = []
75+
generated_files: list[str] = []
7176

7277
# Generate .replit file
7378
replit_config = self._render_template("replit_config.toml", context)
7479
if replit_config:
7580
replit_path = output_dir / ".replit"
76-
replit_path.write_text(replit_config)
77-
generated_files.append(replit_path)
81+
replit_path.write_text(replit_config, encoding="utf-8")
82+
generated_files.append(str(replit_path))
7883

7984
# Generate replit.nix if needed
8085
if context.get("needs_custom_environment"):
8186
replit_nix = self._render_template("replit.nix", context)
8287
if replit_nix:
8388
nix_path = output_dir / "replit.nix"
84-
nix_path.write_text(replit_nix)
85-
generated_files.append(nix_path)
89+
nix_path.write_text(replit_nix, encoding="utf-8")
90+
generated_files.append(str(nix_path))
8691

8792
return generated_files
8893

8994
def _generate_vercel_deployment(
9095
self, output_dir: Path, context: dict[str, Any]
91-
) -> list[Path]:
96+
) -> list[str]:
9297
"""Generate Vercel deployment configuration."""
93-
generated_files = []
98+
generated_files: list[str] = []
9499

95100
# Generate vercel.json
96101
vercel_config = self._render_template("vercel.json", context)
97102
if vercel_config:
98103
vercel_path = output_dir / "vercel.json"
99-
vercel_path.write_text(vercel_config)
100-
generated_files.append(vercel_path)
104+
vercel_path.write_text(vercel_config, encoding="utf-8")
105+
generated_files.append(str(vercel_path))
101106

102107
return generated_files
103108

104109
def _generate_render_deployment(
105110
self, output_dir: Path, context: dict[str, Any]
106-
) -> list[Path]:
111+
) -> list[str]:
107112
"""Generate Render deployment configuration."""
108-
generated_files = []
113+
generated_files: list[str] = []
109114

110115
# Generate render.yaml
111116
render_config = self._render_template("render.yaml", context)
112117
if render_config:
113118
render_path = output_dir / "render.yaml"
114-
render_path.write_text(render_config)
115-
generated_files.append(render_path)
119+
render_path.write_text(render_config, encoding="utf-8")
120+
generated_files.append(str(render_path))
116121

117122
return generated_files

engine/codeflow_engine/actions/prototype_enhancement/generators/docker_generator.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,11 @@ def _get_docker_compose_services(
154154
self, language: str, framework: str, database: str, environment: str
155155
) -> dict[str, Any]:
156156
"""Get services configuration for docker-compose."""
157-
services = {}
157+
_ = framework
158+
services: dict[str, Any] = {}
158159

159160
# Main application service
160-
services["app"] = {
161+
app_service: dict[str, Any] = {
161162
"build": ".",
162163
"container_name": f"app-{environment}",
163164
"restart": "unless-stopped",
@@ -166,26 +167,27 @@ def _get_docker_compose_services(
166167
"volumes": ["./:/app"] if environment == "dev" else None,
167168
"depends_on": [],
168169
}
170+
services["app"] = app_service
169171

170172
# Database service if specified
171173
if database:
172174
db_service = self._get_database_service(database, environment)
173175
if db_service:
174176
services["db"] = db_service
175-
services["app"]["depends_on"].append("db")
177+
app_service["depends_on"].append("db")
176178

177179
# Additional services based on language/framework
178180
if language == "typescript" and environment == "dev":
179-
services["app"]["volumes"] = ["./:/app", "/app/node_modules"]
180-
services["app"]["command"] = "npm run dev"
181+
app_service["volumes"] = ["./:/app", "/app/node_modules"]
182+
app_service["command"] = "npm run dev"
181183

182184
return services
183185

184186
def _get_database_service(
185187
self, database: str, environment: str
186188
) -> dict[str, Any] | None:
187189
"""Get database service configuration."""
188-
db_configs = {
190+
db_configs: dict[str, dict[str, Any]] = {
189191
"postgresql": {
190192
"image": "postgres:13-alpine",
191193
"container_name": "db",

engine/codeflow_engine/ai/implementation_roadmap/implementor.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
logger = logging.getLogger(__name__)
1919

2020

21+
def _format_phase_duration(duration_days: int) -> str:
22+
"""Format phase duration for report/display payloads."""
23+
return f"{duration_days} days"
24+
25+
2126
class Phase1ExtensionImplementor:
2227
"""
2328
Main orchestrator for Phase 1 extension implementation
@@ -186,7 +191,7 @@ def list_available_tasks(self, phase: str | None = None) -> list[dict[str, Any]]
186191
return []
187192
task_names = phase_obj.tasks
188193
else:
189-
task_names = self.task_registry.get_all_task_names()
194+
task_names = list(self.task_registry.get_all_tasks())
190195

191196
tasks = []
192197
for task_name in task_names:
@@ -198,7 +203,7 @@ def list_available_tasks(self, phase: str | None = None) -> list[dict[str, Any]]
198203
"description": task.description,
199204
"category": task.category,
200205
"complexity": task.complexity,
201-
"estimated_time": task.estimated_time,
206+
"estimated_time": task.estimated_hours,
202207
"dependencies": task.dependencies,
203208
"packages_required": task.packages_required,
204209
}
@@ -220,11 +225,11 @@ def list_available_phases(self) -> list[dict[str, Any]]:
220225
phases.append(
221226
{
222227
"name": phase_name,
223-
"description": phase.description,
228+
"description": phase.display_name,
224229
"task_count": len(phase.tasks),
225-
"estimated_time": phase.estimated_time,
230+
"estimated_time": _format_phase_duration(phase.duration_days),
226231
"success_criteria": phase.success_criteria,
227-
"dependencies": phase.dependencies,
232+
"dependencies": phase.depends_on,
228233
}
229234
)
230235

@@ -252,7 +257,7 @@ def get_task_details(self, task_name: str) -> dict[str, Any] | None:
252257
"description": task.description,
253258
"category": task.category,
254259
"complexity": task.complexity,
255-
"estimated_time": task.estimated_time,
260+
"estimated_time": task.estimated_hours,
256261
"dependencies": task.dependencies,
257262
"packages_required": task.packages_required,
258263
"files_created": task.files_created,
@@ -356,7 +361,7 @@ def validate_environment(self) -> dict[str, Any]:
356361
Returns:
357362
Dictionary containing validation results
358363
"""
359-
validation_results = {
364+
validation_results: dict[str, Any] = {
360365
"valid": True,
361366
"issues": [],
362367
"warnings": [],
@@ -416,14 +421,14 @@ def export_configuration(self) -> dict[str, Any]:
416421
return {
417422
"export_timestamp": datetime.now().isoformat(),
418423
"task_registry": {
419-
"total_tasks": len(self.task_registry.get_all_task_names()),
424+
"total_tasks": len(self.task_registry.get_all_tasks()),
420425
"tasks_by_category": self.report_generator._analyze_task_distribution(),
421426
},
422427
"phase_configuration": {
423428
phase_name: {
424429
"task_count": len(phase.tasks),
425-
"estimated_time": phase.estimated_time,
426-
"dependencies": phase.dependencies,
430+
"estimated_time": _format_phase_duration(phase.duration_days),
431+
"dependencies": phase.depends_on,
427432
}
428433
for phase_name in ["immediate", "medium", "strategic"]
429434
for phase in [self.phases.get_phase(phase_name)]

0 commit comments

Comments
 (0)