Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
72 changes: 41 additions & 31 deletions apps/backend/prompts/spec_quick.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,46 +31,46 @@ That's it. No deep analysis needed.

## PHASE 2: CREATE MINIMAL SPEC

Create a concise `spec.md`:
Create a concise `spec.md` that includes the required validator sections:

```bash
cat > spec.md << 'EOF'
# Quick Spec: [Task Name]
# Specification: [Task Name]

## Task
[One sentence description]
## Overview
[One paragraph describing what is being changed and why]

## Files to Modify
## Workflow Type
**Type**: simple

## Task Scope
### Files to Modify
- `[path/to/file]` - [what to change]

## Change Details
### Change Details
[Brief description of the change - a few sentences max]

## Verification
## Success Criteria
- [ ] [How to verify the change works]

## Notes
[Any gotchas or considerations - optional]
EOF
```

**Keep it short!** A simple spec should be 20-50 lines, not 200+.
**Keep it short!** A simple spec should be concise and focused.

---

## PHASE 3: CREATE SIMPLE PLAN

Create `implementation_plan.json`:
Create `implementation_plan.json` with current schema fields:

```bash
cat > implementation_plan.json << 'EOF'
{
"spec_name": "[spec-name]",
"feature": "[task description]",
"workflow_type": "simple",
"total_phases": 1,
"recommended_workers": 1,
"phases": [
{
"id": "phase-1",
"phase": 1,
"name": "Implementation",
"description": "[task description]",
Expand All @@ -86,17 +86,19 @@ cat > implementation_plan.json << 'EOF'
"patterns_from": [],
"verification": {
"type": "manual",
"run": "[verification step]"
"instructions": "[verification step]"
}
}
]
}
],
"metadata": {
"created_at": "[timestamp]",
"complexity": "simple",
"estimated_sessions": 1
}
"summary": {
"total_phases": 1,
"total_subtasks": 1,
"recommended_workers": 1
},
"created_at": "[timestamp]",
"updated_at": "[timestamp]"
}
EOF
```
Expand Down Expand Up @@ -146,18 +148,22 @@ Ready for implementation.

**spec.md**:
```markdown
# Quick Spec: Button Color Change
# Specification: Button Color Change

## Task
## Overview
Update primary button color from blue (#3B82F6) to green (#22C55E).

## Files to Modify
## Workflow Type
**Type**: simple

## Task Scope
### Files to Modify
- `src/components/Button.tsx` - Update color constant

## Change Details
### Change Details
Change the `primaryColor` variable from `#3B82F6` to `#22C55E`.

## Verification
## Success Criteria
- [ ] Buttons appear green in the UI
- [ ] No console errors
```
Expand All @@ -168,18 +174,22 @@ Change the `primaryColor` variable from `#3B82F6` to `#22C55E`.

**spec.md**:
```markdown
# Quick Spec: Fix Welcome Typo
# Specification: Fix Welcome Typo

## Task
## Overview
Correct spelling of "recieve" to "receive" in welcome message.

## Files to Modify
## Workflow Type
**Type**: simple

## Task Scope
### Files to Modify
- `src/pages/Home.tsx` - Fix typo on line 42

## Change Details
### Change Details
Find "You will recieve" and change to "You will receive".

## Verification
## Success Criteria
- [ ] Welcome message displays correctly
```

Expand Down
52 changes: 45 additions & 7 deletions apps/backend/spec/phases/spec_phases.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,16 @@ async def phase_quick_spec(self) -> PhaseResult:
plan_file = self.spec_dir / "implementation_plan.json"

if spec_file.exists() and plan_file.exists():
self.ui.print_status("Quick spec already exists", "success")
return PhaseResult(
"quick_spec", True, [str(spec_file), str(plan_file)], [], 0
spec_valid = self.spec_validator.validate_spec_document().valid
plan_valid = self.spec_validator.validate_implementation_plan().valid
if spec_valid and plan_valid:
self.ui.print_status("Quick spec already exists", "success")
return PhaseResult(
"quick_spec", True, [str(spec_file), str(plan_file)], [], 0
)

self.ui.print_status(
"Quick spec files exist but are invalid, regenerating...", "warning"
)

is_greenfield = self._check_and_log_greenfield()
Expand Down Expand Up @@ -91,15 +98,46 @@ async def phase_quick_spec(self) -> PhaseResult:
phase_name="quick_spec",
)

if success and spec_file.exists():
if success:
# Create minimal plan if agent didn't
if not plan_file.exists():
writer.create_minimal_plan(self.spec_dir, self.task_description)

