-
Notifications
You must be signed in to change notification settings - Fork 279
feat: Add Laravel framework support with tech stack selection and file upload enhancements #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Add Laravel framework support during project creation: - Add tech stack selection wizard (framework, database, testing) - Support Laravel + React/Vue/Livewire/API starter kits - Add Laravel Boost MCP server integration - Create Laravel-specific prompt templates - Add PHP/Laravel commands to security allowlist - Auto-detect framework from app_spec.txt at runtime Framework options: - React + Node.js (default) - Laravel + React (Inertia.js) - Laravel + Vue (Inertia.js) - Laravel + Livewire - Laravel API Only Database options: SQLite, MySQL, PostgreSQL, MariaDB Testing options: Vitest/Jest (Node.js), Pest/PHPUnit (Laravel) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove invalid --boost flag from laravel new commands (doesn't exist) - Make database option dynamic, reading from app_spec.txt <database> tag - Add separate Laravel Boost installation via Composer - Use --no-interaction flag for php artisan boost:install to skip prompts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… laravel_boost) The Laravel Boost MCP server exposes tools with hyphen naming (mcp__laravel-boost__*), but we were configuring it with underscore (mcp__laravel_boost__*), causing permission errors when the agent tried to use Laravel Boost tools. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document the 15 MCP tools available from Laravel Boost so the agent knows to use them instead of bash commands where appropriate. - Add tools table with purpose and examples - Add guidance on when to use MCP vs bash commands - Update database verification step to reference MCP tools - Update Laravel Log section to recommend MCP tools first Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Allow users to upload markdown and text files in addition to images when creating a project spec. This enables uploading detailed spec documents instead of typing them manually. Changes: - Add TextFileAttachment model with UTF-8 validation (schemas.py) - Update backend to send text files as text content blocks to Claude - Add FileAttachment discriminated union type (ImageAttachment | TextAttachment) - Display text files as compact chips in pending attachments - Show sent text files as expandable accordions in chat messages - Update file input to accept .md and .txt files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…es server The features database was missing the timeout parameter in create_engine(), causing immediate "database is locked" failures when encountering WAL artifacts from other processes. This matches the pattern already used in registry.py. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis pull request adds comprehensive Laravel framework support to the project scaffolding system. It introduces Laravel-specific templates, framework detection, interactive tech stack configuration (framework/database/testing choices), Laravel Boost MCP tool integration, enhanced file attachment handling (text + images), and end-to-end framework-aware workflows for both backend and frontend. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Frontend as Frontend UI
participant Backend as Backend API
participant Agent as AI Agent
participant MCPTools as Laravel Boost MCP
participant FileSystem as Project Directory
User->>Frontend: Start New Project
Frontend->>Frontend: Ask framework choice
User->>Frontend: Select Laravel
Frontend->>Frontend: Display tech stack UI
User->>Frontend: Select database & testing
Frontend->>Backend: POST /projects/create with techStack
Backend->>Backend: Detect framework from spec
Backend->>Backend: Create ProjectCreate with tech_stack
Backend->>Agent: Initialize with is_laravel=true
Agent->>Agent: Load Laravel-specific templates
Agent->>Agent: Create client with LARAVEL_BOOST_TOOLS
Agent->>MCPTools: Initialize Laravel Boost MCP server
Agent->>Backend: Call scaffold_project_prompts(framework="laravel")
Backend->>FileSystem: Select from LARAVEL_TEMPLATES_DIR
Backend->>Agent: Provide Laravel initializer prompt
Agent->>MCPTools: Execute feature_create_bulk via MCP
MCPTools->>FileSystem: Create features, migrations, models
Agent->>Backend: Run artisan commands (migrate, etc.)
Backend->>FileSystem: Bootstrap Laravel app
Agent->>Frontend: Report project ready
Frontend->>User: Show Laravel project initialized
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
security.py (1)
184-233:pkillallowingphpis potentially too broad (can kill non-dev PHP processes).
pkill phpis a pattern match and can hitphp-fpm/other PHP processes. If the intent is “only kill Laravel dev server”, consider requiring-fand matching a more specific command line (e.g.,php artisan serve) for PHP-related kills, or requiring-xfor exact-name matches where possible.Proposed tightening (one possible direction)
def validate_pkill_command(command_string: str) -> tuple[bool, str]: @@ allowed_process_names = { @@ - # PHP/Laravel dev servers - "php", - "artisan", + # PHP/Laravel dev servers (prefer full-cmdline match via -f) + # Keep "php" out of the simple-name allowlist to avoid killing unrelated php/php-fpm processes. + "artisan", } @@ - if target in allowed_process_names: - return True, "" + if target in allowed_process_names: + return True, "" + + # Allow php only when using -f with an artisan command line + # e.g. pkill -f "php artisan serve" + if "php" == target and "-f" in tokens and "artisan" in command_string: + return True, ""ui/src/components/ChatMessage.tsx (1)
155-206: Hardenwindow.open+ be defensive about missingtextContent.
window.open(url, '_blank')should includenoopener,noreferrerto avoid reverse-tabnabbing, and text attachments should render something sensible iftextContentis empty/undefined.Proposed diff
- onClick={() => window.open(attachment.previewUrl, '_blank')} + onClick={() => window.open(attachment.previewUrl, '_blank', 'noopener,noreferrer')} ... - {attachment.textContent} + {attachment.textContent ?? '(no content)'}
🤖 Fix all issues with AI agents
In @security.py:
- Around line 35-41: ALLOWED_COMMANDS is missing "phpstan" which breaks
templates that run ./vendor/bin/phpstan analyse; update the ALLOWED_COMMANDS
list to include "phpstan" (and optionally remove unused "pest" if you confirm
tests are always run via "php artisan test"); keep the note that "artisan" in
validate_pkill_command.allowed_process_names remains correct for stopping dev
servers but does not enable "php artisan ..." usage — ensure any template that
expects direct "artisan" execution is adjusted or the template is updated to
call "php" + "artisan" appropriately.
In @ui/src/components/SpecCreationChat.tsx:
- Around line 157-170: The code in SpecCreationChat.tsx uses atob(base64Data) to
build textContent which garbles non-ASCII UTF-8 characters; replace the atob
usage in the TextAttachment creation (the textContent variable) with proper
UTF-8 decoding by converting base64Data to a Uint8Array and decoding it via new
TextDecoder('utf-8'). Ensure the updated textContent is used when constructing
the TextAttachment and then call setPendingAttachments((prev) => [...prev,
attachment]) as before.
🧹 Nitpick comments (12)
ui/src/hooks/useWebSocket.ts (1)
20-20: LGTM! Aligns with PR objectives to expand debug log retention.The 10x increase (100→1000) in retained log lines supports richer debugging workflows introduced in this PR. The slicing logic on line 79 correctly maintains the limit.
Optional operational note: Retaining 10x more logs increases memory usage proportionally. If logs are verbose or multiple WebSocket connections are active simultaneously, monitor memory consumption in production or consider implementing log level filtering or pagination for very large log volumes.
api/database.py (1)
87-87: Good addition to prevent lock contention errors.The 30-second timeout is appropriate and directly addresses the "database is locked" errors mentioned in the PR objectives. This gives SQLite sufficient time to acquire locks in concurrent access scenarios.
Optional enhancement: Consider enabling WAL mode for better concurrency.
For improved concurrent read/write performance, you could optionally enable SQLite's Write-Ahead Logging (WAL) mode:
♻️ Optional WAL mode enhancement
def create_database(project_dir: Path) -> tuple: """ Create database and return engine + session maker. Args: project_dir: Directory containing the project Returns: Tuple of (engine, SessionLocal) """ db_url = get_database_url(project_dir) engine = create_engine(db_url, connect_args={"check_same_thread": False, "timeout": 30}) Base.metadata.create_all(bind=engine) + + # Enable WAL mode for better concurrency + with engine.connect() as conn: + conn.execute(text("PRAGMA journal_mode=WAL")) + conn.commit() # Migrate existing databases to add in_progress column _migrate_add_in_progress_column(engine)WAL mode allows concurrent readers while a writer is active, reducing lock contention beyond what the timeout alone provides.
ui/src/components/ExpandProjectChat.tsx (1)
111-127: Guard base64 parsing + prefercrypto.randomUUID()for attachment IDs.
dataUrl.split(',')[1]can beundefined(unexpected DataURL), and the current ID generation can collide in theory. Suggest a small hardening tweak.Proposed diff
reader.onload = (e) => { const dataUrl = e.target?.result as string - const base64Data = dataUrl.split(',')[1] + const base64Data = dataUrl.split(',')[1] + if (!base64Data) { + setError(`Failed to parse file data: ${file.name}`) + return + } const attachment: ImageAttachment = { type: 'image', - id: `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, + id: crypto.randomUUID(), filename: file.name, mimeType: file.type as 'image/jpeg' | 'image/png', base64Data, previewUrl: dataUrl, size: file.size, }client.py (1)
264-274: Consider pinning the Laravel Boost MCP package version (and confirm env contract).Playwright uses
@latest; Laravel Boost currently doesn’t. If the package expects a specific version or additional args/env (beyondPROJECT_DIR), startup can break.Possible diff (if you want parity with Playwright)
mcp_servers["laravel-boost"] = { "command": "npx", - "args": ["@anthropic/laravel-boost"], + "args": ["@anthropic/laravel-boost@latest"], "env": { **os.environ, "PROJECT_DIR": str(project_dir.resolve()), }, }agent.py (1)
142-149: Consider explicit handling of None from framework detection.The framework detection logic assumes that
detect_framework_from_specreturns either"laravel"or"nodejs". According to the context, it can also returnNonewhen the spec file is not found. Currently,is_laravelwill beFalsewhenframeworkisNone, which effectively defaults to Node.js behavior.If this is intentional for backward compatibility, consider adding a comment to clarify. Otherwise, you may want to log a warning when framework detection returns
None.💡 Optional enhancement to handle None explicitly
# Detect framework from spec framework = detect_framework_from_spec(project_dir) +if framework is None: + print("Framework: Not detected (defaulting to Node.js)") + is_laravel = False +elif framework == "laravel": -is_laravel = framework == "laravel" -if is_laravel: print("Framework: Laravel (with Laravel Boost MCP)") + is_laravel = True else: print("Framework: Node.js") + is_laravel = False print().claude/templates/laravel/initializer_prompt.template.md (2)
104-127: Add language specifier to fenced code block.The fenced code block lacks a language specifier, which can affect syntax highlighting and linting. Since this shows tool usage syntax, consider using a generic identifier.
📝 Suggested fix
-``` +```text Use the feature_create_bulk tool with features=[ { "category": "functional",
357-359: Add language specifier to fenced code block.Similar to the earlier block, this tool usage example should have a language specifier for consistency.
📝 Suggested fix
-``` +```text Use the feature_get_next tool</details> </blockquote></details> <details> <summary>server/services/spec_chat_session.py (1)</summary><blockquote> `294-301`: **Move import to module level.** The `import base64 as b64` inside the function is executed on every call with text attachments. While functional, this is unconventional and slightly inefficient. The module already has access to base64 functionality. <details> <summary>♻️ Proposed fix</summary> Add at the top of the file (around line 9): ```python import base64Then update the decoding:
elif att.mimeType in ('text/plain', 'text/markdown'): # Text file: decode and send as text content block - import base64 as b64 - decoded_text = b64.b64decode(att.base64Data).decode('utf-8') + decoded_text = base64.b64decode(att.base64Data).decode('utf-8') # Format with filename header for context.claude/templates/laravel/coding_prompt_yolo.template.md (1)
40-46: Add language specifiers to MCP tool usage blocks.Multiple fenced code blocks showing MCP tool usage lack language specifiers. While these are minor formatting issues, adding specifiers improves consistency.
📝 Example fix for lines 40-46
-``` +```text # 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</details> This applies similarly to blocks at lines 74-77, 82-84, 112-114, 187-189, and 298-316. </blockquote></details> <details> <summary>start.py (1)</summary><blockquote> `487-507`: **Consider persisting tech stack choices for template customization.** The `tech_stack` dict is built with database and testing choices but only used for display. These values could be valuable for pre-populating the app_spec.txt template or passed to the scaffolding function for automatic placeholder replacement. This could be addressed in a follow-up PR if needed. For now, the display provides useful confirmation to users about their choices. </blockquote></details> <details> <summary>ui/src/components/NewProjectModal.tsx (1)</summary><blockquote> `31-80`: **Consider extracting constants to a shared location.** These option constants are well-defined and correctly typed. However, since the same options may be needed in other components or for server-side validation, consider moving them to `lib/constants.ts` or colocating with the types in `lib/types.ts` to enable reuse and ensure consistency. </blockquote></details> <details> <summary>server/schemas.py (1)</summary><blockquote> `291-316`: **Minor: Error message substring check may be fragile.** The exception handling on line 314 checks for `'not valid UTF-8'` but the actual error raised on line 312 is `'File is not valid UTF-8 text'`. This works but is fragile if error messages change. <details> <summary>♻️ Suggested improvement</summary> Consider using a custom exception class or restructuring to avoid substring matching: ```diff @field_validator('base64Data') @classmethod def validate_base64_and_size(cls, v: str) -> str: """Validate that base64 data is valid UTF-8 text and within size limit.""" try: decoded = base64.b64decode(v) - if len(decoded) > MAX_FILE_SIZE: - raise ValueError( - f'File size ({len(decoded) / (1024 * 1024):.1f} MB) exceeds ' - f'maximum of {MAX_FILE_SIZE // (1024 * 1024)} MB' - ) - # Validate it's valid UTF-8 text - decoded.decode('utf-8') - return v - except UnicodeDecodeError: - raise ValueError('File is not valid UTF-8 text') - except Exception as e: - if 'File size' in str(e) or 'not valid UTF-8' in str(e): - raise - raise ValueError(f'Invalid base64 data: {e}') + except Exception as e: + raise ValueError(f'Invalid base64 data: {e}') + + if len(decoded) > MAX_FILE_SIZE: + raise ValueError( + f'File size ({len(decoded) / (1024 * 1024):.1f} MB) exceeds ' + f'maximum of {MAX_FILE_SIZE // (1024 * 1024)} MB' + ) + + try: + decoded.decode('utf-8') + except UnicodeDecodeError: + raise ValueError('File is not valid UTF-8 text') + + return v
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
.claude/templates/laravel/app_spec.template.txt.claude/templates/laravel/coding_prompt.template.md.claude/templates/laravel/coding_prompt_yolo.template.md.claude/templates/laravel/initializer_prompt.template.mdagent.pyapi/database.pyclient.pyprompts.pysecurity.pyserver/routers/projects.pyserver/routers/spec_creation.pyserver/schemas.pyserver/services/spec_chat_session.pystart.pyui/src/components/ChatMessage.tsxui/src/components/ExpandProjectChat.tsxui/src/components/NewProjectModal.tsxui/src/components/SpecCreationChat.tsxui/src/hooks/useProjects.tsui/src/hooks/useSpecChat.tsui/src/hooks/useWebSocket.tsui/src/lib/api.tsui/src/lib/types.ts
🧰 Additional context used
📓 Path-based instructions (7)
ui/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
ui/src/**/*.{ts,tsx}: Use TypeScript for React components in the UI (React 18 with TypeScript)
Use React Query (TanStack Query) for API calls and data fetching in the UI
Use Radix UI for component primitives in the React UI
Run ESLint to lint React UI code
Files:
ui/src/hooks/useWebSocket.tsui/src/components/ChatMessage.tsxui/src/lib/types.tsui/src/components/ExpandProjectChat.tsxui/src/hooks/useProjects.tsui/src/components/NewProjectModal.tsxui/src/lib/api.tsui/src/hooks/useSpecChat.tsui/src/components/SpecCreationChat.tsx
.claude/templates/**/*.template.md
📄 CodeRabbit inference engine (CLAUDE.md)
Prompt templates should follow a fallback chain: project-specific in {project_dir}/prompts/{name}.md, then base template in .claude/templates/{name}.template.md
Files:
.claude/templates/laravel/coding_prompt.template.md.claude/templates/laravel/initializer_prompt.template.md.claude/templates/laravel/coding_prompt_yolo.template.md
*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Python backend should use the ClaudeSDKClient configuration in client.py with security hooks and MCP servers
Files:
prompts.pystart.pysecurity.pyagent.pyclient.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use SQLite with SQLAlchemy ORM for database access and feature management
Files:
prompts.pystart.pyserver/routers/spec_creation.pyserver/services/spec_chat_session.pyserver/schemas.pyapi/database.pysecurity.pyserver/routers/projects.pyagent.pyclient.py
server/routers/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use FastAPI for REST API endpoints in the server/routers/ directory
Files:
server/routers/spec_creation.pyserver/routers/projects.py
security.py
📄 CodeRabbit inference engine (CLAUDE.md)
Implement bash command validation using the ALLOWED_COMMANDS whitelist in security.py
Files:
security.py
agent.py
📄 CodeRabbit inference engine (CLAUDE.md)
Agent sessions should auto-continue with a 3-second delay between sessions
Files:
agent.py
🧠 Learnings (8)
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Applies to .claude/templates/**/*.template.md : Prompt templates should follow a fallback chain: project-specific in {project_dir}/prompts/{name}.md, then base template in .claude/templates/{name}.template.md
Applied to files:
.claude/templates/laravel/coding_prompt.template.md.claude/templates/laravel/initializer_prompt.template.mdprompts.py
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Two-agent pattern: Initializer Agent reads app spec and creates features in first session; Coding Agent implements features one by one in subsequent sessions
Applied to files:
.claude/templates/laravel/initializer_prompt.template.md
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Applies to ui/src/**/*.{ts,tsx} : Use TypeScript for React components in the UI (React 18 with TypeScript)
Applied to files:
ui/src/lib/types.tsui/src/hooks/useProjects.tsui/src/components/SpecCreationChat.tsx
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Applies to ui/src/**/*.{ts,tsx} : Use React Query (TanStack Query) for API calls and data fetching in the UI
Applied to files:
ui/src/hooks/useProjects.ts
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Applies to security.py : Implement bash command validation using the ALLOWED_COMMANDS whitelist in security.py
Applied to files:
security.py
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Applies to mcp_server/**/*.py : Use MCP (Model Context Protocol) servers for agent tools: feature_mcp.py for feature management, with tools: feature_get_stats, feature_get_next, feature_get_for_regression, feature_mark_passing, feature_skip, feature_create_bulk
Applied to files:
client.py
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Defense-in-depth security model: OS-level sandbox for bash commands, filesystem restricted to project directory, bash commands validated against ALLOWED_COMMANDS whitelist
Applied to files:
client.py
📚 Learning: 2026-01-10T08:23:04.012Z
Learnt from: CR
Repo: leonvanzyl/autocoder PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T08:23:04.012Z
Learning: Applies to *.py : Python backend should use the ClaudeSDKClient configuration in client.py with security hooks and MCP servers
Applied to files:
client.py
🧬 Code graph analysis (13)
start.py (2)
server/schemas.py (1)
is_laravel(62-64)prompts.py (1)
scaffold_project_prompts(148-202)
server/routers/spec_creation.py (2)
server/schemas.py (2)
ImageAttachment(267-288)TextFileAttachment(291-316)ui/src/lib/types.ts (2)
ImageAttachment(214-222)FileAttachment(236-236)
server/services/spec_chat_session.py (3)
server/schemas.py (2)
ImageAttachment(267-288)TextFileAttachment(291-316)ui/src/lib/types.ts (2)
ImageAttachment(214-222)FileAttachment(236-236)server/services/expand_chat_session.py (2)
_make_multimodal_message(42-51)_query_claude(239-340)
ui/src/components/ExpandProjectChat.tsx (1)
ui/src/hooks/useExpandChat.ts (1)
a(305-309)
ui/src/hooks/useProjects.ts (2)
server/schemas.py (1)
TechStackConfig(56-80)ui/src/lib/types.ts (1)
TechStackConfig(388-392)
ui/src/components/NewProjectModal.tsx (1)
ui/src/lib/types.ts (6)
FrameworkOption(394-400)DatabaseOption(402-407)TestingOption(409-415)FrameworkChoice(377-382)TestingFramework(386-386)TechStackConfig(388-392)
server/schemas.py (1)
ui/src/lib/types.ts (6)
FrameworkChoice(377-382)DatabaseChoice(384-384)TestingFramework(386-386)TechStackConfig(388-392)FileAttachment(236-236)ImageAttachment(214-222)
ui/src/lib/api.ts (2)
server/schemas.py (2)
TechStackConfig(56-80)ProjectSummary(103-108)ui/src/lib/types.ts (2)
TechStackConfig(388-392)ProjectSummary(13-18)
server/routers/projects.py (1)
server/schemas.py (1)
is_laravel(62-64)
agent.py (3)
prompts.py (1)
detect_framework_from_spec(25-50)server/schemas.py (1)
is_laravel(62-64)client.py (1)
create_client(132-293)
client.py (1)
server/schemas.py (1)
is_laravel(62-64)
ui/src/hooks/useSpecChat.ts (1)
ui/src/lib/types.ts (1)
FileAttachment(236-236)
ui/src/components/SpecCreationChat.tsx (2)
ui/src/lib/types.ts (3)
FileAttachment(236-236)ImageAttachment(214-222)TextAttachment(225-233)server/schemas.py (1)
ImageAttachment(267-288)
🪛 LanguageTool
.claude/templates/laravel/app_spec.template.txt
[style] ~248-~248: Using many exclamation marks might seem excessive (in this case: 17 exclamation marks for a text that’s 3681 characters long)
Context: ... </design_system> <key_interactions> ...
(EN_EXCESSIVE_EXCLAMATION)
.claude/templates/laravel/coding_prompt.template.md
[style] ~129-~129: ‘almost NEVER’ might be wordy. Consider a shorter alternative.
Context: ...ure (EXTREMELY RARE) Skipping should almost NEVER happen. Only skip for truly external ...
(EN_WORDINESS_PREMIUM_ALMOST_NEVER)
[style] ~381-~381: Consider using a different verb for a more formal wording.
Context: ...ou completed - Any issues discovered or fixed - What should be worked on next - Curre...
(FIX_RESOLVE)
.claude/templates/laravel/coding_prompt_yolo.template.md
[style] ~91-~91: ‘almost NEVER’ might be wordy. Consider a shorter alternative.
Context: ...ure (EXTREMELY RARE) Skipping should almost NEVER happen. Only skip for truly external ...
(EN_WORDINESS_PREMIUM_ALMOST_NEVER)
[style] ~220-~220: Consider using a different verb for a more formal wording.
Context: ...ou completed - Any issues discovered or fixed - What should be worked on next - Curre...
(FIX_RESOLVE)
🪛 markdownlint-cli2 (0.18.1)
.claude/templates/laravel/coding_prompt.template.md
29-29: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
60-60: Bare URL used
(MD034, no-bare-urls)
61-61: Bare URL used
(MD034, no-bare-urls)
74-74: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
112-112: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
119-119: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
150-150: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
190-190: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
235-235: Bare URL used
(MD034, no-bare-urls)
256-256: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
342-342: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
346-346: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
500-500: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
540-540: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
.claude/templates/laravel/initializer_prompt.template.md
104-104: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
357-357: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
.claude/templates/laravel/coding_prompt_yolo.template.md
29-29: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
60-60: Bare URL used
(MD034, no-bare-urls)
61-61: Bare URL used
(MD034, no-bare-urls)
74-74: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
112-112: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
119-119: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
150-150: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
190-190: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
235-235: Bare URL used
(MD034, no-bare-urls)
256-256: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
342-342: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
346-346: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (41)
security.py (1)
47-49:pgrepis not currently used in the codebase; the whitelist approach is sufficient protection.
pgrepis listed in the ALLOWED_COMMANDS allowlist but is not invoked anywhere in the codebase. Since usage would require explicit code changes (subject to review), and the whitelist mechanism itself prevents arbitrary flag injection, no additional flag-level restrictions are needed at this time.client.py (2)
132-190: LGTM: Laravel tools gated behindis_laraveland stay out of YOLO/Node flows by default.The conditional extension of both
allowed_toolsandpermissions_listis the right pattern for keeping the security surface minimal outside Laravel projects. Based on learnings, this matches the repo’s allowlist-based security model.
99-117: The Laravel Boost MCP package (@anthropic/laravel-boost) does not exist on the public npm registry and cannot be verified.The tool IDs in
LARAVEL_BOOST_TOOLScannot be validated because the source package is unavailable. Before this code runs, confirm:
- The package name is correct and the package is published (public or private registry)
- The exact tool names exported by the package match these identifiers
- The npx invocation path is accessible in your environment
Without these validations, the allowlist will either fail silently or fail loudly at runtime.
ui/src/lib/api.ts (1)
54-69: Good propagation of tech stack selection viatech_stackpayload.
tech_stack: techStackwill be omitted whenundefined(JSON.stringify behavior), so this stays backwards-compatible for callers that don’t pass a tech stack.ui/src/components/ChatMessage.tsx (1)
8-30: Stateful expand/collapse via Set is clean and avoids mutation.server/routers/projects.py (1)
172-179: The code is correct. The lazy import mechanism in this file properly assigns_scaffold_project_promptsvia_init_imports()(called at line 133 before the function usage at line 178). Thescaffold_project_promptsfunction signature acceptsframework: str = "nodejs"and supports both "nodejs" and "laravel" values as documented, matching the framework types passed from lines 173-176.server/routers/spec_creation.py (2)
17-17: LGTM! Import and type annotation updated correctly.The imports now include both
TextFileAttachmentand the union typeFileAttachment, and the attachment list typing correctly reflects the expanded attachment model.Also applies to: 257-257
256-275: The MIME type string literals in the attachment parsing logic correctly match the schema definitions. The router uses'image/jpeg','image/png','text/plain', and'text/markdown', which exactly correspond to theLiteraltypes defined inschemas.py. No changes needed.agent.py (1)
190-190: LGTM! Framework-aware client creation.The
is_laravelflag is correctly passed tocreate_client, enabling framework-specific MCP server configuration and tool availability..claude/templates/laravel/coding_prompt.template.md (3)
1-62: LGTM! Clear orientation and startup instructions.Steps 1 and 2 provide comprehensive orientation commands and server startup procedures tailored for Laravel projects. The instructions correctly reference Laravel-specific URLs and commands.
494-531: LGTM! Clear feature tool usage boundaries.The feature tool usage rules effectively prevent token waste by restricting queries to specific, necessary operations. The explicit "DO NOT" list helps prevent exploratory queries that would bloat context.
464-491: Tool names in template are correct.All 15 Laravel Boost MCP tools listed match the actual tools defined in
client.py(lines 101-116). The template uses simplified names (e.g.,database_query,list_routes) for documentation clarity, which correctly correspond to the actual tool names with hyphens and prefixes (e.g.,mcp__laravel-boost__database-query).ui/src/hooks/useSpecChat.ts (1)
6-6: LGTM! Attachment type generalized correctly.The hook now accepts
FileAttachment[]instead ofImageAttachment[], enabling support for both image and text file attachments. The type changes are applied consistently across the import, function signature, and implementation.Also applies to: 25-25, 367-367
ui/src/hooks/useProjects.ts (1)
7-7: LGTM! Tech stack configuration parameter added correctly.The
techStackparameter is properly typed as optional and passed through to the API layer, enabling framework/database/testing selection during project creation. The change is backward compatible since the parameter is optional.Also applies to: 32-37
.claude/templates/laravel/app_spec.template.txt (1)
1-359: LGTM!Well-structured Laravel project specification template. The sections comprehensively cover Laravel-specific configuration (starter kits, Pest/PHPUnit, Boost MCP), technology choices, and implementation guidance. Port assignments (8000 for Laravel, 5173 for Vite) are correct for the standard Laravel development setup.
.claude/templates/laravel/initializer_prompt.template.md (1)
246-320: LGTM!The
init.shscript template is well-designed with proper error handling (set -e), conditional checks for existing dependencies, concurrent server management with proper cleanup viatrap, and support for both Inertia-based and API-only Laravel projects.ui/src/components/SpecCreationChat.tsx (1)
338-365: LGTM!The conditional rendering correctly differentiates between image and text attachments. The text file preview shows appropriate metadata (filename, size) without overwhelming the UI, and the FileText icon provides good visual distinction.
prompts.py (2)
25-50: LGTM!The framework detection logic is well-structured with a clear hierarchy: check for explicit
<laravel_specific>tag first, then fall back to keyword detection, with sensible defaults. The backward-compatible default to'nodejs'is appropriate.
165-189: LGTM!The framework-aware template selection with fallback chain is correctly implemented. The code properly handles the case where Laravel templates don't exist by falling back to base templates with an appropriate warning. This aligns with the coding guideline for prompt template fallback chains.
server/services/spec_chat_session.py (1)
280-306: LGTM on attachment type handling.The logic correctly handles both image and text attachments by checking
mimeType. The text file formatting with filename headers provides good context for Claude to understand the attached content..claude/templates/laravel/coding_prompt_yolo.template.md (2)
1-10: LGTM!The YOLO mode header clearly communicates the trade-offs: skipping browser testing in favor of static analysis (Pint, PHPStan) for rapid prototyping. The synchronization comment with
coding_prompt.template.mdis helpful for maintainability.
262-289: Excellent MCP tools reference table.The Laravel Boost MCP tools table provides clear guidance on when to use MCP tools vs bash commands. This practical comparison (e.g.,
tinkerMCP for one-liners vsphp artisan tinkerfor interactive REPL) helps agents make efficient choices.start.py (2)
192-229: LGTM!The framework choice function provides a clear menu with helpful descriptions for each option. The default handling (empty string = option 1) follows good CLI UX patterns. The returned dict structure with
is_laravelflag enables downstream framework-specific logic.
260-296: LGTM!The testing choice function correctly adapts options based on framework context (Pest/PHPUnit for Laravel, Vitest/Jest for Node.js). The conditional structure is clean and the defaults are sensible (Pest for Laravel, Vitest for Node.js).
ui/src/components/NewProjectModal.tsx (9)
12-25: LGTM!Clean imports with proper type imports from the types module. The
Checkicon is correctly imported for the selection UI.
82-90: LGTM!Helper functions are concise and correctly mirror the server-side
TechStackConfig.is_laravel()andget_default_testing()methods, ensuring consistent behavior.
113-125: LGTM!State initialization and derived
availableTestingOptionsare correctly implemented. The filter ensures only testing frameworks appropriate for the selected stack are shown.
156-164: LGTM!
handleFrameworkSelectcorrectly updates the framework and resets the testing option to the appropriate default for that framework, preventing invalid combinations (e.g., selecting Jest with Laravel).
262-266: LGTM!State is correctly reset to defaults when the modal closes, ensuring a clean state for subsequent project creation.
270-282: LGTM!Back navigation correctly traverses all new steps and clears state appropriately.
186-191: LGTM!Both manual and Claude project creation paths correctly include the
techStackconfiguration in the API payload.Also applies to: 203-208
407-477: LGTM!The framework selection UI is well-structured with clear visual feedback for selection state, appropriate use of semantic buttons, and consistent styling.
479-575: LGTM!The tech options UI correctly presents database and filtered testing framework options. The grid layout is consistent with the framework selection step.
ui/src/lib/types.ts (3)
214-236: LGTM!Good use of discriminated union pattern with the
typefield forImageAttachmentandTextAttachment. This enables exhaustive type checking when handling attachments. TheFileAttachmentunion type cleanly combines both.
239-247: LGTM!
ChatMessage.attachmentscorrectly uses the newFileAttachment[]union type to support both image and text attachments.
377-415: LGTM!Tech stack types are well-defined and correctly align with server-side
schemas.pydefinitions. TheTestingOption.forFrameworkfield provides a clear UI-friendly way to filter options.server/schemas.py (5)
28-54: LGTM!The enums are well-structured using
str, Enumfor proper JSON serialization. The values align with frontendFrameworkChoice,DatabaseChoice, andTestingFrameworktypes.
56-81: LGTM!
TechStackConfigis well-designed with sensible defaults and useful helper methods. Thetesting: TestingFramework | None = Noneapproach withget_default_testing()allows flexible defaulting on the server side.
92-92: LGTM!Optional
tech_stackfield maintains backward compatibility with existing API consumers.
263-264: LGTM!Consolidating to a single
MAX_FILE_SIZEconstant simplifies maintenance and ensures consistent limits across file types.
319-320: LGTM!The
FileAttachmenttype alias correctly creates a union type matching the frontend definition for consistent typing across the stack.
| # PHP/Laravel development | ||
| "php", | ||
| "composer", | ||
| "laravel", | ||
| "artisan", | ||
| "pest", | ||
| "pint", # Laravel code style fixer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find invocations of Laravel/PHP test tooling to confirm which basenames must be allowlisted
rg -n --hidden -S '\b(phpunit|pest|pint|artisan|composer)\b' -g'!.git/**' | head -100Repository: leonvanzyl/autocoder
Length of output: 9593
🏁 Script executed:
#!/bin/bash
# Also search for vendor/bin invocations that might require special handling
rg -n --hidden -S '\./vendor/bin/|composer require|php artisan' -g'!.git/**' | head -100Repository: leonvanzyl/autocoder
Length of output: 7783
🏁 Script executed:
#!/bin/bash
# Check the security.py file to see current ALLOWED_COMMANDS and validate_pkill_command implementation
cat -n security.py | sed -n '1,250p'Repository: leonvanzyl/autocoder
Length of output: 8420
Add phpstan to ALLOWED_COMMANDS; clarify that artisan in the allowlist only supports direct execution (not the typical php artisan form).
Your analysis is correct: extract_commands("php artisan ...") extracts only php, so the artisan entry in ALLOWED_COMMANDS (line 39) does not enable typical Laravel usage. However, artisan in validate_pkill_command.allowed_process_names (line 202) is appropriate for stopping dev servers.
Missing: The templates invoke ./vendor/bin/phpstan analyse (e.g., coding_prompt_yolo.template.md:167), but phpstan is not in ALLOWED_COMMANDS. Either add phpstan or remove the template reference if it is not expected to run. Additionally, pest in ALLOWED_COMMANDS (line 40) is not invoked directly; tests use php artisan test instead.
🤖 Prompt for AI Agents
In @security.py around lines 35 - 41, ALLOWED_COMMANDS is missing "phpstan"
which breaks templates that run ./vendor/bin/phpstan analyse; update the
ALLOWED_COMMANDS list to include "phpstan" (and optionally remove unused "pest"
if you confirm tests are always run via "php artisan test"); keep the note that
"artisan" in validate_pkill_command.allowed_process_names remains correct for
stopping dev servers but does not enable "php artisan ..." usage — ensure any
template that expects direct "artisan" execution is adjusted or the template is
updated to call "php" + "artisan" appropriately.
| } else { | ||
| // Text file attachment - decode for display | ||
| const textContent = atob(base64Data) | ||
| const attachment: TextAttachment = { | ||
| type: 'text', | ||
| id, | ||
| filename: file.name, | ||
| mimeType: file.type as 'text/plain' | 'text/markdown', | ||
| base64Data, | ||
| textContent, | ||
| size: file.size, | ||
| } | ||
| setPendingAttachments((prev) => [...prev, attachment]) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UTF-8 decoding issue with atob() for non-ASCII text.
The atob() function returns a binary string where each character represents one byte. For UTF-8 encoded text with non-ASCII characters (e.g., emojis, accented letters), this will produce garbled output. Use TextDecoder for proper UTF-8 handling.
🐛 Proposed fix
} else {
- // Text file attachment - decode for display
- const textContent = atob(base64Data)
+ // Text file attachment - decode UTF-8 for display
+ const binaryString = atob(base64Data)
+ const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0))
+ const textContent = new TextDecoder('utf-8').decode(bytes)
const attachment: TextAttachment = {
type: 'text',
id,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else { | |
| // Text file attachment - decode for display | |
| const textContent = atob(base64Data) | |
| const attachment: TextAttachment = { | |
| type: 'text', | |
| id, | |
| filename: file.name, | |
| mimeType: file.type as 'text/plain' | 'text/markdown', | |
| base64Data, | |
| textContent, | |
| size: file.size, | |
| } | |
| setPendingAttachments((prev) => [...prev, attachment]) | |
| } | |
| } else { | |
| // Text file attachment - decode UTF-8 for display | |
| const binaryString = atob(base64Data) | |
| const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0)) | |
| const textContent = new TextDecoder('utf-8').decode(bytes) | |
| const attachment: TextAttachment = { | |
| type: 'text', | |
| id, | |
| filename: file.name, | |
| mimeType: file.type as 'text/plain' | 'text/markdown', | |
| base64Data, | |
| textContent, | |
| size: file.size, | |
| } | |
| setPendingAttachments((prev) => [...prev, attachment]) | |
| } |
🤖 Prompt for AI Agents
In @ui/src/components/SpecCreationChat.tsx around lines 157 - 170, The code in
SpecCreationChat.tsx uses atob(base64Data) to build textContent which garbles
non-ASCII UTF-8 characters; replace the atob usage in the TextAttachment
creation (the textContent variable) with proper UTF-8 decoding by converting
base64Data to a Uint8Array and decoding it via new TextDecoder('utf-8'). Ensure
the updated textContent is used when constructing the TextAttachment and then
call setPendingAttachments((prev) => [...prev, attachment]) as before.
Summary
This PR introduces comprehensive Laravel framework support to autocoder, along with several quality-of-life improvements and bug fixes.
Major Features
Laravel Framework Integration: Full support for Laravel projects with tech stack selection wizard
File Upload Enhancements: Extended spec creation chat to accept markdown (.md) and text (.txt) files
Improvements
Bug Fixes
Files Changed
23 files modified across frontend components, backend services, and configuration files.
Testing
Screenshots
Add any relevant screenshots here
🤖 Co-authored with Claude Opus 4.5
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.