feat: Add YOLO mode for rapid prototyping without browser testing

Add a new YOLO (You Only Live Once) mode that skips all browser testing
and regression tests for faster feature iteration during prototyping.

Changes made:

**Core YOLO Mode Implementation:**
- Add --yolo CLI flag to autonomous_agent_demo.py
- Update agent.py to accept yolo_mode parameter and select appropriate prompt
- Modify client.py to conditionally include Playwright MCP server (excluded in YOLO mode)
- Add coding_prompt_yolo.template.md with static analysis only verification
- Add get_coding_prompt_yolo() to prompts.py

**Server/API Updates:**
- Add AgentStartRequest schema with yolo_mode field
- Update AgentStatus to include yolo_mode
- Modify process_manager.py to pass --yolo flag to subprocess
- Update agent router to accept yolo_mode in start request

**UI Updates:**
- Add YOLO toggle button (lightning bolt icon) in AgentControl
- Show YOLO mode indicator when agent is running in YOLO mode
- Add useAgentStatus hook to track current mode
- Update startAgent API to accept yoloMode parameter
- Add YOLO toggle in SpecCreationChat completion flow

**Spec Creation Improvements:**
- Fix create-spec.md to properly replace [FEATURE_COUNT] placeholder
- Add REQUIRED FEATURE COUNT section to initializer_prompt.template.md
- Fix spec_chat_session.py to create security settings file for Claude SDK
- Delete app_spec.txt before spec creation to allow fresh creation

**Documentation:**
- Add YOLO mode section to CLAUDE.md with usage examples
- Add checkpoint.md slash command for creating detailed commits

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-02 08:36:58 +02:00
parent 981d452134
commit 05607b310a
20 changed files with 592 additions and 76 deletions

View File

@@ -0,0 +1,40 @@
---
description: Create commit with detailed comment
---
Please create a comprehensive checkpoint commit with the following steps:
1. **Initialize Git if needed**: Run `git init` if git has not been instantiated for the project yet.
2. **Analyze all changes**:
- Run `git status` to see all tracked and untracked files
- Run `git diff` to see detailed changes in tracked files
- Run `git log -5 --oneline` to understand the commit message style of this repository
3. **Stage everything**:
- Add ALL tracked changes (modified and deleted files)
- Add ALL untracked files (new files)
- Use `git add -A` or `git add .` to stage everything
4. **Create a detailed commit message**:
- **First line**: Write a clear, concise summary (50-72 chars) describing the primary change
- Use imperative mood (e.g., "Add feature" not "Added feature")
- Examples: "feat: add user authentication", "fix: resolve database connection issue", "refactor: improve API route structure"
- **Body**: Provide a detailed description including:
- What changes were made (list of key modifications)
- Why these changes were made (purpose/motivation)
- Any important technical details or decisions
- Breaking changes or migration notes if applicable
- **Footer**: Include co-author attribution as shown in the Git Safety Protocol
5. **Execute the commit**: Create the commit with the properly formatted message following this repository's conventions.
IMPORTANT:
- Do NOT skip any files - include everything
- Make the commit message descriptive enough that someone reviewing the git log can understand what was accomplished
- Follow the project's existing commit message conventions (check git log first)
- Include the Claude Code co-author attribution in the commit message

View File

@@ -459,10 +459,19 @@ Create a new file using this XML structure:
If the output directory has an existing `initializer_prompt.md`, read it and update the feature count.
If not, copy from `.claude/templates/initializer_prompt.template.md` first, then update.
Update the feature count references to match the derived count from Phase 4L:
**CRITICAL: You MUST update the feature count placeholder:**
- Line containing "create ... test cases" - update to the derived feature count
- Line containing "Minimum ... features" - update to the derived feature count
1. Find the line containing `**[FEATURE_COUNT]**` in the "REQUIRED FEATURE COUNT" section
2. Replace `[FEATURE_COUNT]` with the exact number agreed upon in Phase 4L (e.g., `25`)
3. The result should read like: `You must create exactly **25** features using the...`
**Example edit:**
```
Before: **CRITICAL:** You must create exactly **[FEATURE_COUNT]** features using the `feature_create_bulk` tool.
After: **CRITICAL:** You must create exactly **25** features using the `feature_create_bulk` tool.
```
**Verify the update:** After editing, read the file again to confirm the feature count appears correctly. If `[FEATURE_COUNT]` still appears in the file, the update failed and you must try again.
**Note:** You do NOT need to update `coding_prompt.md` - the coding agent works through features one at a time regardless of total count.

View File

