Skip to content
Merged
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
60 changes: 56 additions & 4 deletions codeframe/agents/backend_worker_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,11 @@ async def generate_code(self, context: Dict[str, Any]) -> Dict[str, Any]:

return result

def apply_file_changes(self, files: List[Dict[str, Any]]) -> List[str]:
def apply_file_changes(
self,
files: List[Dict[str, Any]],
intervention_context: Optional[Dict[str, Any]] = None,
) -> List[str]:
"""
Apply file changes to disk.

Expand All @@ -378,8 +382,13 @@ def apply_file_changes(self, files: List[Dict[str, Any]]) -> List[str]:
When not using SDK, safely writes, modifies, or deletes files with security
validation and atomic operations.

If intervention_context is provided (from supervisor intervention), the
method will convert "create" actions to "modify" for files that already
exist, preventing FileExistsError on retry.

Args:
files: List of file change dictionaries from generate_code()
intervention_context: Optional intervention context from supervisor

Returns:
List of modified file paths
Expand All @@ -392,6 +401,16 @@ def apply_file_changes(self, files: List[Dict[str, Any]]) -> List[str]:

modified_paths = []

# Extract existing files from intervention context if available
existing_files = []
if intervention_context and intervention_context.get("intervention_applied"):
existing_files = intervention_context.get("existing_files", [])
logger.info(
f"[DIAG] Intervention context active: "
f"strategy={intervention_context.get('strategy')}, "
f"existing_files={len(existing_files)}"
)

for file_spec in files:
path = file_spec["path"]
action = file_spec["action"]
Expand All @@ -411,12 +430,40 @@ def apply_file_changes(self, files: List[Dict[str, Any]]) -> List[str]:
raise ValueError(f"Path traversal detected: {path}")

if self.use_sdk:
# SDK mode: Files already written by SDK Write tool
# Just validate and track paths
# SDK mode: Files already written by SDK Write tool.
# Note: Intervention context is NOT applied in SDK mode because
# file operations are handled externally by the SDK. If file
# conflicts occur in SDK mode, they must be resolved at the
# SDK/tool level rather than through tactical pattern intervention.
logger.info(f"SDK handled {action} for: {path}")
modified_paths.append(path)
else:
# Non-SDK mode: Perform file operations directly

# Handle intervention: convert create to modify for existing files
if action == "create" and target_path.exists():
# Check if intervention says to convert
if intervention_context and intervention_context.get("intervention_applied"):
strategy = intervention_context.get("strategy", "")
if strategy == "convert_create_to_edit":
action = "modify"
logger.info(
f"[DIAG] Converted 'create' to 'modify' for existing file: {path}"
)
elif strategy == "skip_file_creation":
logger.info(
f"[DIAG] Skipping creation of existing file: {path}"
)
modified_paths.append(path)
continue
else:
# No intervention context - let LeadAgent handle via
# tactical pattern intervention on retry
raise FileExistsError(
f"File already exists: {path}. "
"Cannot create over existing file without intervention context."
)

if action == "create" or action == "modify":
if action == "modify" and not target_path.exists():
raise FileNotFoundError(f"Cannot modify non-existent file: {path}")
Expand Down Expand Up @@ -1016,7 +1063,12 @@ async def execute_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
generation_result = await self.generate_code(context)

# 4. Apply file changes
files_modified = self.apply_file_changes(generation_result["files"])
# Check for intervention context from supervisor
intervention_context = task.get("intervention_context")
files_modified = self.apply_file_changes(
generation_result["files"],
intervention_context=intervention_context,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# 4.5. Run linting on modified files (Sprint 9 Phase 5: T111)
await self._run_and_check_linting(task, files_modified)
Expand Down
49 changes: 43 additions & 6 deletions codeframe/agents/frontend_worker_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,13 @@ async def execute_task(self, task: Dict[str, Any], project_id: int = 1) -> Dict[
types_code = None

# Create files in correct location
# Check for intervention context from supervisor
intervention_context = task.get("intervention_context")
file_paths = self._create_component_files(
component_spec["name"], component_code, types_code
component_spec["name"],
component_code,
types_code,
intervention_context=intervention_context,
)

# Update imports/exports
Expand Down Expand Up @@ -375,7 +380,11 @@ def _generate_typescript_types(self, spec: Dict[str, Any]) -> Optional[str]:
return None

def _create_component_files(
self, component_name: str, component_code: str, types_code: Optional[str] = None
self,
component_name: str,
component_code: str,
types_code: Optional[str] = None,
intervention_context: Optional[Dict[str, Any]] = None,
) -> Dict[str, str]:
"""
Create component files in correct directory structure.
Expand All @@ -384,6 +393,7 @@ def _create_component_files(
component_name: Name of component
component_code: Component source code
types_code: Optional TypeScript types
intervention_context: Optional intervention context from supervisor

Returns:
Dict of created file paths
Expand All @@ -396,10 +406,37 @@ def _create_component_files(

# Check for conflicts
if component_file.exists():
raise FileExistsError(
f"Component file already exists: {component_file}. "
"Please choose a different name or delete the existing file."
)
# Check if intervention context allows handling conflict
if intervention_context and intervention_context.get("intervention_applied"):
strategy = intervention_context.get("strategy", "")

if strategy == "convert_create_to_edit":
logger.info(
f"[DIAG] Intervention: Overwriting existing component file: {component_file}"
)
# Fall through to write the file (overwrite)
elif strategy == "skip_file_creation":
logger.info(
f"[DIAG] Intervention: Skipping existing component file: {component_file}"
)
# Return paths without writing
try:
relative_path = component_file.relative_to(self.project_root)
except ValueError:
relative_path = component_file.relative_to(self.web_ui_root.parent)
return {"component": str(relative_path)}
else:
# Unknown strategy, raise the original error
raise FileExistsError(
f"Component file already exists: {component_file}. "
"Please choose a different name or delete the existing file."
)
else:
# No intervention context - raise original error
raise FileExistsError(
f"Component file already exists: {component_file}. "
"Please choose a different name or delete the existing file."
)

# Write component file
component_file.write_text(component_code, encoding="utf-8")
Expand Down
Loading
Loading