mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-01-30 06:12:06 +00:00
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:
40
.claude/commands/checkpoint.md
Normal file
40
.claude/commands/checkpoint.md
Normal 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
|
||||
@@ -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.
|
||||
|
||||
|
||||
274
.claude/templates/coding_prompt_yolo.template.md
Normal file
274
.claude/templates/coding_prompt_yolo.template.md
Normal 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).
|
||||
@@ -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,
|
||||
|
||||
27
CLAUDE.md
27
CLAUDE.md
@@ -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
|
||||
|
||||
13
agent.py
13
agent.py
@@ -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,13 +177,17 @@ 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
|
||||
if is_first_run:
|
||||
prompt = get_initializer_prompt(project_dir)
|
||||
is_first_run = False # Only use initializer once
|
||||
else:
|
||||
# Use YOLO prompt if in YOLO mode
|
||||
if yolo_mode:
|
||||
prompt = get_coding_prompt_yolo(project_dir)
|
||||
else:
|
||||
prompt = get_coding_prompt(project_dir)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
70
client.py
70
client.py
@@ -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,14 +93,14 @@ 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
|
||||
"""
|
||||
# Create comprehensive security settings
|
||||
# Note: Using relative paths ("./**") restricts access to project directory
|
||||
# since cwd is set to project_dir
|
||||
security_settings = {
|
||||
"sandbox": {"enabled": True, "autoAllowBashIfSandboxed": True},
|
||||
"permissions": {
|
||||
"defaultMode": "acceptEdits", # Auto-approve edits within allowed directories
|
||||
"allow": [
|
||||
# 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(./**)",
|
||||
@@ -109,11 +110,21 @@ def create_client(project_dir: Path, model: str):
|
||||
# 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,
|
||||
],
|
||||
]
|
||||
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
|
||||
security_settings = {
|
||||
"sandbox": {"enabled": True, "autoAllowBashIfSandboxed": True},
|
||||
"permissions": {
|
||||
"defaultMode": "acceptEdits", # Auto-approve edits within allowed directories
|
||||
"allow": permissions_list,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -129,6 +140,9 @@ 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)")
|
||||
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,20 +154,8 @@ def create_client(project_dir: Path, model: str):
|
||||
else:
|
||||
print(" - Warning: System Claude CLI not found, using bundled CLI")
|
||||
|
||||
return ClaudeSDKClient(
|
||||
options=ClaudeAgentOptions(
|
||||
model=model,
|
||||
cli_path=system_cli, # Use system CLI to avoid bundled Bun crash (exit code 3)
|
||||
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,
|
||||
],
|
||||
# Build MCP servers config - features is always included, playwright only in standard mode
|
||||
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"],
|
||||
@@ -165,7 +167,23 @@ def create_client(project_dir: Path, model: str):
|
||||
"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,
|
||||
cli_path=system_cli, # Use system CLI to avoid bundled Bun crash (exit code 3)
|
||||
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=allowed_tools,
|
||||
mcp_servers=mcp_servers,
|
||||
hooks={
|
||||
"PreToolUse": [
|
||||
HookMatcher(matcher="Bash", hooks=[bash_security_hook]),
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -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,14 +38,35 @@ 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' ? (
|
||||
<>
|
||||
{/* 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="Start Agent"
|
||||
title={yoloEnabled ? "Start Agent (YOLO Mode)" : "Start Agent"}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2 size={18} className="animate-spin" />
|
||||
@@ -49,6 +74,7 @@ export function AgentControl({ projectName, status }: AgentControlProps) {
|
||||
<Play size={18} />
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
) : status === 'running' ? (
|
||||
<>
|
||||
<button
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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' && (
|
||||
<>
|
||||
{/* YOLO Mode Toggle */}
|
||||
<button
|
||||
onClick={() => onComplete('')}
|
||||
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>
|
||||
|
||||
@@ -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] })
|
||||
},
|
||||
|
||||
@@ -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 }),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface AgentStatusResponse {
|
||||
status: AgentStatus
|
||||
pid: number | null
|
||||
started_at: string | null
|
||||
yolo_mode: boolean
|
||||
}
|
||||
|
||||
export interface AgentActionResponse {
|
||||
|
||||
Reference in New Issue
Block a user