diff --git a/.claude/commands/checkpoint.md b/.claude/commands/checkpoint.md new file mode 100644 index 0000000..4787866 --- /dev/null +++ b/.claude/commands/checkpoint.md @@ -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 diff --git a/.claude/commands/create-spec.md b/.claude/commands/create-spec.md index b26a22a..9c8aa8c 100644 --- a/.claude/commands/create-spec.md +++ b/.claude/commands/create-spec.md @@ -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. diff --git a/.claude/templates/coding_prompt_yolo.template.md b/.claude/templates/coding_prompt_yolo.template.md new file mode 100644 index 0000000..5e2f1b7 --- /dev/null +++ b/.claude/templates/coding_prompt_yolo.template.md @@ -0,0 +1,274 @@ + + + +## 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). diff --git a/.claude/templates/initializer_prompt.template.md b/.claude/templates/initializer_prompt.template.md index 742a834..312cd17 100644 --- a/.claude/templates/initializer_prompt.template.md +++ b/.claude/templates/initializer_prompt.template.md @@ -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, diff --git a/CLAUDE.md b/CLAUDE.md index 13feb97..dc0f5e0 100644 --- a/CLAUDE.md +++ b/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 diff --git a/agent.py b/agent.py index c7d3b22..7b6ef87 100644 --- a/agent.py +++ b/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,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: diff --git a/autonomous_agent_demo.py b/autonomous_agent_demo.py index 0e9b2ee..caa2491 100644 --- a/autonomous_agent_demo.py +++ b/autonomous_agent_demo.py @@ -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: diff --git a/client.py b/client.py index 47d92f3..4c81160 100644 --- a/client.py +++ b/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,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]), diff --git a/prompts.py b/prompts.py index 7e666d0..85ebd81 100644 --- a/prompts.py +++ b/prompts.py @@ -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"), ] diff --git a/server/routers/agent.py b/server/routers/agent.py index 116adcf..ecce84c 100644 --- a/server/routers/agent.py +++ b/server/routers/agent.py @@ -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, diff --git a/server/schemas.py b/server/schemas.py index 53bbad1..ab78080 100644 --- a/server/schemas.py +++ b/server/schemas.py @@ -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): diff --git a/server/services/process_manager.py b/server/services/process_manager.py index 6f78e24..31042cc 100644 --- a/server/services/process_manager.py +++ b/server/services/process_manager.py @@ -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, } diff --git a/server/services/spec_chat_session.py b/server/services/spec_chat_session.py index d762ac0..a1a55db 100644 --- a/server/services/spec_chat_session.py +++ b/server/services/spec_chat_session.py @@ -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 diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 4077755..3c639b6 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -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() { )} diff --git a/ui/src/components/AgentControl.tsx b/ui/src/components/AgentControl.tsx index 5356f87..1b94fdd 100644 --- a/ui/src/components/AgentControl.tsx +++ b/ui/src/components/AgentControl.tsx @@ -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 */} + {/* YOLO Mode Indicator - shown when running in YOLO mode */} + {(status === 'running' || status === 'paused') && yoloMode && ( +
+ + + YOLO + +
+ )} + {/* Control Buttons */}
{status === 'stopped' || status === 'crashed' ? ( - + <> + {/* YOLO Toggle - only shown when stopped */} + + + ) : status === 'running' ? ( <> )} {initializerStatus === 'idle' && ( - + <> + {/* YOLO Mode Toggle */} + + + )}
diff --git a/ui/src/hooks/useProjects.ts b/ui/src/hooks/useProjects.ts index e93aea9..0cb61fa 100644 --- a/ui/src/hooks/useProjects.ts +++ b/ui/src/hooks/useProjects.ts @@ -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] }) }, diff --git a/ui/src/lib/api.ts b/ui/src/lib/api.ts index 6ffba63..d8650dd 100644 --- a/ui/src/lib/api.ts +++ b/ui/src/lib/api.ts @@ -117,9 +117,13 @@ export async function getAgentStatus(projectName: string): Promise { +export async function startAgent( + projectName: string, + yoloMode: boolean = false +): Promise { return fetchJSON(`/projects/${encodeURIComponent(projectName)}/agent/start`, { method: 'POST', + body: JSON.stringify({ yolo_mode: yoloMode }), }) } diff --git a/ui/src/lib/types.ts b/ui/src/lib/types.ts index 76f6667..a40b9cb 100644 --- a/ui/src/lib/types.ts +++ b/ui/src/lib/types.ts @@ -89,6 +89,7 @@ export interface AgentStatusResponse { status: AgentStatus pid: number | null started_at: string | null + yolo_mode: boolean } export interface AgentActionResponse {