feat: add feature editing capability for pending/in-progress features

Add the ability for users to edit features that are not yet completed,
allowing them to provide corrections or additional instructions when the
agent is stuck or implementing a feature incorrectly.

Backend changes:
- Add FeatureUpdate schema in server/schemas.py with optional fields
- Add PATCH /api/projects/{project_name}/features/{feature_id} endpoint
- Validate that completed features (passes=True) cannot be edited

Frontend changes:
- Add FeatureUpdate type in ui/src/lib/types.ts
- Add updateFeature() API function in ui/src/lib/api.ts
- Add useUpdateFeature() React Query mutation hook
- Create EditFeatureForm.tsx component with pre-filled form values
- Update FeatureModal.tsx with Edit button for non-completed features

The edit form allows modifying category, name, description, priority,
and test steps. Save button is disabled until changes are detected.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-14 14:54:53 +02:00
parent 07c2010d32
commit d1b8eb5f99
8 changed files with 373 additions and 4 deletions

View File

@@ -17,6 +17,7 @@ from ..schemas import (
FeatureCreate,
FeatureListResponse,
FeatureResponse,
FeatureUpdate,
)
from ..utils.validation import validate_project_name
@@ -217,6 +218,63 @@ async def get_feature(project_name: str, feature_id: int):
raise HTTPException(status_code=500, detail="Database error occurred")
@router.patch("/{feature_id}", response_model=FeatureResponse)
async def update_feature(project_name: str, feature_id: int, update: FeatureUpdate):
"""
Update a feature's details.
Only features that are not yet completed (passes=False) can be edited.
This allows users to provide corrections or additional instructions
when the agent is stuck or implementing a feature incorrectly.
"""
project_name = validate_project_name(project_name)
project_dir = _get_project_path(project_name)
if not project_dir:
raise HTTPException(status_code=404, detail=f"Project '{project_name}' not found in registry")
if not project_dir.exists():
raise HTTPException(status_code=404, detail="Project directory not found")
_, Feature = _get_db_classes()
try:
with get_db_session(project_dir) as session:
feature = session.query(Feature).filter(Feature.id == feature_id).first()
if not feature:
raise HTTPException(status_code=404, detail=f"Feature {feature_id} not found")
# Prevent editing completed features
if feature.passes:
raise HTTPException(
status_code=400,
detail="Cannot edit a completed feature. Features marked as done are immutable."
)
# Apply updates for non-None fields
if update.category is not None:
feature.category = update.category
if update.name is not None:
feature.name = update.name
if update.description is not None:
feature.description = update.description
if update.steps is not None:
feature.steps = update.steps
if update.priority is not None:
feature.priority = update.priority
session.commit()
session.refresh(feature)
return feature_to_response(feature)
except HTTPException:
raise
except Exception:
logger.exception("Failed to update feature")
raise HTTPException(status_code=500, detail="Failed to update feature")
@router.delete("/{feature_id}")
async def delete_feature(project_name: str, feature_id: int):
"""Delete a feature."""

View File

@@ -87,6 +87,15 @@ class FeatureCreate(FeatureBase):
priority: int | None = None
class FeatureUpdate(BaseModel):
"""Request schema for updating a feature (partial updates allowed)."""
category: str | None = None
name: str | None = None
description: str | None = None
steps: list[str] | None = None
priority: int | None = None
class FeatureResponse(FeatureBase):
"""Response schema for a feature."""
id: int