self.ui.print_status("Quick spec created", "success")
return PhaseResult(
"quick_spec", True, [str(spec_file), str(plan_file)], [], attempt
spec_valid = (
spec_file.exists()
and self.spec_validator.validate_spec_document().valid
)
plan_valid = (
plan_file.exists()
and self.spec_validator.validate_implementation_plan().valid
)

if not plan_valid and plan_file.exists():
from ..validate_pkg.auto_fix import auto_fix_plan

if auto_fix_plan(self.spec_dir):
plan_valid = (
self.spec_validator.validate_implementation_plan().valid
)

if spec_valid and plan_valid:
self.ui.print_status("Quick spec created", "success")
return PhaseResult(
"quick_spec",
True,
[str(spec_file), str(plan_file)],
[],
attempt,
)

errors.append(
f"Attempt {attempt + 1}: Quick spec output invalid "
f"(spec_valid={spec_valid}, plan_valid={plan_valid})"
)
self.ui.print_status(
"Quick spec created files but validation failed", "error"
)
continue

errors.append(f"Attempt {attempt + 1}: Quick spec agent failed")

Expand Down
76 changes: 73 additions & 3 deletions apps/backend/spec/pipeline/agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from pathlib import Path

from agents.tools_pkg.models import AGENT_CONFIGS

# Configure safe encoding before any output (fixes Windows encoding errors)
from ui.capabilities import configure_safe_encoding

Expand All @@ -29,6 +31,30 @@
class AgentRunner:
"""Manages agent execution with logging and error handling."""

PHASE_AGENT_MAP = {
"discovery": "spec_discovery",
"requirements": "spec_gatherer",
"historical_context": "spec_gatherer",
"research": "spec_researcher",
"context": "spec_context",
"spec_writing": "spec_writer",
"quick_spec": "spec_writer",
"self_critique": "spec_critic",
"planning": "planner",
"validation": "spec_validation",
"complexity_assessment": "spec_gatherer",
}

PROMPT_AGENT_MAP = {
"spec_researcher.md": "spec_researcher",
"spec_writer.md": "spec_writer",
"spec_quick.md": "spec_writer",
"spec_critic.md": "spec_critic",
"planner.md": "planner",
"validation_fixer.md": "spec_validation",
"complexity_assessor.md": "spec_gatherer",
}

def __init__(
self,
project_dir: Path,
Expand Down Expand Up @@ -57,6 +83,7 @@ async def run_agent(
thinking_budget: int | None = None,
thinking_level: str = "medium",
prior_phase_summaries: str | None = None,
phase_name: str | None = None,
) -> tuple[bool, str]:
"""Run an agent with the given prompt.

Expand All @@ -67,6 +94,7 @@ async def run_agent(
thinking_budget: Token budget for extended thinking (None = disabled)
thinking_level: Thinking level string (low, medium, high)
prior_phase_summaries: Summaries from previous phases for context
phase_name: Optional phase name used to select agent profile

Returns:
Tuple of (success, response_text)
Expand Down Expand Up @@ -131,6 +159,20 @@ async def run_agent(
resolve_model_id,
)

agent_type = self._resolve_agent_type(prompt_file, phase_name)
if agent_type not in AGENT_CONFIGS:
debug_error(
"agent_runner",
f"Unknown agent type resolved for prompt {prompt_file}",
phase_name=phase_name,
agent_type=agent_type,
)
return (
False,
f"Unknown spec agent type '{agent_type}' for prompt {prompt_file}"
+ (f" (phase: {phase_name})" if phase_name else ""),
)

betas = get_model_betas(self.model)
fast_mode = get_fast_mode(self.spec_dir)
debug(
Expand All @@ -146,6 +188,7 @@ async def run_agent(
self.project_dir,
self.spec_dir,
resolved_model,
agent_type=agent_type,
betas=betas,
fast_mode=fast_mode,
**thinking_kwargs,
Expand Down Expand Up @@ -255,14 +298,41 @@ async def run_agent(
return True, response_text

except Exception as e:
error_text = str(e)
detailed_error = (
f"{type(e).__name__}: {error_text}"
f" [prompt={prompt_file}]"
+ (f" [phase={phase_name}]" if phase_name else "")
)
debug_error(
"agent_runner",
f"Agent session error: {e}",
f"Agent session error: {detailed_error}",
exception_type=type(e).__name__,
)
if self.task_logger:
self.task_logger.log_error(f"Agent error: {e}", LogPhase.PLANNING)
return False, str(e)
self.task_logger.log_error(
f"Agent error ({prompt_file}{f'/{phase_name}' if phase_name else ''}): {detailed_error}",
LogPhase.PLANNING,
)
return False, detailed_error

@classmethod
def _resolve_agent_type(
cls,
prompt_file: str,
phase_name: str | None,
) -> str:
"""Resolve the spec pipeline agent type for the current prompt/phase."""
normalized_prompt = (prompt_file or "").strip()
normalized_phase = (phase_name or "").strip().lower()

if normalized_prompt in cls.PROMPT_AGENT_MAP:
return cls.PROMPT_AGENT_MAP[normalized_prompt]

if normalized_phase in cls.PHASE_AGENT_MAP:
return cls.PHASE_AGENT_MAP[normalized_phase]

return "spec_writer"

@staticmethod
def _extract_tool_input_display(inp: dict) -> str | None:
Expand Down
1 change: 1 addition & 0 deletions apps/backend/spec/pipeline/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ async def _run_agent(
thinking_budget=thinking_budget,
thinking_level=self.thinking_level,
prior_phase_summaries=prior_summaries if prior_summaries else None,
phase_name=phase_name,
)

async def _store_phase_summary(self, phase_name: str) -> None:
Expand Down
18 changes: 10 additions & 8 deletions apps/backend/spec/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@

def create_minimal_plan(spec_dir: Path, task_description: str) -> Path:
"""Create a minimal implementation plan for simple tasks."""
timestamp = datetime.now().isoformat()
plan = {
"spec_name": spec_dir.name,
"feature": task_description or spec_dir.name,
"workflow_type": "simple",
"total_phases": 1,
"recommended_workers": 1,
"phases": [
{
"id": "phase-1",
"phase": 1,
"name": "Implementation",
"description": task_description or "Simple implementation",
Expand All @@ -34,17 +34,19 @@ def create_minimal_plan(spec_dir: Path, task_description: str) -> Path:
"patterns_from": [],
"verification": {
"type": "manual",
"run": "Verify the change works as expected",
"instructions": "Verify the change works as expected",
},
}
],
}
],
"metadata": {
"created_at": datetime.now().isoformat(),
"complexity": "simple",
"estimated_sessions": 1,
"summary": {
"total_phases": 1,
"total_subtasks": 1,
"recommended_workers": 1,
},
"created_at": timestamp,
"updated_at": timestamp,
}

plan_file = spec_dir / "implementation_plan.json"
Expand Down
Loading
Loading