@@ -0,0 +1,274 @@
<!-- YOLO MODE PROMPT - Keep synchronized with coding_prompt.template.md -->
<!-- Last synced: 2026-01-01 -->
## YOLO MODE - Rapid Prototyping (Testing Disabled)
**WARNING:** This mode skips all browser testing and regression tests.
Features are marked as passing after lint/type-check succeeds.
Use for rapid prototyping only - not for production-quality development.
---
## YOUR ROLE - CODING AGENT (YOLO MODE)
You are continuing work on a long-running autonomous development task.
This is a FRESH context window - you have no memory of previous sessions.
### STEP 1: GET YOUR BEARINGS (MANDATORY)
Start by orienting yourself:
```bash
# 1. See your working directory
pwd
# 2. List files to understand project structure
ls -la
# 3. Read the project specification to understand what you're building
cat app_spec.txt
# 4. Read progress notes from previous sessions
cat claude-progress.txt
# 5. Check recent git history
git log --oneline -20
```
Then use MCP tools to check feature status:
```
# 6. Get progress statistics (passing/total counts)
Use the feature_get_stats tool
# 7. Get the next feature to work on
Use the feature_get_next tool
```
Understanding the `app_spec.txt` is critical - it contains the full requirements
for the application you're building.
### STEP 2: START SERVERS (IF NOT RUNNING)
If `init.sh` exists, run it:
```bash
chmod +x init.sh
./init.sh
```
Otherwise, start servers manually and document the process.
### STEP 3: CHOOSE ONE FEATURE TO IMPLEMENT
Get the next feature to implement:
```
# Get the highest-priority pending feature
Use the feature_get_next tool
```
Once you've retrieved the feature, **immediately mark it as in-progress**:
```
# Mark feature as in-progress to prevent other sessions from working on it
Use the feature_mark_in_progress tool with feature_id=42
```
Focus on completing one feature in this session before moving on to other features.
It's ok if you only complete one feature in this session, as there will be more sessions later that continue to make progress.
#### When to Skip a Feature (EXTREMELY RARE)
**Skipping should almost NEVER happen.** Only skip for truly external blockers you cannot control:
- **External API not configured**: Third-party service credentials missing (e.g., Stripe keys, OAuth secrets)
- **External service unavailable**: Dependency on service that's down or inaccessible
- **Environment limitation**: Hardware or system requirement you cannot fulfill
**NEVER skip because:**
| Situation | Wrong Action | Correct Action |
|-----------|--------------|----------------|
| "Page doesn't exist" | Skip | Create the page |
| "API endpoint missing" | Skip | Implement the endpoint |
| "Database table not ready" | Skip | Create the migration |
| "Component not built" | Skip | Build the component |
| "No data to test with" | Skip | Create test data or build data entry flow |
| "Feature X needs to be done first" | Skip | Build feature X as part of this feature |
If a feature requires building other functionality first, **build that functionality**. You are the coding agent - your job is to make the feature work, not to defer it.
If you must skip (truly external blocker only):
```
Use the feature_skip tool with feature_id={id}
```
Document the SPECIFIC external blocker in `claude-progress.txt`. "Functionality not built" is NEVER a valid reason.
### STEP 4: IMPLEMENT THE FEATURE
Implement the chosen feature thoroughly:
1. Write the code (frontend and/or backend as needed)
2. Ensure proper error handling
3. Follow existing code patterns in the codebase
### STEP 5: VERIFY WITH LINT AND TYPE CHECK (YOLO MODE)
**In YOLO mode, verification is done through static analysis only.**
Run the appropriate lint and type-check commands for your project:
**For TypeScript/JavaScript projects:**
```bash
npm run lint
npm run typecheck # or: npx tsc --noEmit
```
**For Python projects:**
```bash
ruff check .
mypy .
```
**If lint/type-check passes:** Proceed to mark the feature as passing.
**If lint/type-check fails:** Fix the errors before proceeding.
### STEP 6: UPDATE FEATURE STATUS
**YOU CAN ONLY MODIFY ONE FIELD: "passes"**
After lint/type-check passes, mark the feature as passing:
```
# Mark feature #42 as passing (replace 42 with the actual feature ID)
Use the feature_mark_passing tool with feature_id=42
```
**NEVER:**
- Delete features
- Edit feature descriptions
- Modify feature steps
- Combine or consolidate features
- Reorder features
### STEP 7: COMMIT YOUR PROGRESS
Make a descriptive git commit:
```bash
git add .
git commit -m "Implement [feature name] - YOLO mode
- Added [specific changes]
- Lint/type-check passing
- Marked feature #X as passing
"
```
### STEP 8: UPDATE PROGRESS NOTES
Update `claude-progress.txt` with:
- What you accomplished this session
- Which feature(s) you completed
- Any issues discovered or fixed
- What should be worked on next
- Current completion status (e.g., "45/200 features passing")
### STEP 9: END SESSION CLEANLY
Before context fills up:
1. Commit all working code
2. Update claude-progress.txt
3. Mark features as passing if lint/type-check verified
4. Ensure no uncommitted changes
5. Leave app in working state
---
## FEATURE TOOL USAGE RULES (CRITICAL - DO NOT VIOLATE)
The feature tools exist to reduce token usage. **DO NOT make exploratory queries.**
### ALLOWED Feature Tools (ONLY these):
```
# 1. Get progress stats (passing/in_progress/total counts)
feature_get_stats
# 2. Get the NEXT feature to work on (one feature only)
feature_get_next
# 3. Mark a feature as in-progress (call immediately after feature_get_next)
feature_mark_in_progress with feature_id={id}
# 4. Mark a feature as passing (after lint/type-check succeeds)
feature_mark_passing with feature_id={id}
# 5. Skip a feature (moves to end of queue) - ONLY when blocked by dependency
feature_skip with feature_id={id}
# 6. Clear in-progress status (when abandoning a feature)
feature_clear_in_progress with feature_id={id}
```
### RULES:
- Do NOT try to fetch lists of all features
- Do NOT query features by category
- Do NOT list all pending features
**You do NOT need to see all features.** The feature_get_next tool tells you exactly what to work on. Trust it.
---
## EMAIL INTEGRATION (DEVELOPMENT MODE)
When building applications that require email functionality (password resets, email verification, notifications, etc.), you typically won't have access to a real email service or the ability to read email inboxes.
**Solution:** Configure the application to log emails to the terminal instead of sending them.
- Password reset links should be printed to the console
- Email verification links should be printed to the console
- Any notification content should be logged to the terminal
**During testing:**
1. Trigger the email action (e.g., click "Forgot Password")
2. Check the terminal/server logs for the generated link
3. Use that link directly to verify the functionality works
This allows you to fully test email-dependent flows without needing external email services.
---
## IMPORTANT REMINDERS (YOLO MODE)
**Your Goal:** Rapidly prototype the application with all features implemented
**This Session's Goal:** Complete at least one feature
**Quality Bar (YOLO Mode):**
- Code compiles without errors (lint/type-check passing)
- Follows existing code patterns
- Basic error handling in place
- Features are implemented according to spec
**Note:** Browser testing and regression testing are SKIPPED in YOLO mode.
Features may have bugs that would be caught by manual testing.
Use standard mode for production-quality verification.
**You have unlimited time.** Take as long as needed to implement features correctly.
The most important thing is that you leave the code base in a clean state before
terminating the session (Step 9).
---
Begin by running Step 1 (Get Your Bearings).

