hydra-swe-builder
Version:
A comprehensive CLI tool for creating software engineering projects (Mobile, Frontend, Backend)
1,935 lines (1,668 loc) • 66.5 kB
JavaScript
#!/usr/bin/env node
const { program } = require('commander');
const inquirer = require('inquirer');
const chalk = require('chalk');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const { default: ora } = require('ora');
// ASCII Art Banner
const banner = `
██╗ ██╗██╗ ██╗██████╗ ██████╗ █████╗
██║ ██║╚██╗ ██╔╝██╔══██╗██╔══██╗██╔══██╗
███████║ ╚████╔╝ ██║ ██║██████╔╝███████║
██╔══██║ ╚██╔╝ ██║ ██║██╔══██╗██╔══██║
██║ ██║ ██║ ██████╔╝██║ ██║██║ ██║
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
███████╗██╗ ██╗███████╗
██╔════╝██║ ██║██╔════╝
███████╗██║ █╗ ██║█████╗
╚════██║██║███╗██║██╔══╝
███████║╚███╔███╔╝███████╗
╚══════╝ ╚══╝╚══╝ ╚══════╝
HYDRA (Software Engineering Project Builder) v1.0.0
by Michel Okoubi
`;
class SEBuilderCLI {
constructor() {
this.projectTypes = {
mobile: {
name: 'Mobile Application',
icon: '📱',
options: [
{ name: 'React Native with Expo', value: 'expo' },
{ name: 'React Native CLI', value: 'rn-cli' },
{ name: 'Native iOS (Swift)', value: 'ios' },
{ name: 'Native Android (Kotlin)', value: 'android' }
]
},
frontend: {
name: 'Frontend Application',
icon: '🌐',
options: [
{ name: 'Angular', value: 'angular' },
{ name: 'Next.js', value: 'nextjs' }
]
},
backend: {
name: 'Backend API',
icon: '⚙️',
options: [
{ name: 'NestJS + Fastify', value: 'nestjs' },
{ name: 'Rust + Actix Web', value: 'rust' },
{ name: 'Go + Gin', value: 'go' },
{ name: 'Quarkus (Java)', value: 'quarkus' }
]
},
fullstack: {
name: 'Full Stack Project',
icon: '🔗',
description: 'Complete application with frontend, backend, and mobile'
}
};
this.apiTypes = [
{ name: 'REST API', value: 'rest' },
{ name: 'GraphQL', value: 'graphql' },
{ name: 'gRPC', value: 'grpc' },
{ name: 'WebSocket', value: 'websocket' },
{ name: 'All of the above', value: 'all' }
];
}
displayBanner() {
console.log(chalk.cyan(banner));
console.log(chalk.green("Welcome to Hydra 🚀"));
}
async interactiveMode() {
this.displayBanner();
const { projectType } = await inquirer.prompt([
{
type: 'list',
name: 'projectType',
message: 'What type of project do you want to create?',
choices: Object.entries(this.projectTypes).map(([key, config]) => ({
name: `${config.icon} ${config.name}`,
value: key,
short: config.name
}))
}
]);
const { projectName } = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'Enter your project name:',
validate: (input) => {
if (!input.trim()) return 'Project name is required';
if (!/^[a-zA-Z0-9-_]+$/.test(input)) return 'Project name must contain only letters, numbers, hyphens, and underscores';
return true;
}
}
]);
await this.createProject(projectType, projectName);
}
async createProject(type, name, options = {}) {
console.log(chalk.blue(`\n🚀 Creating ${this.projectTypes[type].name}: ${name}`));
switch (type) {
case 'mobile':
await this.createMobileProject(name, options);
break;
case 'frontend':
await this.createFrontendProject(name, options);
break;
case 'backend':
await this.createBackendProject(name, options);
break;
case 'fullstack':
await this.createFullStackProject(name, options);
break;
default:
console.log(chalk.red('❌ Invalid project type'));
process.exit(1);
}
}
async createMobileProject(projectName, options = {}) {
let framework = options.framework;
if (!framework) {
const { selectedFramework } = await inquirer.prompt([
{
type: 'list',
name: 'selectedFramework',
message: 'Select mobile framework:',
choices: this.projectTypes.mobile.options
}
]);
framework = selectedFramework;
}
const spinner = this.createSpinner(`Creating ${framework} project...`);
try {
switch (framework) {
case 'expo':
await this.scaffoldExpoProject(projectName);
break;
case 'rn-cli':
await this.scaffoldRNProject(projectName);
break;
case 'ios':
await this.scaffoldiOSProject(projectName);
break;
case 'android':
await this.scaffoldAndroidProject(projectName);
break;
}
spinner.succeed(chalk.green(`✅ ${framework} project created successfully!`));
this.showPostInstallInstructions(projectName, framework);
} catch (error) {
spinner.fail(chalk.red(`❌ Error creating project: ${error.message}`));
process.exit(1);
}
}
async createBackendProject(projectName, options = {}) {
let framework = options.framework;
let apiType = options.apiType;
if (!framework) {
const { selectedFramework } = await inquirer.prompt([
{
type: 'list',
name: 'selectedFramework',
message: 'Select backend framework:',
choices: this.projectTypes.backend.options
}
]);
framework = selectedFramework;
}
if (!apiType) {
const { selectedApiType } = await inquirer.prompt([
{
type: 'list',
name: 'selectedApiType',
message: 'Select API type:',
choices: this.apiTypes
}
]);
apiType = selectedApiType;
}
const spinner = this.createSpinner(`Creating ${framework} project with ${apiType}...`);
try {
switch (framework) {
case 'nestjs':
await this.scaffoldNestJSProject(projectName, apiType);
break;
case 'rust':
await this.scaffoldRustProject(projectName, apiType);
break;
case 'go':
await this.scaffoldGoProject(projectName, apiType);
break;
case 'quarkus':
await this.scaffoldQuarkusProject(projectName, apiType);
break;
}
spinner.succeed(chalk.green(`✅ ${framework} project created successfully!`));
this.showPostInstallInstructions(projectName, framework);
} catch (error) {
spinner.fail(chalk.red(`❌ Error creating project: ${error.message}`));
process.exit(1);
}
}
async createFrontendProject(projectName, options = {}) {
let framework = options.framework;
if (!framework) {
const { selectedFramework } = await inquirer.prompt([
{
type: 'list',
name: 'selectedFramework',
message: 'Select frontend framework:',
choices: this.projectTypes.frontend.options
}
]);
framework = selectedFramework;
}
const spinner = this.createSpinner(`Creating ${framework} project...`);
try {
switch (framework) {
case 'angular':
await this.scaffoldAngularProject(projectName);
break;
case 'nextjs':
await this.scaffoldNextJSProject(projectName);
break;
}
spinner.succeed(chalk.green(`✅ ${framework} project created successfully!`));
this.showPostInstallInstructions(projectName, framework);
} catch (error) {
spinner.fail(chalk.red(`❌ Error creating project: ${error.message}`));
process.exit(1);
}
}
async createFullStackProject(projectName, options = {}) {
const spinner = this.createSpinner('Creating full stack project structure...');
try {
this.createDirectory(projectName);
process.chdir(projectName);
// Create project structure
await this.scaffoldNextJSProject('frontend');
await this.scaffoldNestJSProject('backend', 'all');
await this.scaffoldExpoProject('mobile');
// Create configuration files
this.createDockerCompose();
this.createMonorepoConfig();
this.createReadme(projectName);
spinner.succeed(chalk.green('✅ Full stack project created successfully!'));
this.showPostInstallInstructions(projectName, 'fullstack');
} catch (error) {
spinner.fail(chalk.red(`❌ Error creating project: ${error.message}`));
process.exit(1);
}
}
// Scaffolding methods (simplified versions of the original implementation)
async scaffoldExpoProject(projectName) {
this.execCommand(`npx create-expo-app@latest ${projectName} --template blank-typescript`);
process.chdir(projectName);
this.execCommand('npx expo install react-native-screens react-native-safe-area-context');
this.execCommand('npm install @react-navigation/native @react-navigation/stack');
}
async scaffoldRNProject(projectName) {
this.execCommand(`npx @react-native-community/cli@latest init ${projectName}`);
}
async scaffoldiOSProject(projectName) {
this.createDirectory(projectName);
process.chdir(projectName);
this.createiOSStructure(projectName);
}
async scaffoldAndroidProject(projectName) {
this.createDirectory(projectName);
process.chdir(projectName);
this.createAndroidStructure(projectName);
}
async scaffoldAngularProject(projectName) {
this.execCommand(`npx @angular/cli@latest new ${projectName} --routing --style=scss --skip-git`);
process.chdir(projectName);
this.execCommand('ng add @angular/material --skip-confirmation');
}
async scaffoldNextJSProject(projectName) {
this.execCommand(`npx create-next-app@latest ${projectName} --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"`);
process.chdir(projectName);
this.execCommand('npm install lucide-react @radix-ui/react-icons');
}
async scaffoldNestJSProject(projectName, apiType) {
this.execCommand(`npx @nestjs/cli new ${projectName} --skip-git`);
process.chdir(projectName);
// Install Fastify
this.execCommand('npm install @nestjs/platform-fastify');
// Install API-specific dependencies
const deps = this.getBackendDependencies('nestjs', apiType);
if (deps.length > 0) {
this.execCommand(`npm install ${deps.join(' ')}`);
}
// Create enhanced main.ts
this.createNestJSMain();
}
async scaffoldRustProject(projectName, apiType) {
this.execCommand(`cargo new ${projectName}`);
process.chdir(projectName);
const cargoToml = this.generateRustCargoToml(projectName, apiType);
this.writeFile('Cargo.toml', cargoToml);
const mainRs = this.generateRustMain(apiType);
this.writeFile('src/main.rs', mainRs);
}
async scaffoldGoProject(projectName, apiType) {
this.createDirectory(projectName);
process.chdir(projectName);
this.execCommand(`go mod init ${projectName}`);
const deps = this.getBackendDependencies('go', apiType);
if (deps.length > 0) {
this.execCommand(`go get ${deps.join(' ')}`);
}
const mainGo = this.generateGoMain(apiType);
this.writeFile('main.go', mainGo);
}
async scaffoldQuarkusProject(projectName, apiType) {
const extensions = this.getQuarkusExtensions(apiType);
this.execCommand(
`mvn io.quarkus.platform:quarkus-maven-plugin:create ` +
`-DprojectGroupId=com.example ` +
`-DprojectArtifactId=${projectName} ` +
`-Dextensions="${extensions}"`
);
}
// Helper methods
createSpinner(message) {
// Simple spinner implementation
return ora({
text: message,
spinner: 'dots',
color: 'cyan'
}).start();
}
execCommand(command) {
console.log(chalk.gray(`$ ${command}`));
execSync(command, { stdio: 'inherit' });
}
createDirectory(dirName) {
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, { recursive: true });
}
}
writeFile(filePath, content) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, content);
}
getBackendDependencies(framework, apiType) {
const deps = {
nestjs: {
rest: ['@nestjs/swagger', 'swagger-ui-express'],
graphql: ['@nestjs/graphql', '@nestjs/apollo', 'apollo-server-fastify', 'graphql'],
grpc: ['@nestjs/microservices', '@grpc/grpc-js', '@grpc/proto-loader'],
websocket: ['@nestjs/websockets', '@nestjs/platform-socket.io'],
all: ['@nestjs/swagger', '@nestjs/graphql', '@nestjs/apollo', '@nestjs/microservices', '@nestjs/websockets']
},
go: {
rest: ['github.com/gin-gonic/gin'],
graphql: ['github.com/99designs/gqlgen'],
grpc: ['google.golang.org/grpc'],
websocket: ['github.com/gorilla/websocket'],
all: ['github.com/gin-gonic/gin', 'github.com/99designs/gqlgen', 'google.golang.org/grpc', 'github.com/gorilla/websocket']
}
};
return deps[framework]?.[apiType] || [];
}
getQuarkusExtensions(apiType) {
const extensions = {
rest: 'resteasy-reactive,resteasy-reactive-jackson',
graphql: 'resteasy-reactive,smallrye-graphql',
grpc: 'grpc',
websocket: 'websockets',
all: 'resteasy-reactive,resteasy-reactive-jackson,smallrye-graphql,grpc,websockets'
};
return extensions[apiType] || extensions.rest;
}
createDockerCompose() {
const dockerCompose = `version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://localhost:3001
depends_on:
- backend
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "3001:3001"
environment:
- DATABASE_URL=postgresql://postgres:password@database:5432/myapp
depends_on:
- database
- redis
database:
image: postgres:15-alpine
ports:
- "5432:5432"
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
`;
this.writeFile('docker-compose.yml', dockerCompose);
}
createMonorepoConfig() {
const packageJson = {
name: "fullstack-app",
private: true,
workspaces: ["frontend", "backend", "mobile"],
scripts: {
"dev:frontend": "cd frontend && npm run dev",
"dev:backend": "cd backend && npm run start:dev",
"dev:mobile": "cd mobile && npm start",
"build": "npm run build:frontend && npm run build:backend",
"build:frontend": "cd frontend && npm run build",
"build:backend": "cd backend && npm run build"
}
};
this.writeFile('package.json', JSON.stringify(packageJson, null, 2));
}
createReadme(projectName) {
const readme = `# ${projectName}
A full-stack application built with modern technologies.
## Structure
- \`frontend/\` - Next.js application
- \`backend/\` - NestJS API server
- \`mobile/\` - React Native (Expo) mobile app
## Getting Started
### Development
1. Install dependencies:
\`\`\`bash
npm install
\`\`\`
2. Start all services:
\`\`\`bash
docker-compose up --build
\`\`\`
Or start individually:
\`\`\`bash
# Frontend
npm run dev:frontend
# Backend
npm run dev:backend
# Mobile
npm run dev:mobile
\`\`\`
## Services
- Frontend: http://localhost:3000
- Backend: http://localhost:3001
- API Documentation: http://localhost:3001/api
- Database: localhost:5432
- Redis: localhost:6379
## Technologies
### Frontend
- Next.js 14
- TypeScript
- Tailwind CSS
### Backend
- NestJS
- Fastify
- PostgreSQL
- Redis
### Mobile
- React Native
- Expo
`;
this.writeFile('README.md', readme);
}
showPostInstallInstructions(projectName, projectType) {
console.log(chalk.yellow('\n📋 Next Steps:'));
console.log(chalk.yellow('================\n'));
const instructions = {
expo: [
`cd ${projectName}`,
'npm start',
'',
'📱 Scan QR code with Expo Go app or press (i) for iOS simulator'
],
angular: [
`cd ${projectName}`,
'ng serve',
'',
'🌐 Open http://localhost:4200'
],
nextjs: [
`cd ${projectName}`,
'npm run dev',
'',
'🌐 Open http://localhost:3000'
],
nestjs: [
`cd ${projectName}`,
'npm run start:dev',
'',
'🌐 API: http://localhost:3001',
'📚 Docs: http://localhost:3001/api'
],
rust: [
`cd ${projectName}`,
'cargo run',
'',
'🌐 Open http://localhost:8080'
],
go: [
`cd ${projectName}`,
'go run main.go',
'',
'🌐 Open http://localhost:8080'
],
quarkus: [
`cd ${projectName}`,
'./mvnw quarkus:dev',
'',
'🌐 Open http://localhost:8080'
],
fullstack: [
`cd ${projectName}`,
'docker-compose up --build',
'',
'🌐 Frontend: http://localhost:3000',
'🌐 Backend: http://localhost:3001',
'📱 Mobile: Run "npm run dev:mobile" separately'
]
};
const steps = instructions[projectType] || [`cd ${projectName}`, 'Follow framework-specific instructions'];
steps.forEach(step => {
if (step === '') {
console.log();
} else if (step.startsWith('🌐') || step.startsWith('📚') || step.startsWith('📱')) {
console.log(chalk.green(step));
} else {
console.log(chalk.cyan(step));
}
});
console.log(chalk.magenta('\n✨ Happy coding! ✨'));
}
// Template generators (simplified versions)
createNestJSMain() {
const mainTs = `import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ logger: true })
);
// Enable CORS
await app.register(require('@fastify/cors'), {
origin: true,
credentials: true,
});
// Swagger Documentation
const config = new DocumentBuilder()
.setTitle('API Documentation')
.setDescription('Generated API documentation')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
const port = process.env.PORT || 3001;
await app.listen(port, '0.0.0.0');
console.log(\`🚀 Server running on http://localhost:\${port}\`);
console.log(\`📚 API Documentation: http://localhost:\${port}/api\`);
}
bootstrap();
`;
this.writeFile('src/main.ts', mainTs);
}
generateRustCargoToml(projectName, apiType) {
let dependencies = `[dependencies]
actix-web = "4.4"
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
env_logger = "0.10"`;
if (apiType === 'graphql' || apiType === 'all') {
dependencies += '\njuniper = "0.16"';
}
if (apiType === 'grpc' || apiType === 'all') {
dependencies += '\ntonic = "0.10"\nprost = "0.12"';
}
return `[package]
name = "${projectName}"
version = "0.1.0"
edition = "2021"
${dependencies}
`;
}
generateRustMain(apiType) {
return `use actix_web::{web, App, HttpResponse, HttpServer, Result, middleware::Logger};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct ApiResponse {
message: String,
status: String,
}
async fn health() -> Result<HttpResponse> {
Ok(HttpResponse::Ok().json(ApiResponse {
message: "Server is running".to_string(),
status: "OK".to_string(),
}))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init();
println!("🦀 Starting Rust server on http://localhost:8080");
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.route("/health", web::get().to(health))
.route("/", web::get().to(|| async {
HttpResponse::Ok().json(ApiResponse {
message: "Welcome to Rust API".to_string(),
status: "OK".to_string(),
})
}))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
`;
}
generateGoMain(apiType) {
return `package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type ApiResponse struct {
Message string \`json:"message"\`
Status string \`json:"status"\`
}
func main() {
r := gin.Default()
// CORS middleware
r.Use(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, ApiResponse{
Message: "Welcome to Go API",
Status: "OK",
})
})
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, ApiResponse{
Message: "Server is running",
Status: "OK",
})
})
println("🐹 Starting Go server on http://localhost:8080")
r.Run(":8080")
}
`;
}
createiOSStructure(projectName) {
// Create basic iOS project files
const files = {
'Package.swift': this.getiOSPackageSwift(projectName),
'Sources/App.swift': this.getiOSAppSwift(projectName),
'Sources/ContentView.swift': this.getiOSContentView(),
'README.md': `# ${projectName}\n\nAn iOS application built with SwiftUI.`
};
Object.entries(files).forEach(([path, content]) => {
this.writeFile(path, content);
});
}
createAndroidStructure(projectName) {
// Create basic Android project structure
const files = {
'app/src/main/kotlin/MainActivity.kt': this.getAndroidMainActivity(projectName),
'app/src/main/res/layout/activity_main.xml': this.getAndroidMainLayout(),
'app/build.gradle.kts': this.getAndroidBuildGradle(projectName),
'settings.gradle.kts': `rootProject.name = "${projectName}"\\ninclude(":app")`,
'README.md': `# ${projectName}\n\nAn Android application built with Kotlin.`
};
Object.entries(files).forEach(([path, content]) => {
this.writeFile(path, content);
});
}
// iOS Templates
getiOSPackageSwift(projectName) {
return `// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "${projectName}",
platforms: [
.iOS(.v16)
],
products: [
.library(name: "${projectName}", targets: ["${projectName}"])
],
targets: [
.target(name: "${projectName}")
]
)
`;
}
getiOSAppSwift(projectName) {
return `import SwiftUI
@main
struct ${projectName}App: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
`;
}
getiOSContentView() {
return `import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Image(systemName: "swift")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, SwiftUI!")
.font(.title)
.fontWeight(.bold)
Text("Welcome to your new iOS app")
.font(.subheadline)
.foregroundStyle(.secondary)
}
.padding()
}
}
#Preview {
ContentView()
}
`;
}
// Android Templates
getAndroidMainActivity(projectName) {
return `package com.example.${projectName.toLowerCase()}
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.${projectName.toLowerCase()}.ui.theme.${projectName}Theme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
${projectName}Theme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen()
}
}
}
}
}
@Composable
fun MainScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Hello, Android!",
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Welcome to your new Kotlin app",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@Preview(showBackground = true)
@Composable
fun MainScreenPreview() {
${projectName}Theme {
MainScreen()
}
}
`;
}
getAndroidMainLayout() {
return `<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, Android!"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
`;
}
getAndroidBuildGradle(projectName) {
return `plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.example.${projectName.toLowerCase()}"
compileSdk = 34
defaultConfig {
applicationId = "com.example.${projectName.toLowerCase()}"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.4"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.activity:activity-compose:1.8.2")
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
`;
}
// Web Templates
getWebPackageJson(projectName) {
return `{
"name": "${projectName.toLowerCase()}",
"version": "1.0.0",
"description": "A modern web application",
"main": "index.js",
"scripts": {
"start": "webpack serve --mode development",
"build": "webpack --mode production",
"test": "jest",
"lint": "eslint src/"
},
"keywords": ["web", "javascript", "webpack"],
"author": "HYDRA Software Engineering",
"license": "MIT",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/core": "^7.22.0",
"@babel/preset-env": "^7.22.0",
"@babel/preset-react": "^7.22.0",
"babel-loader": "^9.1.0",
"css-loader": "^6.8.0",
"html-webpack-plugin": "^5.5.0",
"style-loader": "^3.3.0",
"webpack": "^5.88.0",
"webpack-cli": "^5.1.0",
"webpack-dev-server": "^4.15.0",
"eslint": "^8.44.0",
"jest": "^29.6.0"
}
}
`;
}
getWebIndexHtml(projectName) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${projectName}</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
`;
}
getWebIndexJs() {
return `import React from 'react';
import ReactDOM from 'react-dom/client';
import './styles.css';
const App = () => {
return (
<div className="app">
<header className="app-header">
<h1>Welcome to Your Web App</h1>
<p>Built with React and Webpack</p>
<button className="cta-button">Get Started</button>
</header>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
`;
}
getWebStyles() {
return `* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.app {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.app-header {
text-align: center;
background: rgba(255, 255, 255, 0.95);
padding: 3rem 2rem;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
max-width: 600px;
}
.app-header h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.app-header p {
font-size: 1.2rem;
margin-bottom: 2rem;
color: #666;
}
.cta-button {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 30px;
font-size: 1.1rem;
border-radius: 50px;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
font-weight: 600;
}
.cta-button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
}
`;
}
getWebpackConfig() {
return `const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 3000,
hot: true,
open: true,
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
`;
}
// API Templates
getAPIPackageJson(projectName) {
return `{
"name": "${projectName.toLowerCase()}-api",
"version": "1.0.0",
"description": "RESTful API built with Node.js and Express",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest"
},
"keywords": ["api", "nodejs", "express", "rest"],
"author": "HYDRA Software Engineering",
"license": "MIT",
"dependencies": {
"express": "^4.18.0",
"cors": "^2.8.5",
"helmet": "^7.0.0",
"morgan": "^1.10.0",
"dotenv": "^16.3.0",
"mongoose": "^7.4.0"
},
"devDependencies": {
"nodemon": "^3.0.0",
"jest": "^29.6.0",
"supertest": "^6.3.0"
}
}
`;
}
getAPIServer() {
return `const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Routes
app.get('/', (req, res) => {
res.json({
message: 'Welcome to your API',
version: '1.0.0',
endpoints: {
health: '/health',
api: '/api/v1'
}
});
});
app.get('/health', (req, res) => {
res.json({
status: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
// API Routes
app.use('/api/v1', require('./routes/api'));
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'Something went wrong!',
message: process.env.NODE_ENV === 'development' ? err.message : 'Internal server error'
});
});
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({
error: 'Route not found',
path: req.originalUrl
});
});
app.listen(PORT, () => {
console.log(\`🚀 Server running on port \${PORT}\`);
console.log(\`📚 API documentation: http://localhost:\${PORT}\`);
});
module.exports = app;
`;
}
getAPIRoutes() {
return `const express = require('express');
const router = express.Router();
// Sample data (replace with database)
let items = [
{ id: 1, name: 'Item 1', description: 'First sample item' },
{ id: 2, name: 'Item 2', description: 'Second sample item' }
];
// GET /api/v1/items
router.get('/items', (req, res) => {
res.json({
success: true,
data: items,
count: items.length
});
});
// GET /api/v1/items/:id
router.get('/items/:id', (req, res) => {
const item = items.find(i => i.id === parseInt(req.params.id));
if (!item) {
return res.status(404).json({
success: false,
error: 'Item not found'
});
}
res.json({
success: true,
data: item
});
});
// POST /api/v1/items
router.post('/items', (req, res) => {
const { name, description } = req.body;
if (!name) {
return res.status(400).json({
success: false,
error: 'Name is required'
});
}
const newItem = {
id: items.length + 1,
name,
description: description || ''
};
items.push(newItem);
res.status(201).json({
success: true,
data: newItem
});
});
// PUT /api/v1/items/:id
router.put('/items/:id', (req, res) => {
const itemIndex = items.findIndex(i => i.id === parseInt(req.params.id));
if (itemIndex === -1) {
return res.status(404).json({
success: false,
error: 'Item not found'
});
}
const { name, description } = req.body;
items[itemIndex] = {
...items[itemIndex],
name: name || items[itemIndex].name,
description: description !== undefined ? description : items[itemIndex].description
};
res.json({
success: true,
data: items[itemIndex]
});
});
// DELETE /api/v1/items/:id
router.delete('/items/:id', (req, res) => {
const itemIndex = items.findIndex(i => i.id === parseInt(req.params.id));
if (itemIndex === -1) {
return res.status(404).json({
success: false,
error: 'Item not found'
});
}
items.splice(itemIndex, 1);
res.json({
success: true,
message: 'Item deleted successfully'
});
});
module.exports = router;
`;
}
// Desktop Templates (Electron)
getDesktopPackageJson(projectName) {
return `{
"name": "${projectName.toLowerCase()}-desktop",
"version": "1.0.0",
"description": "Desktop application built with Electron",
"main": "main.js",
"scripts": {
"start": "electron .",
"dev": "electron . --dev",
"build": "electron-builder",
"build-win": "electron-builder --win",
"build-mac": "electron-builder --mac",
"build-linux": "electron-builder --linux"
},
"keywords": ["electron", "desktop", "cross-platform"],
"author": "HYDRA Software Engineering",
"license": "MIT",
"dependencies": {
"electron": "^25.3.0"
},
"devDependencies": {
"electron-builder": "^24.6.3"
},
"build": {
"appId": "com.example.${projectName.toLowerCase()}",
"productName": "${projectName}",
"directories": {
"output": "dist"
},
"files": [
"main.js",
"renderer.js",
"styles.css",
"index.html"
]
}
}
`;
}
getDesktopMain() {
return `const { app, BrowserWindow, Menu } = require('electron');
const path = require('path');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
},
icon: path.join(__dirname, 'assets/icon.png'),
titleBarStyle: 'default',
show: false
});
mainWindow.loadFile('index.html');
// Show window when ready
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
// Open DevTools in development
if (process.argv.includes('--dev')) {
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// Create application menu
const template = [
{
label: 'File',
submenu: [
{
label: 'New',
accelerator: 'CmdOrCtrl+N',
click: () => {
console.log('New file');
}
},
{
label: 'Open',
accelerator: 'CmdOrCtrl+O',
click: () => {
console.log('Open file');
}
},
{ type: 'separator' },
{
role: 'quit'
}
]
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' }
]
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'actualSize' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]
},
{
label: 'Help',
submenu: [
{
label: 'About',
click: () => {
console.log('About');
}
}
]
}
];
app.whenReady().then(() => {
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
`;
}
getDesktopRenderer() {
return `// Renderer process code
document.addEventListener('DOMContentLoaded', () => {
console.log('Desktop app loaded!');
// Add click event to the main button
const button = document.getElementById('main-button');
if (button) {
button.addEventListener('click', () => {
button.textContent = 'Clicked!';
setTimeout(() => {
button.textContent = 'Click me!';
}, 1000);
});
}
// Display system info
const systemInfo = document.getElementById('system-info');
if (systemInfo) {
const info = {
platform: process.platform,
arch: process.arch,
nodeVersion: process.versions.node,
electronVersion: process.versions.electron
};
systemInfo.innerHTML = \`
<h3>System Information</h3>
<p><strong>Platform:</strong> \${info.platform}</p>
<p><strong>Architecture:</strong> \${info.arch}</p>
<p><strong>Node.js:</strong> \${info.nodeVersion}</p>
<p><strong>Electron:</strong> \${info.electronVersion}</p>
\`;
}
});
`;
}
getDesktopHTML(projectName) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${projectName}</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<header class="app-header">
<h1>Welcome to ${projectName}</h1>
<p>Your cross-platform desktop application</p>
</header>
<main class="app-main">
<div class="card">
<h2>Getting Started</h2>
<p>This is your Electron desktop application. You can build cross-platform apps with web technologies.</p>
<button id="main-button" class="btn-primary">Click me!</button>
</div>
<div class="card" id="system-info">
<!-- System info will be populated by renderer.js -->
</div>
</main>
<footer class="app-footer">
<p>Built with ❤️ using Electron</p>
</footer>
</div>
<script src="renderer.js"></script>
</body>
</html>
`;
}
getDesktopCSS() {
return `* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.app-header {
text-align: center;
padding: 2rem 0;
}
.app-header h1 {
font-size: 3rem;
margin-bottom: 0.5rem;
color: white;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
.app-header p {
font-size: 1.3rem;
color: rgba(255, 255, 255, 0.9);
}
.app-main {
flex: 1;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 2rem;
margin: 2rem 0;
}
.card {
background: rgba(255, 255, 255, 0.95);
padding: 2rem;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card h2 {
color: #333;
margin-bottom: 1rem;
font-size: 1.8rem;
}
.card h3 {
color: #555;
margin-bottom: 1rem;
font-size: 1.4rem;
}
.card p {
color: #666;
margin-bottom: 1.5rem;
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 24px;
font-size: 1rem;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
}
.app-footer {
text-align: center;
padding: 1rem 0;
color: rgba(255, 255, 255, 0.8);
border-top: 1px solid rgba(255, 255, 255, 0.1);
margin-top: 2rem;
}
`;
}
// Template Generators
generateWebProject(projectName) {
console.log(chalk.blue('📦 Creating Web project structure...'));
const files = {
'package.json': this.getWebPackageJson(projectName),
'webpack.config.js': this.getWebpackConfig(),
'public/index.html': this.getWebIndexHtml(projectName),
'src/index.js': this.getWebIndexJs(),
'src/styles.css': this.getWebStyles(),
'.gitignore': this.getGitignore('web'),
'README.md': this.getWebReadme(projectName)
};
Object.entries(files).forEach(([path, content]) => {
this.writeFile(path, content);
});
console.log(chalk.green('✅ Web project created successfully!'));
this.showWebInstructions(projectName);
}
generateMobileProject(projectName, platform) {
console.log(chalk.blue(`📱 Creating ${platform} mobile project structure...`));
if (platform === 'ios') {
this.createiOSStructure(projectName);
this.showIOSInstructions(projectName);
} else if (platform === 'android') {
this.createAndroidStructure(projectName);
this.showAndroidInstructions(projectName);
} else {
// React Native
this.createReactNativeStructure(projectName);
this.showReactNativeInstructions(projectName);
}
console.log(chalk.green('✅ Mobile project created successfully!'));
}
generateDesktopProject(projectName) {
console.log(chalk.blue('🖥️ Creating Desktop project structure...'));
const files = {
'package.json': this.getDesktopPackageJson(projectName),
'main.js': this.getDesktopMain(),
'renderer.js': this.getDesktopRenderer(),
'index.html': this.getDesktopHTML(projectName),
'styles.css': this.getDesktopCSS(),
'.gitignore': this.getGitignore('desktop'),
'README.md': this.getDesktopReadme(projectName)
};
Object.entries(files).forEach(([path, content]) => {
this.writeFile(path, content);
});
console.log(chalk.green('✅ Desktop project created successfully!'));
this.showDesktopInstructions(projectName);
}
generateAPIProject(projectName) {
console.log(chalk.blue('🚀 Creating API project structure...'));
const files = {
'package.json': this.getAPIPackageJson(projectName),
'server.js': this.getAPIServer(),
'routes/api.js': this.getAPIRoutes(),
'.env': this.getEnvFile(),
'.gitignore': this.getGitignore('api'),
'README.md': this.getAPIReadme(projectName)
};
Object.entries(files).forEach(([path, content]) => {
this.writeFile(path, content);
});
console.log(chalk.green('✅ API project created successfully!'));
this.showAPIInstructions(projectName);
}
generateFullStackProject(projectName) {
console.log(chalk.blue('🌐 Creating Full-Stack project structure...'));
// Create client and server directories
const clientFiles = {
'client/package.json': this.getWebPackageJson(projectName + '-client'),
'client/webpack.config.js': this.getWebpackConfig(),
'client/public/index.html': this.getWebIndexHtml(projectName),
'client/src/index.js': this.getFullStackClientJS(),
'client/src/styles.css': this.getWebStyles(),
'client/.gitignore': this.getGitignore('web')
};
const serverFiles = {
'server/package.json': this.getAPIPackageJson(projectName + '-server'),
'server/server.js': this.getAPIServer(),
'server/routes/api.js': this.getAPIRoutes(),
'server/.env': this.getEnvFile(),
'server/.gitignore': this.getGitignore('api')
};
const rootFiles = {
'package.json': this.getFullStackRootPackage(projectName),
'README.md': this.getFullStackReadme(projectName),
'.gitignore': this.getGitignore('fullstack')
};
[...Object.entries(clientFiles), ...Object.entries(serverFiles), ...Object.entries(rootFiles)]
.forEach(([path, content]) => {
this.writeFile(path, content);
});
console.log(chalk.green('✅ Full-Stack project created successfully!'));
this.showFullStackInstructions(projectName);
}
// React Native structure
createReactNativeStructure(projectName) {
const files = {
'package.json': this.getReactNativePackage(projectName),
'App.js': this.getReactNativeApp(),
'index.js': this.getReactNativeIndex