argument('name'); $directory = getcwd().'/'.$name; // Validate project name if (! $this->validateProjectName($name)) { return self::FAILURE; } // Check if directory exists if (File::isDirectory($directory) && ! $this->option('force')) { $this->newLine(); $this->components->error("Directory [{$name}] already exists!"); $this->newLine(); $this->components->warn('Use --force to overwrite the existing directory.'); $this->newLine(); return self::FAILURE; } $this->newLine(); $this->components->info(' ╔═══════════════════════════════════════════╗'); $this->components->info(' ║ Core PHP Framework Project Creator ║'); $this->components->info(' ╚═══════════════════════════════════════════╝'); $this->newLine(); $template = $this->option('template') ?: $this->defaultTemplate; $this->components->twoColumnDetail('Project Name', $name); $this->components->twoColumnDetail('Template', $template); $this->components->twoColumnDetail('Directory', $directory); $this->newLine(); try { // Step 1: Create project from template $this->components->task('Creating project from template', function () use ($directory, $template, $name) { return $this->createFromTemplate($directory, $template, $name); }); // Step 2: Install dependencies if (! $this->option('no-install')) { $this->components->task('Installing Composer dependencies', function () use ($directory) { return $this->installDependencies($directory); }); // Step 3: Run core:install $this->components->task('Running framework installation', function () use ($directory) { return $this->runCoreInstall($directory); }); } // Success! $this->newLine(); $this->components->info(' ✓ Project created successfully!'); $this->newLine(); $this->components->info(' Next steps:'); $this->line(" 1. cd {$name}"); if ($this->option('no-install')) { $this->line(' 2. composer install'); $this->line(' 3. php artisan core:install'); $this->line(' 4. php artisan serve'); } else { $this->line(' 2. php artisan serve'); } $this->newLine(); $this->showPackageInfo(); return self::SUCCESS; } catch (\Throwable $e) { $this->newLine(); $this->components->error(' Project creation failed: '.$e->getMessage()); $this->newLine(); // Cleanup on failure if (File::isDirectory($directory)) { $cleanup = $this->confirm('Remove failed project directory?', true); if ($cleanup) { File::deleteDirectory($directory); $this->components->info(' Cleaned up project directory.'); } } return self::FAILURE; } } /** * Validate project name. */ protected function validateProjectName(string $name): bool { if (empty($name)) { $this->components->error('Project name cannot be empty'); return false; } if (! preg_match('/^[a-z0-9_-]+$/i', $name)) { $this->components->error('Project name can only contain letters, numbers, hyphens, and underscores'); return false; } if (in_array(strtolower($name), ['vendor', 'app', 'test', 'tests', 'src', 'public'])) { $this->components->error("Project name '{$name}' is reserved"); return false; } return true; } /** * Create project from template repository. */ protected function createFromTemplate(string $directory, string $template, string $projectName): bool { $branch = $this->option('branch'); // If force, delete existing directory if ($this->option('force') && File::isDirectory($directory)) { File::deleteDirectory($directory); } // Check if template is a URL or repo slug $templateUrl = $this->resolveTemplateUrl($template); // Clone the template $result = Process::run("git clone --branch {$branch} --single-branch --depth 1 {$templateUrl} {$directory}"); if (! $result->successful()) { throw new \RuntimeException("Failed to clone template: {$result->errorOutput()}"); } // Remove .git directory to make it a fresh repo File::deleteDirectory("{$directory}/.git"); // Update composer.json with project name $this->updateComposerJson($directory, $projectName); // Initialize new git repository Process::run("cd {$directory} && git init"); Process::run("cd {$directory} && git add ."); Process::run("cd {$directory} && git commit -m \"Initial commit from Core PHP Framework template\""); return true; } /** * Resolve template to full git URL. */ protected function resolveTemplateUrl(string $template): string { // If already a URL, return as-is if (str_starts_with($template, 'http://') || str_starts_with($template, 'https://')) { return $template; } // If contains .git, treat as SSH URL if (str_contains($template, '.git')) { return $template; } // Otherwise, assume GitHub slug return "https://github.com/{$template}.git"; } /** * Update composer.json with project name. */ protected function updateComposerJson(string $directory, string $projectName): void { $composerPath = "{$directory}/composer.json"; if (! File::exists($composerPath)) { return; } $composer = json_decode(File::get($composerPath), true); $composer['name'] = $this->generateComposerName($projectName); $composer['description'] = "Core PHP Framework application - {$projectName}"; // Update namespace if using default App namespace if (isset($composer['autoload']['psr-4']['App\\'])) { $studlyName = Str::studly($projectName); $composer['autoload']['psr-4']["{$studlyName}\\"] = 'app/'; unset($composer['autoload']['psr-4']['App\\']); } File::put($composerPath, json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."\n"); } /** * Generate composer package name from project name. */ protected function generateComposerName(string $projectName): string { $vendor = $this->ask('Composer vendor name', 'my-company'); $package = Str::slug($projectName); return "{$vendor}/{$package}"; } /** * Install composer dependencies. */ protected function installDependencies(string $directory): bool { $composerBin = $this->findComposer(); $command = $this->option('dev') ? "{$composerBin} install --prefer-source" : "{$composerBin} install"; $result = Process::run("cd {$directory} && {$command}"); if (! $result->successful()) { throw new \RuntimeException("Composer install failed: {$result->errorOutput()}"); } return true; } /** * Run core:install command. */ protected function runCoreInstall(string $directory): bool { $result = Process::run("cd {$directory} && php artisan core:install --no-interaction"); if (! $result->successful()) { throw new \RuntimeException("core:install failed: {$result->errorOutput()}"); } return true; } /** * Find the composer binary. */ protected function findComposer(): string { // Check if composer is in PATH $result = Process::run('which composer'); if ($result->successful()) { return trim($result->output()); } // Check common locations $locations = [ '/usr/local/bin/composer', '/usr/bin/composer', $_SERVER['HOME'].'/.composer/composer.phar', ]; foreach ($locations as $location) { if (File::exists($location)) { return $location; } } return 'composer'; // Fallback, will fail if not in PATH } /** * Show package information. */ protected function showPackageInfo(): void { $this->components->info(' 📦 Installed Core PHP Packages:'); $this->components->twoColumnDetail(' host-uk/core', 'Core framework components'); $this->components->twoColumnDetail(' host-uk/core-admin', 'Admin panel & Livewire modals'); $this->components->twoColumnDetail(' host-uk/core-api', 'REST API with scopes & webhooks'); $this->components->twoColumnDetail(' host-uk/core-mcp', 'Model Context Protocol tools'); $this->newLine(); $this->components->info(' 📚 Documentation:'); $this->components->twoColumnDetail(' https://github.com/host-uk/core-php', 'GitHub Repository'); $this->components->twoColumnDetail(' https://docs.core-php.dev', 'Official Docs (future)'); $this->newLine(); } /** * Get shell completion suggestions. */ public function complete( CompletionInput $input, CompletionSuggestions $suggestions ): void { if ($input->mustSuggestArgumentValuesFor('name')) { // Suggest common project naming patterns $suggestions->suggestValues([ 'my-app', 'api-service', 'admin-panel', 'saas-platform', ]); } if ($input->mustSuggestOptionValuesFor('template')) { // Suggest known templates $suggestions->suggestValues([ 'host-uk/core-template', 'host-uk/core-api-template', 'host-uk/core-admin-template', ]); } } }