View File

@@ -9,6 +9,16 @@ Start by reading `app_spec.txt` in your working directory. This file contains
the complete specification for what you need to build. Read it carefully
before proceeding.
---
## REQUIRED FEATURE COUNT
**CRITICAL:** You must create exactly **[FEATURE_COUNT]** features using the `feature_create_bulk` tool.
This number was determined during spec creation and must be followed precisely. Do not create more or fewer features than specified.
---
### CRITICAL FIRST TASK: Create Features
Based on `app_spec.txt`, create features using the feature_create_bulk tool. The features are stored in a SQLite database,

View File

@@ -42,8 +42,35 @@ python start.py
# Run agent directly for a project (use absolute path or registered name)
python autonomous_agent_demo.py --project-dir C:/Projects/my-app
python autonomous_agent_demo.py --project-dir my-app # if registered
# YOLO mode: rapid prototyping without browser testing
python autonomous_agent_demo.py --project-dir my-app --yolo
```
### YOLO Mode (Rapid Prototyping)
YOLO mode skips all testing for faster feature iteration:
```bash
# CLI
python autonomous_agent_demo.py --project-dir my-app --yolo
# UI: Toggle the lightning bolt button before starting the agent
```
**What's different in YOLO mode:**
- No regression testing (skips `feature_get_for_regression`)
- No Playwright MCP server (browser automation disabled)
- Features marked passing after lint/type-check succeeds
- Faster iteration for prototyping
**What's the same:**
- Lint and type-check still run to verify code compiles
- Feature MCP server for tracking progress
- All other development tools available
**When to use:** Early prototyping when you want to quickly scaffold features without verification overhead. Switch back to standard mode for production-quality development.
### React UI (in ui/ directory)
```bash

View File

@@ -24,6 +24,7 @@ from progress import print_session_header, print_progress_summary, has_features
from prompts import (
get_initializer_prompt,
get_coding_prompt,
get_coding_prompt_yolo,
copy_spec_to_project,
has_project_prompts,
)
@@ -111,6 +112,7 @@ async def run_autonomous_agent(
project_dir: Path,
model: str,
max_iterations: Optional[int] = None,
yolo_mode: bool = False,
) -> None:
"""
Run the autonomous agent loop.
@@ -119,12 +121,17 @@ async def run_autonomous_agent(
project_dir: Directory for the project
model: Claude model to use
max_iterations: Maximum number of iterations (None for unlimited)
yolo_mode: If True, skip browser testing and use YOLO prompt
"""
print("\n" + "=" * 70)
print(" AUTONOMOUS CODING AGENT DEMO")
print("=" * 70)
print(f"\nProject directory: {project_dir}")
print(f"Model: {model}")
if yolo_mode:
print("Mode: YOLO (testing disabled)")
else:
print("Mode: Standard (full testing)")
if max_iterations:
print(f"Max iterations: {max_iterations}")
else:
@@ -170,7 +177,7 @@ async def run_autonomous_agent(
print_session_header(iteration, is_first_run)
# Create client (fresh context)
client = create_client(project_dir, model)
client = create_client(project_dir, model, yolo_mode=yolo_mode)
# Choose prompt based on session type
# Pass project_dir to enable project-specific prompts
@@ -178,7 +185,11 @@ async def run_autonomous_agent(
prompt = get_initializer_prompt(project_dir)
is_first_run = False # Only use initializer once
else:
prompt = get_coding_prompt(project_dir)
# Use YOLO prompt if in YOLO mode
if yolo_mode:
prompt = get_coding_prompt_yolo(project_dir)
else:
prompt = get_coding_prompt(project_dir)
# Run session with async context manager
async with client:

View File

@@ -16,6 +16,9 @@ Example Usage:
# Limit iterations for testing
python autonomous_agent_demo.py --project-dir my-app --max-iterations 5
# YOLO mode: rapid prototyping without browser testing
python autonomous_agent_demo.py --project-dir my-app --yolo
"""
import argparse
@@ -57,6 +60,9 @@ Examples:
# Limit iterations for testing
python autonomous_agent_demo.py --project-dir my-app --max-iterations 5
# YOLO mode: rapid prototyping without browser testing
python autonomous_agent_demo.py --project-dir my-app --yolo
Authentication:
Uses Claude CLI credentials from ~/.claude/.credentials.json
Run 'claude login' to authenticate (handled by start.bat/start.sh)
@@ -84,6 +90,13 @@ Authentication:
help=f"Claude model to use (default: {DEFAULT_MODEL})",
)
parser.add_argument(
"--yolo",
action="store_true",
default=False,
help="Enable YOLO mode: rapid prototyping without browser testing",
)
return parser.parse_args()
@@ -122,6 +135,7 @@ def main() -> None:
project_dir=project_dir,
model=args.model,
max_iterations=args.max_iterations,
yolo_mode=args.yolo,
)
)
except KeyboardInterrupt:

View File

@@ -72,13 +72,14 @@ BUILTIN_TOOLS = [
]
def create_client(project_dir: Path, model: str):
def create_client(project_dir: Path, model: str, yolo_mode: bool = False):
"""
Create a Claude Agent SDK client with multi-layered security.
Args:
project_dir: Directory for the project
model: Claude model to use
yolo_mode: If True, skip Playwright MCP server for rapid prototyping
Returns:
Configured ClaudeSDKClient (from claude_agent_sdk)
@@ -92,6 +93,30 @@ def create_client(project_dir: Path, model: str):
Note: Authentication is handled by start.bat/start.sh before this runs.
The Claude SDK auto-detects credentials from ~/.claude/.credentials.json
"""
# Build allowed tools list based on mode
# In YOLO mode, exclude Playwright tools for faster prototyping
allowed_tools = [*BUILTIN_TOOLS, *FEATURE_MCP_TOOLS]
if not yolo_mode:
allowed_tools.extend(PLAYWRIGHT_TOOLS)
# Build permissions list
permissions_list = [
# Allow all file operations within the project directory
"Read(./**)",
"Write(./**)",
"Edit(./**)",
"Glob(./**)",
"Grep(./**)",
# Bash permission granted here, but actual commands are validated
# by the bash_security_hook (see security.py for allowed commands)
"Bash(*)",
# Allow Feature MCP tools for feature management
*FEATURE_MCP_TOOLS,
]
if not yolo_mode:
# Allow Playwright MCP tools for browser automation (standard mode only)
permissions_list.extend(PLAYWRIGHT_TOOLS)
# Create comprehensive security settings
# Note: Using relative paths ("./**") restricts access to project directory
# since cwd is set to project_dir
@@ -99,21 +124,7 @@ def create_client(project_dir: Path, model: str):
"sandbox": {"enabled": True, "autoAllowBashIfSandboxed": True},
"permissions": {
"defaultMode": "acceptEdits", # Auto-approve edits within allowed directories
"allow": [
# Allow all file operations within the project directory
"Read(./**)",
"Write(./**)",
"Edit(./**)",
"Glob(./**)",
"Grep(./**)",
# Bash permission granted here, but actual commands are validated
# by the bash_security_hook (see security.py for allowed commands)
"Bash(*)",
# Allow Playwright MCP tools for browser automation
*PLAYWRIGHT_TOOLS,
# Allow Feature MCP tools for feature management
*FEATURE_MCP_TOOLS,
],
"allow": permissions_list,
},
}
@@ -129,7 +140,10 @@ def create_client(project_dir: Path, model: str):
print(" - Sandbox enabled (OS-level bash isolation)")
print(f" - Filesystem restricted to: {project_dir.resolve()}")
print(" - Bash commands restricted to allowlist (see security.py)")
print(" - MCP servers: playwright (browser), features (database)")
if yolo_mode:
print(" - MCP servers: features (database) - YOLO MODE (no Playwright)")
else:
print(" - MCP servers: playwright (browser), features (database)")
print(" - Project settings enabled (skills, commands, CLAUDE.md)")
print()
@@ -140,6 +154,27 @@ def create_client(project_dir: Path, model: str):
else:
print(" - Warning: System Claude CLI not found, using bundled CLI")
# Build MCP servers config - features is always included, playwright only in standard mode
mcp_servers = {
"features": {
"command": sys.executable, # Use the same Python that's running this script
"args": ["-m", "mcp_server.feature_mcp"],
"env": {
# Inherit parent environment (PATH, ANTHROPIC_API_KEY, etc.)
**os.environ,
# Add custom variables
"PROJECT_DIR": str(project_dir.resolve()),
"PYTHONPATH": str(Path(__file__).parent.resolve()),
},
},
}
if not yolo_mode:
# Include Playwright MCP server for browser automation (standard mode only)
mcp_servers["playwright"] = {
"command": "npx",
"args": ["@playwright/mcp@latest", "--viewport-size", "1280x720"],
}
return ClaudeSDKClient(
options=ClaudeAgentOptions(
model=model,
@@ -147,25 +182,8 @@ def create_client(project_dir: Path, model: str):
system_prompt="You are an expert full-stack developer building a production-quality web application.",
setting_sources=["project"], # Enable skills, commands, and CLAUDE.md from project dir
max_buffer_size=10 * 1024 * 1024, # 10MB for large Playwright screenshots
allowed_tools=[
*BUILTIN_TOOLS,
*PLAYWRIGHT_TOOLS,
*FEATURE_MCP_TOOLS,
],
mcp_servers={
"playwright": {"command": "npx", "args": ["@playwright/mcp@latest", "--viewport-size", "1280x720"]},
"features": {
"command": sys.executable, # Use the same Python that's running this script
"args": ["-m", "mcp_server.feature_mcp"],
"env": {
# Inherit parent environment (PATH, ANTHROPIC_API_KEY, etc.)
**os.environ,
# Add custom variables
"PROJECT_DIR": str(project_dir.resolve()),
"PYTHONPATH": str(Path(__file__).parent.resolve()),
},
},
},
allowed_tools=allowed_tools,
mcp_servers=mcp_servers,
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[bash_security_hook]),

View File

@@ -75,6 +75,11 @@ def get_coding_prompt(project_dir: Path | None = None) -> str:
return load_prompt("coding_prompt", project_dir)
def get_coding_prompt_yolo(project_dir: Path | None = None) -> str:
"""Load the YOLO mode coding agent prompt (project-specific if available)."""
return load_prompt("coding_prompt_yolo", project_dir)
def get_app_spec(project_dir: Path) -> str:
"""
Load the app spec from the project.
@@ -131,6 +136,7 @@ def scaffold_project_prompts(project_dir: Path) -> Path:
templates = [
("app_spec.template.txt", "app_spec.txt"),
("coding_prompt.template.md", "coding_prompt.md"),
("coding_prompt_yolo.template.md", "coding_prompt_yolo.md"),
("initializer_prompt.template.md", "initializer_prompt.md"),
]

View File

@@ -11,7 +11,7 @@ from pathlib import Path
from fastapi import APIRouter, HTTPException
from ..schemas import AgentStatus, AgentActionResponse
from ..schemas import AgentStatus, AgentActionResponse, AgentStartRequest
from ..services.process_manager import get_manager
@@ -68,15 +68,19 @@ async def get_agent_status(project_name: str):
status=manager.status,
pid=manager.pid,
started_at=manager.started_at,
yolo_mode=manager.yolo_mode,
)
@router.post("/start", response_model=AgentActionResponse)
async def start_agent(project_name: str):
async def start_agent(
project_name: str,
request: AgentStartRequest = AgentStartRequest(),
):
"""Start the agent for a project."""
manager = get_project_manager(project_name)
success, message = await manager.start()
success, message = await manager.start(yolo_mode=request.yolo_mode)
return AgentActionResponse(
success=success,

View File

@@ -99,11 +99,17 @@ class FeatureListResponse(BaseModel):
# Agent Schemas
# ============================================================================
class AgentStartRequest(BaseModel):
"""Request schema for starting the agent."""
yolo_mode: bool = False
class AgentStatus(BaseModel):
"""Current agent status."""
status: Literal["stopped", "running", "paused", "crashed"]
pid: int | None = None
started_at: datetime | None = None
yolo_mode: bool = False
class AgentActionResponse(BaseModel):

View File

@@ -74,6 +74,7 @@ class AgentProcessManager:
self._status: Literal["stopped", "running", "paused", "crashed"] = "stopped"
self.started_at: datetime | None = None
self._output_task: asyncio.Task | None = None
self.yolo_mode: bool = False # YOLO mode for rapid prototyping
# Support multiple callbacks (for multiple WebSocket clients)
self._output_callbacks: Set[Callable[[str], Awaitable[None]]] = set()
@@ -214,10 +215,13 @@ class AgentProcessManager:
self.status = "stopped"
self._remove_lock()
async def start(self) -> tuple[bool, str]:
async def start(self, yolo_mode: bool = False) -> tuple[bool, str]:
"""
Start the agent as a subprocess.
Args:
yolo_mode: If True, run in YOLO mode (no browser testing)
Returns:
Tuple of (success, message)
"""
@@ -227,6 +231,9 @@ class AgentProcessManager:
if not self._check_lock():
return False, "Another agent instance is already running for this project"
# Store YOLO mode for status queries
self.yolo_mode = yolo_mode
# Build command - pass absolute path to project directory
cmd = [
sys.executable,
@@ -235,6 +242,10 @@ class AgentProcessManager:
str(self.project_dir.resolve()),
]
# Add --yolo flag if YOLO mode is enabled
if yolo_mode:
cmd.append("--yolo")
try:
# Start subprocess with piped stdout/stderr
# Use project_dir as cwd so Claude SDK sandbox allows access to project files
@@ -295,6 +306,7 @@ class AgentProcessManager:
self.status = "stopped"
self.process = None
self.started_at = None
self.yolo_mode = False # Reset YOLO mode
return True, "Agent stopped"
except Exception as e:
@@ -375,6 +387,7 @@ class AgentProcessManager:
"status": self.status,
"pid": self.pid,
"started_at": self.started_at.isoformat() if self.started_at else None,
"yolo_mode": self.yolo_mode,
}

View File

@@ -7,6 +7,7 @@ Uses the create-spec.md skill to guide users through app spec creation.
"""
import asyncio
import json
import logging
import shutil
import threading
@@ -87,6 +88,33 @@ class SpecChatSession:
# Ensure project directory exists (like CLI does in start.py)
self.project_dir.mkdir(parents=True, exist_ok=True)
# Delete app_spec.txt so Claude can create it fresh
# The SDK requires reading existing files before writing, but app_spec.txt is created new
# Note: We keep initializer_prompt.md so Claude can read and update the template
prompts_dir = self.project_dir / "prompts"
app_spec_path = prompts_dir / "app_spec.txt"
if app_spec_path.exists():
app_spec_path.unlink()
logger.info("Deleted scaffolded app_spec.txt for fresh spec creation")
# Create security settings file (like client.py does)
# This grants permissions for file operations in the project directory
security_settings = {
"sandbox": {"enabled": False}, # Disable sandbox for spec creation
"permissions": {
"defaultMode": "acceptEdits",
"allow": [
"Read(./**)",
"Write(./**)",
"Edit(./**)",
"Glob(./**)",
],
},
}
settings_file = self.project_dir / ".claude_settings.json"
with open(settings_file, "w") as f:
json.dump(security_settings, f, indent=2)
# Replace $ARGUMENTS with absolute project path (like CLI does in start.py:184)
# Using absolute path avoids confusion when project folder name differs from app name
project_path = str(self.project_dir.resolve())
@@ -111,6 +139,7 @@ class SpecChatSession:
permission_mode="acceptEdits", # Auto-approve file writes for spec creation
max_turns=100,
cwd=str(self.project_dir.resolve()),
settings=str(settings_file.resolve()),
)
)
# Enter the async context and track it

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useCallback } from 'react'
import { useProjects, useFeatures } from './hooks/useProjects'
import { useProjects, useFeatures, useAgentStatus } from './hooks/useProjects'
import { useProjectWebSocket } from './hooks/useWebSocket'
import { useFeatureSound } from './hooks/useFeatureSound'
import { useCelebration } from './hooks/useCelebration'
@@ -34,6 +34,7 @@ function App() {
const { data: projects, isLoading: projectsLoading } = useProjects()
const { data: features } = useFeatures(selectedProject)
const { data: agentStatusData } = useAgentStatus(selectedProject)
const wsState = useProjectWebSocket(selectedProject)
// Play sounds when features move between columns
@@ -151,6 +152,7 @@ function App() {
<AgentControl
projectName={selectedProject}
status={wsState.agentStatus}
yoloMode={agentStatusData?.yolo_mode ?? false}
/>
</>
)}

View File

@@ -1,4 +1,5 @@
import { Play, Pause, Square, Loader2 } from 'lucide-react'
import { useState } from 'react'
import { Play, Pause, Square, Loader2, Zap } from 'lucide-react'
import {
useStartAgent,
useStopAgent,
@@ -10,9 +11,12 @@ import type { AgentStatus } from '../lib/types'
interface AgentControlProps {
projectName: string
status: AgentStatus
yoloMode?: boolean // From server status - whether currently running in YOLO mode
}
export function AgentControl({ projectName, status }: AgentControlProps) {
export function AgentControl({ projectName, status, yoloMode = false }: AgentControlProps) {
const [yoloEnabled, setYoloEnabled] = useState(false)
const startAgent = useStartAgent(projectName)
const stopAgent = useStopAgent(projectName)
const pauseAgent = usePauseAgent(projectName)
@@ -24,7 +28,7 @@ export function AgentControl({ projectName, status }: AgentControlProps) {
pauseAgent.isPending ||
resumeAgent.isPending
const handleStart = () => startAgent.mutate()
const handleStart = () => startAgent.mutate(yoloEnabled)
const handleStop = () => stopAgent.mutate()
const handlePause = () => pauseAgent.mutate()
const handleResume = () => resumeAgent.mutate()
@@ -34,21 +38,43 @@ export function AgentControl({ projectName, status }: AgentControlProps) {
{/* Status Indicator */}
<StatusIndicator status={status} />
{/* YOLO Mode Indicator - shown when running in YOLO mode */}
{(status === 'running' || status === 'paused') && yoloMode && (
<div className="flex items-center gap-1 px-2 py-1 bg-[var(--color-neo-pending)] border-3 border-[var(--color-neo-border)]">
<Zap size={14} className="text-yellow-900" />
<span className="font-display font-bold text-xs uppercase text-yellow-900">
YOLO
</span>
</div>
)}
{/* Control Buttons */}
<div className="flex gap-1">
{status === 'stopped' || status === 'crashed' ? (
<button
onClick={handleStart}
disabled={isLoading}
className="neo-btn neo-btn-success text-sm py-2 px-3"
title="Start Agent"
>
{isLoading ? (
<Loader2 size={18} className="animate-spin" />
) : (
<Play size={18} />
)}
</button>
<>
{/* YOLO Toggle - only shown when stopped */}
<button
onClick={() => setYoloEnabled(!yoloEnabled)}
className={`neo-btn text-sm py-2 px-3 ${
yoloEnabled ? 'neo-btn-warning' : 'neo-btn-secondary'
}`}
title="YOLO Mode: Skip testing for rapid prototyping"
>
<Zap size={18} className={yoloEnabled ? 'text-yellow-900' : ''} />
</button>
<button
onClick={handleStart}
disabled={isLoading}
className="neo-btn neo-btn-success text-sm py-2 px-3"
title={yoloEnabled ? "Start Agent (YOLO Mode)" : "Start Agent"}
>
{isLoading ? (
<Loader2 size={18} className="animate-spin" />
) : (
<Play size={18} />
)}
</button>
</>
) : status === 'running' ? (
<>
<button

View File

@@ -39,6 +39,7 @@ export function NewProjectModal({
const [error, setError] = useState<string | null>(null)
const [initializerStatus, setInitializerStatus] = useState<InitializerStatus>('idle')
const [initializerError, setInitializerError] = useState<string | null>(null)
const [yoloModeSelected, setYoloModeSelected] = useState(false)
// Suppress unused variable warning - specMethod may be used in future
void _specMethod
@@ -116,11 +117,13 @@ export function NewProjectModal({
}
}
const handleSpecComplete = async () => {
const handleSpecComplete = async (_specPath: string, yoloMode: boolean = false) => {
// Save yoloMode for retry
setYoloModeSelected(yoloMode)
// Auto-start the initializer agent
setInitializerStatus('starting')
try {
await startAgent(projectName.trim())
await startAgent(projectName.trim(), yoloMode)
// Success - navigate to project
setStep('complete')
setTimeout(() => {
@@ -136,7 +139,7 @@ export function NewProjectModal({
const handleRetryInitializer = () => {
setInitializerError(null)
setInitializerStatus('idle')
handleSpecComplete()
handleSpecComplete('', yoloModeSelected)
}
const handleChatCancel = () => {
@@ -153,6 +156,7 @@ export function NewProjectModal({
setError(null)
setInitializerStatus('idle')
setInitializerError(null)
setYoloModeSelected(false)
onClose()
}

View File

@@ -6,7 +6,7 @@
*/
import { useEffect, useRef, useState } from 'react'
import { Send, X, CheckCircle2, AlertCircle, Wifi, WifiOff, RotateCcw, Loader2, ArrowRight } from 'lucide-react'
import { Send, X, CheckCircle2, AlertCircle, Wifi, WifiOff, RotateCcw, Loader2, ArrowRight, Zap } from 'lucide-react'
import { useSpecChat } from '../hooks/useSpecChat'
import { ChatMessage } from './ChatMessage'
import { QuestionOptions } from './QuestionOptions'
@@ -16,7 +16,7 @@ type InitializerStatus = 'idle' | 'starting' | 'error'
interface SpecCreationChatProps {
projectName: string
onComplete: (specPath: string) => void
onComplete: (specPath: string, yoloMode?: boolean) => void
onCancel: () => void
initializerStatus?: InitializerStatus
initializerError?: string | null
@@ -33,6 +33,7 @@ export function SpecCreationChat({
}: SpecCreationChatProps) {
const [input, setInput] = useState('')
const [error, setError] = useState<string | null>(null)
const [yoloEnabled, setYoloEnabled] = useState(false)
const messagesEndRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
@@ -257,7 +258,9 @@ export function SpecCreationChat({
{initializerStatus === 'starting' ? (
<>
<Loader2 size={20} className="animate-spin" />
<span className="font-bold">Starting agent...</span>
<span className="font-bold">
Starting agent{yoloEnabled ? ' (YOLO mode)' : ''}...
</span>
</>
) : initializerStatus === 'error' ? (
<>
@@ -284,13 +287,28 @@ export function SpecCreationChat({
</button>
)}
{initializerStatus === 'idle' && (
<button
onClick={() => onComplete('')}
className="neo-btn neo-btn-primary"
>
Continue to Project
<ArrowRight size={16} />
</button>
<>
{/* YOLO Mode Toggle */}
<button
onClick={() => setYoloEnabled(!yoloEnabled)}
className={`neo-btn text-sm py-2 px-3 ${
yoloEnabled ? 'neo-btn-warning' : 'bg-white'
}`}
title="YOLO Mode: Skip testing for rapid prototyping"
>
<Zap size={16} className={yoloEnabled ? 'text-yellow-900' : ''} />
<span className={yoloEnabled ? 'text-yellow-900 font-bold' : ''}>
YOLO
</span>
</button>
<button
onClick={() => onComplete('', yoloEnabled)}
className="neo-btn neo-btn-primary"
>
Continue to Project
<ArrowRight size={16} />
</button>
</>
)}
</div>
</div>

View File

@@ -111,7 +111,7 @@ export function useStartAgent(projectName: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: () => api.startAgent(projectName),
mutationFn: (yoloMode: boolean = false) => api.startAgent(projectName, yoloMode),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['agent-status', projectName] })
},

View File

@@ -117,9 +117,13 @@ export async function getAgentStatus(projectName: string): Promise<AgentStatusRe
return fetchJSON(`/projects/${encodeURIComponent(projectName)}/agent/status`)
}
export async function startAgent(projectName: string): Promise<AgentActionResponse> {
export async function startAgent(
projectName: string,
yoloMode: boolean = false
): Promise<AgentActionResponse> {
return fetchJSON(`/projects/${encodeURIComponent(projectName)}/agent/start`, {
method: 'POST',
body: JSON.stringify({ yolo_mode: yoloMode }),
})
}

View File

@@ -89,6 +89,7 @@ export interface AgentStatusResponse {
status: AgentStatus
pid: number | null
started_at: string | null
yolo_mode: boolean
}
export interface AgentActionResponse {