Docker Local Development Environment Generator Overview This skill helps you create optimized Docker development environments for your projects. It generates , , and related configurations through an interactive, question-driven workflow. When to use this skill: - Setting up a new Docker development environment - Dockerizing an existing project for local development - Adding services (database, Redis, email testing) to your Docker setup - Updating or merging with existing Docker configurations Key Principle: This skill ALWAYS asks questions before making decisions. You will be notified about…

,\n '^127\\.0\\.0\\.1

Docker Local Development Environment Generator Overview This skill helps you create optimized Docker development environments for your projects. It generates , , and related configurations through an interactive, question-driven workflow. When to use this skill: - Setting up a new Docker development environment - Dockerizing an existing project for local development - Adding services (database, Redis, email testing) to your Docker setup - Updating or merging with existing Docker configurations Key Principle: This skill ALWAYS asks questions before making decisions. You will be notified about…

,\n '^.+\\.local

Docker Local Development Environment Generator Overview This skill helps you create optimized Docker development environments for your projects. It generates , , and related configurations through an interactive, question-driven workflow. When to use this skill: - Setting up a new Docker development environment - Dockerizing an existing project for local development - Adding services (database, Redis, email testing) to your Docker setup - Updating or merging with existing Docker configurations Key Principle: This skill ALWAYS asks questions before making decisions. You will be notified about…

,\n];\n```\n\n### development.services.yml\n\nCreate `sites/development.services.yml`:\n\n```yaml\nparameters:\n http.response.debug_cacheability_headers: true\n twig.config:\n debug: true\n auto_reload: true\n cache: false\n\nservices:\n cache.backend.null:\n class: Drupal\\Core\\Cache\\NullBackendFactory\n```\n\n### Drush Commands\n\n```bash\n# Clear cache\ndrush cr\n\n# Run database updates\ndrush updb\n\n# Install a module\ndrush en module_name\n\n# Generate one-time login link\ndrush uli\n```\n\n## Joomla Setup\n\n### Docker Compose Service\n\n```yaml\napp:\n build:\n context: .\n dockerfile: Dockerfile\n volumes:\n - ./:/var/www/html\n depends_on:\n - db\n environment:\n JOOMLA_DB_HOST: db\n JOOMLA_DB_NAME: ${DB_DATABASE:-joomla}\n JOOMLA_DB_USER: ${DB_USERNAME:-joomla}\n JOOMLA_DB_PASSWORD: ${DB_PASSWORD:-joomla}\n```\n\n### Dockerfile\n\n```dockerfile\nFROM php:8.2-fpm\n\n# Install Joomla required extensions\nRUN apt-get update && apt-get install -y \\\n libpng-dev libjpeg-dev libfreetype6-dev \\\n libzip-dev libicu-dev libxml2-dev \\\n && docker-php-ext-configure gd --with-freetype --with-jpeg \\\n && docker-php-ext-install -j$(nproc) \\\n gd mysqli pdo_mysql zip intl xml opcache\n\nWORKDIR /var/www/html\n```\n\n### configuration.php for Docker\n\n```php\n\u003c?php\nclass JConfig {\n // Database\n public $dbtype = 'mysqli';\n public $host = 'db';\n public $user = 'joomla';\n public $password = 'joomla';\n public $db = 'joomla';\n public $dbprefix = 'jos_';\n public $dbencryption = 0;\n public $dbsslverifyservercert = false;\n public $dbsslkey = '';\n public $dbsslcert = '';\n public $dbsslca = '';\n public $dbsslcipher = '';\n\n // Site\n public $sitename = 'Joomla Development';\n public $secret = 'change-this-secret-key';\n\n // Debug\n public $debug = true;\n public $debug_lang = true;\n\n // Error reporting\n public $error_reporting = 'maximum';\n\n // Logging\n public $log_path = '/var/www/html/administrator/logs';\n public $tmp_path = '/var/www/html/tmp';\n\n // Cache\n public $caching = 0;\n public $cache_handler = 'file';\n public $cachetime = 15;\n public $cache_platformprefix = false;\n\n // Session\n public $session_handler = 'database';\n public $lifetime = 15;\n\n // Mail (use Mailpit)\n public $mailer = 'smtp';\n public $mailfrom = 'admin@localhost';\n public $fromname = 'Joomla';\n public $sendmail = '/usr/sbin/sendmail';\n public $smtpauth = false;\n public $smtpuser = '';\n public $smtppass = '';\n public $smtphost = 'mailpit';\n public $smtpsecure = 'none';\n public $smtpport = 1025;\n}\n```\n\n### Nginx Configuration for Joomla\n\n```nginx\nserver {\n listen 80;\n server_name localhost;\n root /var/www/html;\n index index.php index.html;\n\n client_max_body_size 100M;\n\n # Joomla SEF URLs\n location / {\n try_files $uri $uri/ /index.php?$args;\n }\n\n # PHP handling\n location ~ \\.php$ {\n fastcgi_pass app:9000;\n fastcgi_index index.php;\n fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n include fastcgi_params;\n }\n\n # Deny access to sensitive files\n location ~ /\\.ht {\n deny all;\n }\n\n location = /configuration.php {\n deny all;\n }\n\n location ~ ^/administrator/logs/ {\n deny all;\n }\n}\n```\n\n## Common CMS Tips\n\n### File Permissions\n\n```bash\n# Inside container, set proper permissions\nchown -R www-data:www-data /var/www/html\nfind /var/www/html -type d -exec chmod 755 {} \\;\nfind /var/www/html -type f -exec chmod 644 {} \\;\n\n# Writable directories\nchmod -R 775 /var/www/html/wp-content/uploads # WordPress\nchmod -R 775 /var/www/html/sites/default/files # Drupal\nchmod -R 775 /var/www/html/images # Joomla\nchmod -R 775 /var/www/html/tmp # Joomla\n```\n\n### Database Import\n\n```bash\n# MySQL/MariaDB\ndocker compose exec db mysql -u root -p database_name \u003c dump.sql\n\n# Or using the app container\ndocker compose exec app mysql -h db -u root -p database_name \u003c dump.sql\n```\n\n### WP-CLI in Docker\n\n```bash\n# Run WP-CLI commands\ndocker compose exec app wp plugin list\ndocker compose exec app wp core update\ndocker compose exec app wp cache flush\n\n# Import database\ndocker compose exec app wp db import dump.sql\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11114,"content_sha256":"e39d535af1be7c8cf6920dc338ffa0f997282b68a1912f0766893458f8581eef"},{"filename":"references/health-check-patterns.md","content":"# Health Check Patterns\n\nService verification patterns for all supported stacks.\n\n## Overview\n\nHealth checks run automatically after `docker compose up` to verify all services are working correctly.\n\n## Database Checks\n\n### MySQL/MariaDB\n\n**Connection check:**\n```bash\ndocker compose exec db mysqladmin ping -h localhost -u root --silent\n```\n\n**Expected output:** `mysqld is alive`\n\n**CRUD test:**\n```sql\n-- Create test table\nCREATE TABLE IF NOT EXISTS _health_check_test (\n id INT AUTO_INCREMENT PRIMARY KEY,\n value VARCHAR(255),\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Insert\nINSERT INTO _health_check_test (value) VALUES ('test');\n\n-- Update\nUPDATE _health_check_test SET value = 'updated' WHERE value = 'test';\n\n-- Select (verify)\nSELECT COUNT(*) FROM _health_check_test WHERE value = 'updated';\n-- Expected: 1\n\n-- Delete\nDELETE FROM _health_check_test WHERE value = 'updated';\n\n-- Cleanup\nDROP TABLE IF EXISTS _health_check_test;\n```\n\n### PostgreSQL\n\n**Connection check:**\n```bash\ndocker compose exec db pg_isready -U postgres\n```\n\n**Expected output:** `localhost:5432 - accepting connections`\n\n**CRUD test:**\n```sql\n-- Create test table\nCREATE TABLE IF NOT EXISTS _health_check_test (\n id SERIAL PRIMARY KEY,\n value VARCHAR(255),\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Insert\nINSERT INTO _health_check_test (value) VALUES ('test');\n\n-- Update\nUPDATE _health_check_test SET value = 'updated' WHERE value = 'test';\n\n-- Select (verify)\nSELECT COUNT(*) FROM _health_check_test WHERE value = 'updated';\n-- Expected: 1\n\n-- Delete\nDELETE FROM _health_check_test WHERE value = 'updated';\n\n-- Cleanup\nDROP TABLE IF EXISTS _health_check_test;\n```\n\n## Web Server Checks\n\n### Nginx\n\n**HTTP request:**\n```bash\ncurl -s -o /dev/null -w '%{http_code}' http://localhost:8080\n```\n\n**Expected:** `200`, `301`, or `302`\n\n**Headers check:**\n```bash\ncurl -I http://localhost:8080\n```\n\n**Check configuration:**\n```bash\ndocker compose exec nginx nginx -t\n```\n\n## Redis Checks\n\n**Ping test:**\n```bash\ndocker compose exec redis redis-cli ping\n```\n\n**Expected:** `PONG`\n\n**Set/Get test:**\n```bash\ndocker compose exec redis redis-cli SET health_check test\ndocker compose exec redis redis-cli GET health_check\ndocker compose exec redis redis-cli DEL health_check\n```\n\n**Memory info:**\n```bash\ndocker compose exec redis redis-cli INFO memory | grep used_memory_human\n```\n\n## Email Service Checks\n\n### Mailpit\n\n**Web UI check:**\n```bash\ncurl -s -o /dev/null -w '%{http_code}' http://localhost:8025\n```\n\n**Expected:** `200`\n\n**SMTP connection:**\n```bash\nnc -z localhost 1025 && echo \"SMTP OK\" || echo \"SMTP FAILED\"\n```\n\n**API check:**\n```bash\ncurl -s http://localhost:8025/api/v1/messages | head -c 100\n```\n\n### MailHog\n\n**Web UI check:**\n```bash\ncurl -s -o /dev/null -w '%{http_code}' http://localhost:8025\n```\n\n**API check:**\n```bash\ncurl -s http://localhost:8025/api/v2/messages | head -c 100\n```\n\n## Application Checks\n\n### Laravel\n\n**Artisan check:**\n```bash\ndocker compose exec app php artisan --version\n```\n\n**Expected:** `Laravel Framework X.X.X`\n\n**Environment check:**\n```bash\ndocker compose exec app php artisan about\n```\n\n**Database connection:**\n```bash\ndocker compose exec app php artisan db:show\n```\n\n**Cache check:**\n```bash\ndocker compose exec app php artisan cache:clear\n```\n\n**Queue check (if enabled):**\n```bash\ndocker compose exec app php artisan queue:work --once\n```\n\n### WordPress\n\n**WP-CLI version:**\n```bash\ndocker compose exec app wp --version\n```\n\n**Core version:**\n```bash\ndocker compose exec app wp core version\n```\n\n**Database check:**\n```bash\ndocker compose exec app wp db check\n```\n\n**Core checksums:**\n```bash\ndocker compose exec app wp core verify-checksums\n```\n\n**Plugin list:**\n```bash\ndocker compose exec app wp plugin list\n```\n\n### Drupal\n\n**Drush status:**\n```bash\ndocker compose exec app drush status\n```\n\n**Database check:**\n```bash\ndocker compose exec app drush sql:query \"SELECT 1\"\n```\n\n**Cache rebuild:**\n```bash\ndocker compose exec app drush cr\n```\n\n### Django\n\n**Check command:**\n```bash\ndocker compose exec app python manage.py check\n```\n\n**Expected:** `System check identified no issues`\n\n**Database check:**\n```bash\ndocker compose exec app python manage.py dbshell -c \"SELECT 1\"\n```\n\n**Migration status:**\n```bash\ndocker compose exec app python manage.py showmigrations\n```\n\n### FastAPI\n\n**Health endpoint:**\n```bash\ncurl -s http://localhost:8000/health\n# or\ncurl -s http://localhost:8000/docs\n```\n\n**OpenAPI docs:**\n```bash\ncurl -s -o /dev/null -w '%{http_code}' http://localhost:8000/docs\n```\n\n**Expected:** `200`\n\n### Node.js/Express\n\n**HTTP check:**\n```bash\ncurl -s -o /dev/null -w '%{http_code}' http://localhost:3000\n# or health endpoint\ncurl -s http://localhost:3000/health\n```\n\n**Node version:**\n```bash\ndocker compose exec app node -v\n```\n\n## Queue Worker Checks\n\n### Supervisor Process\n\n**Check running processes:**\n```bash\ndocker compose exec worker supervisorctl status\n```\n\n**Expected output:**\n```\nlaravel-worker:laravel-worker_00 RUNNING pid 123, uptime 0:05:00\nlaravel-scheduler RUNNING pid 124, uptime 0:05:00\n```\n\n### Laravel Horizon\n\n**Horizon status:**\n```bash\ndocker compose exec app php artisan horizon:status\n```\n\n### Celery\n\n**Worker status:**\n```bash\ndocker compose exec worker celery -A myapp status\n```\n\n**Inspect active:**\n```bash\ndocker compose exec worker celery -A myapp inspect active\n```\n\n## Full Stack Integration Test\n\n### Test Sequence\n\n```bash\n#!/bin/bash\n\necho \"=== Full Stack Health Check ===\"\n\n# 1. Database\necho -n \"Database... \"\ndocker compose exec -T db mysqladmin ping -u root --silent && echo \"OK\" || echo \"FAIL\"\n\n# 2. Redis\necho -n \"Redis... \"\ndocker compose exec -T redis redis-cli ping | grep -q PONG && echo \"OK\" || echo \"FAIL\"\n\n# 3. Web Server\necho -n \"Web Server... \"\ncurl -s -o /dev/null -w '%{http_code}' http://localhost:8080 | grep -qE '200|301|302' && echo \"OK\" || echo \"FAIL\"\n\n# 4. Application\necho -n \"Application... \"\ndocker compose exec -T app php artisan --version > /dev/null 2>&1 && echo \"OK\" || echo \"FAIL\"\n\n# 5. Database CRUD\necho -n \"Database CRUD... \"\n./scripts/db-test.sh > /dev/null 2>&1 && echo \"OK\" || echo \"FAIL\"\n\n# 6. Mail Service\necho -n \"Mail Service... \"\ncurl -s -o /dev/null -w '%{http_code}' http://localhost:8025 | grep -q 200 && echo \"OK\" || echo \"FAIL\"\n\necho \"=== Complete ===\"\n```\n\n## Common Failure Scenarios\n\n### Database Connection Refused\n\n**Symptoms:**\n- `Connection refused` error\n- `Can't connect to MySQL server`\n\n**Checks:**\n```bash\n# Is container running?\ndocker compose ps db\n\n# Check logs\ndocker compose logs db\n\n# Is port exposed?\ndocker compose port db 3306\n```\n\n**Solutions:**\n1. Wait longer for database to start\n2. Check environment variables\n3. Check healthcheck in compose file\n\n### Redis Not Ready\n\n**Symptoms:**\n- `Could not connect to Redis`\n- `Connection refused`\n\n**Checks:**\n```bash\ndocker compose exec redis redis-cli ping\ndocker compose logs redis\n```\n\n### Web Server 502 Bad Gateway\n\n**Symptoms:**\n- Nginx returns 502\n- Can't reach PHP-FPM\n\n**Checks:**\n```bash\n# Is PHP-FPM running?\ndocker compose exec app php-fpm -t\n\n# Check Nginx config\ndocker compose exec nginx nginx -t\n\n# Check Nginx logs\ndocker compose logs nginx\n```\n\n### Queue Worker Not Processing\n\n**Symptoms:**\n- Jobs stay in queue\n- Worker shows as running but not processing\n\n**Checks:**\n```bash\n# Check supervisor status\ndocker compose exec worker supervisorctl status\n\n# Check worker logs\ndocker compose logs worker\n\n# Manually run job\ndocker compose exec app php artisan queue:work --once\n```\n\n## Health Check in docker-compose.yml\n\n### Adding Health Checks\n\n```yaml\nservices:\n db:\n image: mysql:8.0\n healthcheck:\n test: [\"CMD\", \"mysqladmin\", \"ping\", \"-h\", \"localhost\"]\n interval: 10s\n timeout: 5s\n retries: 3\n start_period: 30s\n\n redis:\n image: redis:alpine\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 3\n\n app:\n depends_on:\n db:\n condition: service_healthy\n redis:\n condition: service_healthy\n```\n\n### Waiting for Health\n\n```bash\n# Wait for all services to be healthy\ndocker compose up -d --wait\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8206,"content_sha256":"0163c592dc062b535712b870c912742ff1be0ec451cfa49f9c5b9098656e69e2"},{"filename":"references/merge-backup-strategy.md","content":"# Merge & Backup Strategy\n\nHow to handle existing Docker files safely.\n\n## Backup Protocol\n\n### Timestamped Backup Naming\n\nFormat: `{filename}.backup.{YYYY-MM-DD-HHMMSS}`\n\nExamples:\n- `docker-compose.yml.backup.2024-01-15-143022`\n- `Dockerfile.backup.2024-01-15-143022`\n\n### Backup Location\n\nBackups are created in the same directory as the original file.\n\n```bash\n# Original\n./docker-compose.yml\n\n# Backup\n./docker-compose.yml.backup.2024-01-15-143022\n```\n\n### Creating Backups\n\n```bash\n# Bash function to backup file\nbackup_file() {\n local file=$1\n local timestamp=$(date +%Y-%m-%d-%H%M%S)\n cp \"$file\" \"${file}.backup.${timestamp}\"\n echo \"Backed up: ${file}.backup.${timestamp}\"\n}\n```\n\n### Backup Retention\n\nKeep the last 3 backups per file. Older backups can be removed:\n\n```bash\n# List backups sorted by date\nls -la docker-compose.yml.backup.* | sort -r\n\n# Keep only last 3\nls -t docker-compose.yml.backup.* | tail -n +4 | xargs rm -f\n```\n\n## Merge Algorithm\n\n### Service-Level Merging\n\nThe skill merges at the service level, not line-by-line:\n\n1. **Parse existing docker-compose.yml**\n2. **Identify existing services**\n3. **Only add NEW services**\n4. **Preserve existing service configurations**\n\n### Merge Rules\n\n| Scenario | Action |\n|----------|--------|\n| Service exists | Keep existing, don't modify |\n| Service missing | Add from template |\n| Network exists | Keep existing |\n| Network missing | Add from template |\n| Volume exists | Keep existing |\n| Volume missing | Add from template |\n| Top-level config | Preserve existing |\n\n### Example Merge\n\n**Existing docker-compose.yml:**\n```yaml\nversion: '3.8'\n\nservices:\n app:\n build: .\n volumes:\n - ./:/var/www\n\n db:\n image: mysql:8.0\n environment:\n MYSQL_ROOT_PASSWORD: custom_password # User's custom config\n```\n\n**Skill wants to add:**\n```yaml\nservices:\n redis:\n image: redis:alpine\n\n mailpit:\n image: axllent/mailpit\n```\n\n**Result after merge:**\n```yaml\nversion: '3.8'\n\nservices:\n app:\n build: .\n volumes:\n - ./:/var/www\n # Original config preserved\n\n db:\n image: mysql:8.0\n environment:\n MYSQL_ROOT_PASSWORD: custom_password # Preserved!\n\n # NEW services added\n redis:\n image: redis:alpine\n\n mailpit:\n image: axllent/mailpit\n```\n\n### Environment Variable Preservation\n\nAlways preserve user's environment variables:\n\n```yaml\n# Before merge - user has custom vars\ndb:\n environment:\n MYSQL_ROOT_PASSWORD: my_secret_pass\n CUSTOM_VAR: my_value\n\n# After merge - variables preserved\ndb:\n environment:\n MYSQL_ROOT_PASSWORD: my_secret_pass\n CUSTOM_VAR: my_value\n # No new vars added to existing service\n```\n\n### Volume Preservation\n\n```yaml\n# User's custom volumes - preserved\nservices:\n app:\n volumes:\n - ./custom/path:/var/www\n - ~/.ssh:/root/.ssh:ro # Custom mount\n\n# Not replaced with template defaults\n```\n\n### Network Preservation\n\n```yaml\n# User's custom network\nnetworks:\n my_custom_network:\n driver: bridge\n ipam:\n config:\n - subnet: 172.28.0.0/16\n\n# Preserved, not replaced\n```\n\n## Conflict Resolution\n\n### Show Diff Before Applying\n\nAlways show the user what will change:\n\n```diff\n services:\n app:\n # existing config unchanged\n+\n+ redis:\n+ image: redis:alpine\n+ volumes:\n+ - redis_data:/data\n+\n+ mailpit:\n+ image: axllent/mailpit\n+ ports:\n+ - \"1025:1025\"\n+ - \"8025:8025\"\n+\n volumes:\n+ redis_data:\n```\n\n### User Options\n\nPresent these options when existing files found:\n\n```\nI found existing Docker files. How should I proceed?\n\n1. Merge (add new services, preserve your settings)\n - Your customizations will be kept\n - Only NEW services will be added\n - Backup will be created first\n\n2. Replace (backup existing, generate fresh)\n - Existing files will be backed up\n - New files generated from templates\n - You'll need to re-apply customizations\n\n3. Cancel (let me review first)\n - No changes will be made\n - You can review files manually\n```\n\n### Resolving Specific Conflicts\n\n**Port Conflict:**\n```\nService 'nginx' already uses port 8080.\nNew service 'proxy' also wants port 8080.\n\nOptions:\n1. Use different port for 'proxy' (8081)\n2. Keep existing 'nginx' port, skip 'proxy'\n3. Cancel and resolve manually\n```\n\n**Service Name Conflict:**\n```\nTemplate has 'app' service but one already exists.\n\nOptions:\n1. Keep existing 'app' configuration\n2. Rename new service to 'app_new'\n3. Cancel and resolve manually\n```\n\n## Rollback Procedure\n\n### Manual Rollback\n\n```bash\n# Find backup\nls -la *.backup.*\n\n# Restore from backup\ncp docker-compose.yml.backup.2024-01-15-143022 docker-compose.yml\n\n# Verify\ndocker compose config\n```\n\n### Automated Rollback Check\n\n```bash\n# After generating new files, verify they're valid\nif ! docker compose config > /dev/null 2>&1; then\n echo \"Error: Generated config is invalid\"\n echo \"Rolling back...\"\n cp docker-compose.yml.backup.* docker-compose.yml\n exit 1\nfi\n```\n\n### Verification After Merge\n\n1. **Syntax check:**\n ```bash\n docker compose config\n ```\n\n2. **Service check:**\n ```bash\n docker compose config --services\n ```\n\n3. **Test containers start:**\n ```bash\n docker compose up -d\n docker compose ps\n ```\n\n## Best Practices\n\n### Always Backup First\n\n```bash\n# Before any modification\nbackup_file docker-compose.yml\nbackup_file Dockerfile\nbackup_file .env\n```\n\n### Validate Before Applying\n\n```bash\n# Check YAML syntax\ndocker compose config > /dev/null\n\n# Check Dockerfile syntax\ndocker build --check .\n```\n\n### Use Version Control\n\n```bash\n# Commit before changes\ngit add docker-compose.yml Dockerfile\ngit commit -m \"Backup before Docker skill updates\"\n\n# Easy rollback\ngit checkout docker-compose.yml\n```\n\n### Document Custom Changes\n\nAdd comments to mark customizations:\n\n```yaml\nservices:\n db:\n environment:\n # CUSTOM: Using legacy password format\n MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}\n # CUSTOM: Company-specific variable\n COMPANY_CODE: ACME\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5991,"content_sha256":"1718c029cf81700e63b04dd19adb314baf755040a308c4d36cd489ce84f3616d"},{"filename":"references/networking-ports-guide.md","content":"# Networking & Ports Guide\n\nConfiguration guide for port management and Docker networking.\n\n## Port Selection Strategy\n\n### Common Development Port Ranges\n\n| Service | Default | Range | Notes |\n|---------|---------|-------|-------|\n| HTTP | 8080 | 8080-8099 | Web server |\n| HTTPS | 8443 | 8443-8499 | SSL (if needed) |\n| MySQL | 3306 | 3306-3399 | Database |\n| PostgreSQL | 5432 | 5432-5499 | Database |\n| Redis | 6379 | 6379-6399 | Cache |\n| Mail SMTP | 1025 | 1025-1099 | Email testing |\n| Mail UI | 8025 | 8025-8099 | Email web interface |\n| PHP-FPM | 9000 | 9000-9099 | (Usually not exposed) |\n\n### Checking Port Availability\n\n**Using lsof:**\n```bash\nlsof -i :8080\n```\n\n**Using netcat:**\n```bash\nnc -z localhost 8080 && echo \"in use\" || echo \"available\"\n```\n\n**Using ss:**\n```bash\nss -tuln | grep :8080\n```\n\n### Finding Available Ports\n\nUse the `port-check.sh` script:\n\n```bash\n# Check common ports\n./scripts/port-check.sh check\n\n# Get suggested available ports (JSON)\n./scripts/port-check.sh suggest\n\n# Verify specific ports\n./scripts/port-check.sh verify 8080 3306 6379\n\n# Find first available in range\n./scripts/port-check.sh find 8080 8099\n```\n\n## Port Exposure Options\n\n### Reverse Proxy Detection\n\nBefore deciding on port exposure, run the network detection script:\n\n```bash\n./scripts/detect-network.sh\n```\n\nThis script detects:\n- Running reverse proxy containers (Nginx Proxy Manager, Traefik, Caddy)\n- Available Docker networks\n- Suggested network to join\n\n**If reverse proxy detected:**\n- Recommend internal-only ports (no host exposure)\n- Suggest connecting to the proxy's network\n- Only expose database port for SQL tools (optional)\n\n### Internal Only (For Reverse Proxy Users)\n\nWhen using Nginx Proxy Manager, Traefik, or other reverse proxies:\n\n```yaml\nservices:\n nginx:\n # No ports exposed - proxy handles routing\n networks:\n - default\n - proxy_network # Shared with reverse proxy\n\n db:\n # Optionally expose for SQL tools\n ports:\n - \"${DB_PORT:-3306}:3306\"\n\n app:\n # No ports exposed\n\n redis:\n # No ports exposed\n\n mailpit:\n # No ports exposed - access via proxy or internal network\n```\n\n**Benefits:**\n- No port conflicts\n- Clean architecture\n- Proxy handles SSL, domains, routing\n\n### Minimal Exposure (Recommended for Standalone)\n\nOnly expose ports needed for:\n1. Web browser access (Nginx)\n2. Database tools (DBeaver, DataGrip, TablePlus)\n\n```yaml\nservices:\n nginx:\n ports:\n - \"${APP_PORT:-8080}:80\"\n\n db:\n ports:\n - \"${DB_PORT:-3306}:3306\"\n\n # These stay internal\n app:\n # No ports exposed\n\n redis:\n # No ports exposed\n\n mailpit:\n ports:\n - \"${MAIL_UI_PORT:-8025}:8025\" # Only web UI\n```\n\n### Full Exposure\n\nFor debugging or when tools need direct access:\n\n```yaml\nservices:\n nginx:\n ports:\n - \"${APP_PORT:-8080}:80\"\n\n db:\n ports:\n - \"${DB_PORT:-3306}:3306\"\n\n redis:\n ports:\n - \"${REDIS_PORT:-6379}:6379\"\n\n mailpit:\n ports:\n - \"${MAIL_PORT:-1025}:1025\" # SMTP\n - \"${MAIL_UI_PORT:-8025}:8025\" # Web UI\n\n app:\n ports:\n - \"${PHP_FPM_PORT:-9000}:9000\" # Usually not needed\n```\n\n## Docker Networking\n\n### Isolated Project Network (Default)\n\n```yaml\nnetworks:\n default:\n driver: bridge\n name: ${COMPOSE_PROJECT_NAME:-myapp}_network\n\nservices:\n app:\n networks:\n - default\n```\n\n**Pros:**\n- Containers isolated from other projects\n- No naming conflicts\n- Secure by default\n\n### Shared External Network\n\nFor microservices or multiple projects that need to communicate:\n\n**Create the network first:**\n```bash\ndocker network create shared_network\n```\n\n**docker-compose.yml:**\n```yaml\nnetworks:\n shared:\n external: true\n name: shared_network\n\nservices:\n app:\n networks:\n - default\n - shared\n\n api:\n networks:\n - default\n - shared\n```\n\n**Cross-project communication:**\n```bash\n# From one project, access another by container name\ncurl http://other-project-app:80\n```\n\n### Multiple Networks\n\n```yaml\nnetworks:\n frontend:\n driver: bridge\n backend:\n driver: bridge\n\nservices:\n nginx:\n networks:\n - frontend\n\n app:\n networks:\n - frontend\n - backend\n\n db:\n networks:\n - backend\n```\n\n## Nginx Proxy Manager Integration\n\nFor managing multiple Docker projects with custom domains.\n\n### When to Use NPM\n\n- Running 3+ Docker projects\n- Need custom domains (myapp.local, api.local)\n- Want automatic SSL certificates\n- Prefer visual configuration\n\n### NPM Setup\n\n**docker-compose.yml for NPM:**\n```yaml\n# Note: No 'version' field needed - deprecated in Docker Compose v2\n\nservices:\n npm:\n image: jc21/nginx-proxy-manager:latest\n restart: unless-stopped\n ports:\n - \"80:80\"\n - \"443:443\"\n - \"81:81\" # Admin UI\n volumes:\n - npm_data:/data\n - npm_letsencrypt:/etc/letsencrypt\n networks:\n - proxy\n\nnetworks:\n proxy:\n external: true\n name: proxy_network\n\nvolumes:\n npm_data:\n npm_letsencrypt:\n```\n\n**Create proxy network:**\n```bash\ndocker network create proxy_network\n```\n\n### Project Configuration with NPM\n\n```yaml\n# Your project's docker-compose.yml\nnetworks:\n default:\n proxy:\n external: true\n name: proxy_network\n\nservices:\n app:\n networks:\n - default\n - proxy\n # No port exposure needed - NPM handles it\n```\n\n### Local Domain Setup\n\n**Add to /etc/hosts:**\n```\n127.0.0.1 myapp.local\n127.0.0.1 api.local\n127.0.0.1 admin.local\n```\n\n**Or use dnsmasq for wildcard:**\n```bash\n# macOS with Homebrew\nbrew install dnsmasq\necho 'address=/.local/127.0.0.1' >> /usr/local/etc/dnsmasq.conf\nsudo brew services start dnsmasq\n```\n\n## Configuration Storage\n\n### .env File Approach (Recommended)\n\n**.env.docker:**\n```env\n# Ports\nAPP_PORT=8080\nDB_PORT=3306\nREDIS_PORT=6379\nMAIL_PORT=1025\nMAIL_UI_PORT=8025\n\n# Database\nDB_DATABASE=app\nDB_USERNAME=app\nDB_PASSWORD=secret\n\n# Compose project name (for network naming)\nCOMPOSE_PROJECT_NAME=myapp\n```\n\n**docker-compose.yml:**\n```yaml\nservices:\n nginx:\n ports:\n - \"${APP_PORT:-8080}:80\"\n db:\n environment:\n MYSQL_DATABASE: ${DB_DATABASE:-app}\n MYSQL_PASSWORD: ${DB_PASSWORD:-secret}\n```\n\n**Benefits:**\n- Easy to change without editing compose file\n- Different configs per environment\n- Keep secrets out of version control\n\n### Direct docker-compose.yml\n\n```yaml\nservices:\n nginx:\n ports:\n - \"8080:80\"\n db:\n environment:\n MYSQL_DATABASE: app\n MYSQL_PASSWORD: secret\n```\n\n**Benefits:**\n- Simpler for basic setups\n- All config in one place\n\n**Drawbacks:**\n- Harder to customize per environment\n- Secrets in version control (careful!)\n\n## Port Conflict Resolution\n\n### Detection\n\n```bash\n# Check what's using a port\nlsof -i :8080\n\n# Kill process using port (careful!)\nkill $(lsof -t -i :8080)\n```\n\n### Resolution Strategies\n\n1. **Use Different Port**\n ```yaml\n ports:\n - \"8081:80\" # Instead of 8080\n ```\n\n2. **Stop Conflicting Service**\n ```bash\n # Stop local MySQL\n brew services stop mysql\n # or\n sudo systemctl stop mysql\n ```\n\n3. **Use Docker Network Only**\n ```yaml\n # Don't expose port, access via Docker network only\n db:\n # No ports directive\n ```\n\n### Common Conflicts\n\n| Port | Common Conflicting Service |\n|------|---------------------------|\n| 80 | Apache, Nginx, MAMP, XAMPP |\n| 3306 | MySQL, MariaDB |\n| 5432 | PostgreSQL |\n| 6379 | Redis |\n| 8080 | Various dev servers |\n\n## Troubleshooting\n\n### Container Can't Reach Another\n\n```bash\n# Check both are on same network\ndocker network inspect myapp_network\n\n# Test connectivity from container\ndocker compose exec app ping db\ndocker compose exec app nc -z db 3306\n```\n\n### Port Not Accessible from Host\n\n```bash\n# Check port mapping\ndocker compose port nginx 80\n\n# Check container is running\ndocker compose ps\n\n# Check logs\ndocker compose logs nginx\n```\n\n### DNS Resolution Issues\n\n```bash\n# Inside container, check /etc/hosts\ndocker compose exec app cat /etc/hosts\n\n# Check Docker DNS\ndocker compose exec app nslookup db\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7994,"content_sha256":"7e915ac2839d36b38955819618ad3e816ccf7902e7de7309188907ae53c6981a"},{"filename":"references/service-configuration-guide.md","content":"# Service Configuration Guide\n\nComprehensive configuration options for all Docker services.\n\n## Image Management Strategy\n\n### Scan Existing Images\n\nBefore pulling new images, scan what's already available locally to save disk space:\n\n```bash\n./scripts/detect-images.sh\n```\n\nThis script outputs JSON with:\n- Database images (MySQL, MariaDB, PostgreSQL)\n- PHP images with versions\n- Node.js images with versions\n- Redis images\n- Mail testing images (Mailpit, MailHog)\n- Nginx images\n\n### Image Selection Priority\n\n1. **Use existing images when possible** - saves disk space and download time\n2. **Match production versions** - if user needs specific version for compatibility\n3. **Prefer Alpine variants** - smaller image size (e.g., `redis:7-alpine` vs `redis:7`)\n\n### Example Decision Flow\n\n```\nDetected: mysql:8.0.35 (2.3 GB already downloaded)\n\nOptions:\n1. mysql:8.0.35 (already downloaded - recommended)\n2. mysql:8.4 (will download ~500 MB)\n3. mariadb:11 (will download ~400 MB)\n\n→ If production uses MySQL 8.0.x, recommend option 1\n→ If production uses MySQL 8.4, recommend option 2\n→ Always ask user for production compatibility\n```\n\n### Docker Compose Version\n\n**Important:** Do not include `version:` field in docker-compose.yml files.\n- Docker Compose v2 deprecated this field\n- Modern compose files don't need it\n\n## Nginx Configuration\n\n### PHP-FPM (Laravel/WordPress)\n\n```nginx\nserver {\n listen 80;\n server_name localhost;\n root /var/www/html/public; # Laravel\n # root /var/www/html; # WordPress\n index index.php index.html;\n\n location / {\n try_files $uri $uri/ /index.php?$query_string;\n }\n\n location ~ \\.php$ {\n fastcgi_pass app:9000;\n fastcgi_index index.php;\n fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;\n include fastcgi_params;\n }\n\n location ~ /\\.ht {\n deny all;\n }\n\n # Increase upload size (adjust as needed)\n client_max_body_size 100M;\n}\n```\n\n### Node.js Reverse Proxy\n\n```nginx\nserver {\n listen 80;\n server_name localhost;\n\n location / {\n proxy_pass http://app:3000;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection 'upgrade';\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_cache_bypass $http_upgrade;\n }\n}\n```\n\n### Python WSGI/ASGI\n\n```nginx\nserver {\n listen 80;\n server_name localhost;\n\n location / {\n proxy_pass http://app:8000;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n }\n\n location /static/ {\n alias /var/www/static/;\n }\n\n location /media/ {\n alias /var/www/media/;\n }\n}\n```\n\n## Database Configuration\n\n### MySQL 8.x\n\n```yaml\ndb:\n image: mysql:8.0\n environment:\n MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-secret}\n MYSQL_DATABASE: ${DB_DATABASE:-app}\n MYSQL_USER: ${DB_USERNAME:-app}\n MYSQL_PASSWORD: ${DB_PASSWORD:-secret}\n volumes:\n - db_data:/var/lib/mysql\n - ./docker/mysql/init:/docker-entrypoint-initdb.d\n ports:\n - \"${DB_PORT:-3306}:3306\"\n command: --default-authentication-plugin=mysql_native_password\n```\n\n**Development settings (my.cnf):**\n```ini\n[mysqld]\n# Performance\ninnodb_buffer_pool_size = 256M\ninnodb_log_file_size = 64M\n\n# Development friendly\nsql_mode = \"STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO\"\n\n# Logging (optional for debugging)\n# general_log = 1\n# general_log_file = /var/log/mysql/query.log\n```\n\n### MariaDB 11.x\n\n```yaml\ndb:\n image: mariadb:11\n environment:\n MARIADB_ROOT_PASSWORD: ${DB_PASSWORD:-secret}\n MARIADB_DATABASE: ${DB_DATABASE:-app}\n MARIADB_USER: ${DB_USERNAME:-app}\n MARIADB_PASSWORD: ${DB_PASSWORD:-secret}\n volumes:\n - db_data:/var/lib/mysql\n ports:\n - \"${DB_PORT:-3306}:3306\"\n```\n\n### PostgreSQL 16.x\n\n```yaml\ndb:\n image: postgres:16-alpine\n environment:\n POSTGRES_DB: ${DB_DATABASE:-app}\n POSTGRES_USER: ${DB_USERNAME:-app}\n POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}\n volumes:\n - db_data:/var/lib/postgresql/data\n ports:\n - \"${DB_PORT:-5432}:5432\"\n```\n\n## Redis Configuration\n\n### Basic Redis\n\n```yaml\nredis:\n image: redis:alpine\n volumes:\n - redis_data:/data\n ports:\n - \"${REDIS_PORT:-6379}:6379\"\n command: redis-server --appendonly yes\n```\n\n### Redis with Password\n\n```yaml\nredis:\n image: redis:alpine\n volumes:\n - redis_data:/data\n ports:\n - \"${REDIS_PORT:-6379}:6379\"\n command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-secret}\n```\n\n### Redis Configuration Options\n\n| Option | Value | Description |\n|--------|-------|-------------|\n| `appendonly` | yes | Enable AOF persistence |\n| `maxmemory` | 256mb | Memory limit |\n| `maxmemory-policy` | allkeys-lru | Eviction policy |\n\n## Email Testing Services\n\n### Mailpit (Recommended)\n\n```yaml\nmailpit:\n image: axllent/mailpit:latest\n ports:\n - \"${MAIL_PORT:-1025}:1025\" # SMTP\n - \"${MAIL_UI_PORT:-8025}:8025\" # Web UI\n environment:\n MP_SMTP_AUTH_ACCEPT_ANY: 1\n MP_SMTP_AUTH_ALLOW_INSECURE: 1\n```\n\n**Framework configuration:**\n\nLaravel (.env):\n```env\nMAIL_MAILER=smtp\nMAIL_HOST=mailpit\nMAIL_PORT=1025\nMAIL_USERNAME=null\nMAIL_PASSWORD=null\nMAIL_ENCRYPTION=null\n```\n\nWordPress (wp-config.php):\n```php\ndefine('SMTP_HOST', 'mailpit');\ndefine('SMTP_PORT', 1025);\n```\n\nDjango (settings.py):\n```python\nEMAIL_HOST = 'mailpit'\nEMAIL_PORT = 1025\nEMAIL_USE_TLS = False\n```\n\n### MailHog (Alternative)\n\n```yaml\nmailhog:\n image: mailhog/mailhog:latest\n ports:\n - \"${MAIL_PORT:-1025}:1025\"\n - \"${MAIL_UI_PORT:-8025}:8025\"\n```\n\n## Opcache Configuration\n\n### Development Settings\n\n```ini\n; docker/php/opcache.ini\nopcache.enable=1\nopcache.memory_consumption=256\nopcache.interned_strings_buffer=16\nopcache.max_accelerated_files=10000\n\n; CRITICAL for development - validate on every request\nopcache.validate_timestamps=1\nopcache.revalidate_freq=0\n\n; Optional: Enable file-based cache\n; opcache.file_cache=/tmp/opcache\n```\n\n### Production Hints (Comments)\n\n```ini\n; For production, consider:\n; opcache.validate_timestamps=0 ; Don't check for file changes\n; opcache.revalidate_freq=60 ; Or check less frequently\n```\n\n## Supervisor Configuration\n\n### Laravel Queue Worker\n\n```ini\n[program:laravel-worker]\nprocess_name=%(program_name)s_%(process_num)02d\ncommand=php /var/www/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600\nautostart=true\nautorestart=true\nstopasgroup=true\nkillasgroup=true\nuser=www-data\nnumprocs=2\nredirect_stderr=true\nstdout_logfile=/var/www/storage/logs/worker.log\nstopwaitsecs=3600\n```\n\n### Laravel Scheduler\n\n```ini\n[program:laravel-scheduler]\ncommand=/bin/sh -c \"while [ true ]; do php /var/www/artisan schedule:run --verbose --no-interaction; sleep 60; done\"\nautostart=true\nautorestart=true\nuser=www-data\nredirect_stderr=true\nstdout_logfile=/var/www/storage/logs/scheduler.log\n```\n\n### Celery Worker (Python)\n\n```ini\n[program:celery]\ncommand=celery -A myapp worker --loglevel=info\ndirectory=/var/www\nuser=www-data\nnumprocs=1\nautostart=true\nautorestart=true\nstartsecs=10\nstopwaitsecs=600\nstdout_logfile=/var/log/celery/worker.log\nstderr_logfile=/var/log/celery/worker-error.log\n```\n\n## Volume Mount Strategies\n\n### Bind Mount (Development)\n\n```yaml\nvolumes:\n - ./:/var/www\n```\n\n**Pros:**\n- Instant file sync\n- No rebuild needed for code changes\n- Easy debugging\n\n**Cons:**\n- Slower on macOS (~10-20%)\n- File permission issues possible\n\n### Bind Mount with Performance Options (macOS)\n\n```yaml\nvolumes:\n - ./:/var/www:cached # Relaxed consistency\n # or\n - ./:/var/www:delegated # Container-authoritative\n```\n\n### Named Volume (Performance)\n\n```yaml\nvolumes:\n - app_code:/var/www\n\nvolumes:\n app_code:\n```\n\n**Pros:**\n- Best performance\n- Native Docker speed\n\n**Cons:**\n- Requires rebuild for code changes\n- Not suitable for active development\n\n### Hybrid Approach\n\n```yaml\nvolumes:\n - ./:/var/www # Source code (bind mount)\n - vendor_cache:/var/www/vendor # Dependencies (named volume)\n - node_modules_cache:/var/www/node_modules\n\nvolumes:\n vendor_cache:\n node_modules_cache:\n```\n\n## Environment Variables\n\n### .env File Approach (Recommended)\n\n```env\n# .env.docker\nAPP_PORT=8080\nDB_PORT=3306\nREDIS_PORT=6379\nMAIL_PORT=1025\nMAIL_UI_PORT=8025\n\nDB_DATABASE=app\nDB_USERNAME=app\nDB_PASSWORD=secret\n```\n\n**docker-compose.yml:**\n```yaml\nservices:\n nginx:\n ports:\n - \"${APP_PORT:-8080}:80\"\n```\n\n### Direct in docker-compose.yml\n\n```yaml\nservices:\n nginx:\n ports:\n - \"8080:80\"\n db:\n environment:\n MYSQL_DATABASE: app\n MYSQL_PASSWORD: secret\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8894,"content_sha256":"0c0d043f4d1f926528303032745d7b9d523a1f1b81a821989c0946a649a02da0"},{"filename":"references/tech-stack-detection.md","content":"# Tech Stack Detection Reference\n\nThis document describes how to detect various tech stacks automatically before using AI analysis.\n\n## Detection Priority\n\n1. Run `scripts/detect-stack.sh` first (saves AI tokens)\n2. If detection fails or user disagrees, use AI analysis\n3. If stack is unsupported, warn user and proceed with generic config\n\n## PHP Detection\n\n### Laravel\n\n**Primary indicators:**\n- `composer.json` exists AND contains `laravel/framework`\n- `artisan` file exists in project root\n\n**Version detection:**\n```bash\n# From composer.json\ngrep '\"laravel/framework\"' composer.json | grep -oE '[0-9]+\\.[0-9]+'\n```\n\n**Additional detection:**\n- `config/app.php` - Laravel configuration\n- `routes/web.php` - Laravel routes\n- `app/Http/Kernel.php` - HTTP kernel\n\n**Laravel Sail detection:**\n- `docker-compose.yml` with `sail` references\n- `.env` with `SAIL_` variables\n- Warn user to avoid conflicts with existing Sail setup\n\n### WordPress\n\n**Primary indicators:**\n- `wp-config.php` exists\n- `wp-content/` directory exists\n- `wp-includes/` directory exists\n\n**Version detection:**\n```bash\n# From wp-includes/version.php\ngrep '$wp_version' wp-includes/version.php | grep -oE '[0-9]+\\.[0-9]+'\n```\n\n**Theme/Plugin detection:**\n- `wp-content/themes/` - active theme\n- `wp-content/plugins/` - installed plugins\n\n### Drupal\n\n**Primary indicators:**\n- `core/` directory with Drupal core\n- `sites/default/` directory\n- `core/lib/Drupal.php` exists\n\n**Version detection:**\n```bash\n# From core/lib/Drupal.php\ngrep 'const VERSION' core/lib/Drupal.php | grep -oE '[0-9]+\\.[0-9]+'\n```\n\n**Drush detection:**\n- `vendor/bin/drush` exists\n- `drush/` directory in project\n\n### Joomla\n\n**Primary indicators:**\n- `configuration.php` exists\n- `administrator/` directory exists\n- `libraries/src/` or `libraries/joomla/` exists\n\n**Version detection:**\n```bash\n# From libraries/src/Version.php\ngrep 'MAJOR_VERSION\\|MINOR_VERSION' libraries/src/Version.php\n```\n\n## Node.js Detection\n\n### Framework Detection\n\n**Next.js:**\n- `package.json` contains `\"next\"`\n- `next.config.js` or `next.config.ts` exists\n\n**NestJS:**\n- `package.json` contains `\"@nestjs/core\"`\n- `nest-cli.json` exists\n\n**Express:**\n- `package.json` contains `\"express\"`\n- Common patterns: `app.js`, `server.js`, `index.js`\n\n**Fastify:**\n- `package.json` contains `\"fastify\"`\n\n### Version Detection\n\n```bash\n# From .nvmrc\ncat .nvmrc | tr -d 'v'\n\n# From package.json engines\ngrep '\"node\"' package.json | grep -oE '[0-9]+'\n```\n\n### Package Manager Detection\n\n| Lock File | Package Manager |\n|-----------|-----------------|\n| `pnpm-lock.yaml` | pnpm |\n| `yarn.lock` | yarn |\n| `bun.lockb` | bun |\n| `package-lock.json` | npm |\n\n## Python Detection\n\n### Framework Detection\n\n**Django:**\n- `manage.py` exists\n- `requirements.txt` or `pyproject.toml` contains `django`\n- `settings.py` in project\n\n**FastAPI:**\n- `requirements.txt` or `pyproject.toml` contains `fastapi`\n- `main.py` with FastAPI app\n\n**Flask:**\n- `requirements.txt` or `pyproject.toml` contains `flask`\n- `app.py` or `application.py`\n\n### Version Detection\n\n```bash\n# From pyproject.toml\ngrep 'python' pyproject.toml | grep -oE '[0-9]+\\.[0-9]+'\n\n# From runtime.txt (Heroku style)\ncat runtime.txt | grep -oE '[0-9]+\\.[0-9]+'\n\n# From .python-version (pyenv)\ncat .python-version\n```\n\n### Package Manager Detection\n\n| File | Package Manager |\n|------|-----------------|\n| `poetry.lock` | Poetry |\n| `Pipfile.lock` | Pipenv |\n| `requirements.txt` | pip |\n\n## Database Detection\n\n### From .env File\n\n```bash\n# MySQL\ngrep -E 'DB_CONNECTION=mysql|DATABASE_URL.*mysql' .env\n\n# PostgreSQL\ngrep -E 'DB_CONNECTION=pgsql|DATABASE_URL.*postgres' .env\n\n# SQLite\ngrep -E 'DB_CONNECTION=sqlite' .env\n```\n\n### From Framework Config\n\n**Laravel:**\n- `config/database.php` - default connection\n- `.env` - `DB_CONNECTION`\n\n**Django:**\n- `settings.py` - `DATABASES` dict\n- Look for `mysql`, `postgresql`, `psycopg`\n\n**Node.js:**\n- `config/database.js` or similar\n- `.env` for connection string\n\n## Redis Detection\n\n**From .env:**\n```bash\ngrep -E 'REDIS_HOST|CACHE_DRIVER=redis|SESSION_DRIVER=redis|QUEUE_CONNECTION=redis' .env\n```\n\n**From package.json:**\n```bash\ngrep -E '\"redis\"|\"ioredis\"|\"bull\"' package.json\n```\n\n**From requirements.txt:**\n```bash\ngrep -iE '^redis|^celery|^django-redis' requirements.txt\n```\n\n## Queue Detection\n\n**Laravel:**\n- `QUEUE_CONNECTION=redis` or `database` in `.env`\n- `app/Jobs/` directory exists\n\n**Node.js:**\n- `bull` or `bullmq` in `package.json`\n\n**Python:**\n- `celery` in requirements\n\n## Existing Docker Detection\n\nCheck for these files:\n- `docker-compose.yml`\n- `docker-compose.yaml`\n- `Dockerfile`\n- `.dockerignore`\n\nIf found, ask user how to proceed:\n1. Merge with existing\n2. Replace (backup first)\n3. Cancel\n\n## Detection Output Format\n\n```json\n{\n \"detected\": true,\n \"language\": \"php\",\n \"languageVersion\": \"8.3\",\n \"framework\": \"laravel\",\n \"frameworkVersion\": \"11.0\",\n \"cms\": null,\n \"packageManager\": \"composer\",\n \"database\": \"mysql\",\n \"redis\": true,\n \"queue\": true,\n \"existingDocker\": false,\n \"supported\": true\n}\n```\n\n## Supported Stack List\n\n| Stack | Fully Supported |\n|-------|-----------------|\n| Laravel | Yes |\n| WordPress | Yes |\n| Drupal | Yes |\n| Joomla | Yes |\n| Next.js | Yes |\n| NestJS | Yes |\n| Express | Yes |\n| Django | Yes |\n| FastAPI | Yes |\n| Flask | Yes |\n\nFor unsupported stacks, the skill will:\n1. Warn the user\n2. Proceed with generic configuration\n3. Encourage contribution to improve support\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5449,"content_sha256":"26741bd0043ecb13a3cef950085d48001c70086c729e1582f8779a3ffccae77c"},{"filename":"scripts/db-test.sh","content":"#!/bin/bash\n#\n# db-test.sh - Simple CRUD test to verify database works\n# Tests INSERT, UPDATE, DELETE operations\n#\n\nset -e\n\n# Colors\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nNC='\\033[0m'\n\necho \"\"\necho \"==========================================\"\necho \"Database CRUD Test\"\necho \"==========================================\"\necho \"\"\n\n# Detect database type from running containers\ndetect_db() {\n local services=$(docker compose ps --services 2>/dev/null || echo \"\")\n\n if echo \"$services\" | grep -qE \"^(mysql|mariadb|db)$\"; then\n echo \"mysql\"\n elif echo \"$services\" | grep -qE \"^(postgres|postgresql)$\"; then\n echo \"postgresql\"\n else\n echo \"\"\n fi\n}\n\nDB_TYPE=$(detect_db)\n\nif [[ -z \"$DB_TYPE\" ]]; then\n echo -e \"${YELLOW}No database container detected. Skipping CRUD test.${NC}\"\n exit 0\nfi\n\n# Get database credentials from environment or use defaults\nDB_HOST=\"${DB_HOST:-localhost}\"\nDB_USER=\"${DB_USER:-root}\"\nDB_PASS=\"${DB_PASSWORD:-secret}\"\nDB_NAME=\"${DB_DATABASE:-test}\"\n\n# Test table name\nTEST_TABLE=\"_docker_local_dev_test\"\n\nrun_test() {\n local name=$1\n local success=$2\n\n printf \" %-20s\" \"$name...\"\n\n if [[ \"$success\" == \"true\" ]]; then\n echo -e \"${GREEN}OK${NC}\"\n return 0\n else\n echo -e \"${RED}FAILED${NC}\"\n return 1\n fi\n}\n\n# ============================================\n# MySQL/MariaDB Tests\n# ============================================\nif [[ \"$DB_TYPE\" == \"mysql\" ]]; then\n DB_SERVICE=$(docker compose ps --services 2>/dev/null | grep -E \"^(mysql|mariadb|db)$\" | head -1)\n\n echo \"Testing MySQL/MariaDB...\"\n echo \"\"\n\n # CREATE TABLE\n result=$(docker compose exec -T \"$DB_SERVICE\" mysql -u\"$DB_USER\" -p\"$DB_PASS\" -e \"\n CREATE TABLE IF NOT EXISTS $TEST_TABLE (\n id INT AUTO_INCREMENT PRIMARY KEY,\n name VARCHAR(255),\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n );\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"CREATE table\" \"[[ '$result' == *'success'* ]]\" && CREATE_OK=true || CREATE_OK=false\n\n # INSERT\n result=$(docker compose exec -T \"$DB_SERVICE\" mysql -u\"$DB_USER\" -p\"$DB_PASS\" -e \"\n INSERT INTO $TEST_TABLE (name) VALUES ('test_entry');\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"INSERT data\" \"[[ '$result' == *'success'* ]]\" && INSERT_OK=true || INSERT_OK=false\n\n # UPDATE\n result=$(docker compose exec -T \"$DB_SERVICE\" mysql -u\"$DB_USER\" -p\"$DB_PASS\" -e \"\n UPDATE $TEST_TABLE SET name = 'updated_entry' WHERE name = 'test_entry';\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"UPDATE data\" \"[[ '$result' == *'success'* ]]\" && UPDATE_OK=true || UPDATE_OK=false\n\n # SELECT (verify update worked)\n result=$(docker compose exec -T \"$DB_SERVICE\" mysql -u\"$DB_USER\" -p\"$DB_PASS\" -N -e \"\n SELECT COUNT(*) FROM $TEST_TABLE WHERE name = 'updated_entry';\n \" 2>&1)\n [[ \"$result\" == \"1\" ]] && SELECT_OK=true || SELECT_OK=false\n run_test \"SELECT data\" \"$SELECT_OK\"\n\n # DELETE\n result=$(docker compose exec -T \"$DB_SERVICE\" mysql -u\"$DB_USER\" -p\"$DB_PASS\" -e \"\n DELETE FROM $TEST_TABLE WHERE name = 'updated_entry';\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"DELETE data\" \"[[ '$result' == *'success'* ]]\" && DELETE_OK=true || DELETE_OK=false\n\n # DROP TABLE (cleanup)\n result=$(docker compose exec -T \"$DB_SERVICE\" mysql -u\"$DB_USER\" -p\"$DB_PASS\" -e \"\n DROP TABLE IF EXISTS $TEST_TABLE;\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"DROP table\" \"[[ '$result' == *'success'* ]]\" && DROP_OK=true || DROP_OK=false\nfi\n\n# ============================================\n# PostgreSQL Tests\n# ============================================\nif [[ \"$DB_TYPE\" == \"postgresql\" ]]; then\n DB_SERVICE=$(docker compose ps --services 2>/dev/null | grep -E \"^(postgres|postgresql)$\" | head -1)\n DB_USER=\"${DB_USER:-postgres}\"\n\n echo \"Testing PostgreSQL...\"\n echo \"\"\n\n # CREATE TABLE\n result=$(docker compose exec -T \"$DB_SERVICE\" psql -U \"$DB_USER\" -c \"\n CREATE TABLE IF NOT EXISTS $TEST_TABLE (\n id SERIAL PRIMARY KEY,\n name VARCHAR(255),\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n );\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"CREATE table\" \"[[ '$result' == *'success'* ]]\"\n\n # INSERT\n result=$(docker compose exec -T \"$DB_SERVICE\" psql -U \"$DB_USER\" -c \"\n INSERT INTO $TEST_TABLE (name) VALUES ('test_entry');\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"INSERT data\" \"[[ '$result' == *'success'* ]]\"\n\n # UPDATE\n result=$(docker compose exec -T \"$DB_SERVICE\" psql -U \"$DB_USER\" -c \"\n UPDATE $TEST_TABLE SET name = 'updated_entry' WHERE name = 'test_entry';\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"UPDATE data\" \"[[ '$result' == *'success'* ]]\"\n\n # SELECT\n result=$(docker compose exec -T \"$DB_SERVICE\" psql -U \"$DB_USER\" -t -c \"\n SELECT COUNT(*) FROM $TEST_TABLE WHERE name = 'updated_entry';\n \" 2>&1 | tr -d ' ')\n [[ \"$result\" == \"1\" ]] && SELECT_OK=true || SELECT_OK=false\n run_test \"SELECT data\" \"$SELECT_OK\"\n\n # DELETE\n result=$(docker compose exec -T \"$DB_SERVICE\" psql -U \"$DB_USER\" -c \"\n DELETE FROM $TEST_TABLE WHERE name = 'updated_entry';\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"DELETE data\" \"[[ '$result' == *'success'* ]]\"\n\n # DROP TABLE\n result=$(docker compose exec -T \"$DB_SERVICE\" psql -U \"$DB_USER\" -c \"\n DROP TABLE IF EXISTS $TEST_TABLE;\n \" 2>&1 && echo \"success\" || echo \"failed\")\n run_test \"DROP table\" \"[[ '$result' == *'success'* ]]\"\nfi\n\necho \"\"\necho -e \"${GREEN}Database CRUD test completed successfully!${NC}\"\necho \"\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":5772,"content_sha256":"f62892405bb51ef7411e20d803b014cfcdf70fbc77738b9c1c75a55b96e5c926"},{"filename":"scripts/detect-images.sh","content":"#!/bin/bash\n# Detect existing Docker images to suggest reuse and save disk space\n# Usage: ./detect-images.sh\n# Output: JSON with categorized existing images (databases, PHP, Node, Redis, mail)\n\nset -e\n\n# Check if Docker is running\nif ! docker info &>/dev/null 2>&1; then\n echo '{\"docker_running\": false, \"error\": \"Docker is not running or not accessible\"}'\n exit 0\nfi\n\n# Function to get images matching a pattern\nget_images_json() {\n local pattern=$1\n local first=true\n\n while IFS='|' read -r repo_tag size; do\n repo=$(echo \"$repo_tag\" | cut -d':' -f1)\n tag=$(echo \"$repo_tag\" | cut -d':' -f2)\n\n if [ \"$first\" = true ]; then\n first=false\n else\n echo \",\"\n fi\n echo -n \" {\\\"repository\\\": \\\"$repo\\\", \\\"tag\\\": \\\"$tag\\\", \\\"size\\\": \\\"$size\\\"}\"\n done \u003c \u003c(docker images --format '{{.Repository}}:{{.Tag}}|{{.Size}}' 2>/dev/null | grep -E \"^($pattern):\" || true)\n}\n\n# Start JSON output\necho \"{\"\necho ' \"docker_running\": true,'\n\n# Database images (MySQL, MariaDB, PostgreSQL)\necho ' \"databases\": ['\nDB_OUTPUT=$(get_images_json \"mysql|mariadb|postgres\")\nif [ -n \"$DB_OUTPUT\" ]; then\n echo \"$DB_OUTPUT\"\nfi\necho \"\"\necho \" ],\"\n\n# PHP images\necho ' \"php\": ['\nPHP_OUTPUT=$(get_images_json \"php\")\nif [ -n \"$PHP_OUTPUT\" ]; then\n echo \"$PHP_OUTPUT\"\nfi\necho \"\"\necho \" ],\"\n\n# Node.js images\necho ' \"node\": ['\nNODE_OUTPUT=$(get_images_json \"node\")\nif [ -n \"$NODE_OUTPUT\" ]; then\n echo \"$NODE_OUTPUT\"\nfi\necho \"\"\necho \" ],\"\n\n# Python images\necho ' \"python\": ['\nPYTHON_OUTPUT=$(get_images_json \"python\")\nif [ -n \"$PYTHON_OUTPUT\" ]; then\n echo \"$PYTHON_OUTPUT\"\nfi\necho \"\"\necho \" ],\"\n\n# Redis images\necho ' \"redis\": ['\nREDIS_OUTPUT=$(get_images_json \"redis\")\nif [ -n \"$REDIS_OUTPUT\" ]; then\n echo \"$REDIS_OUTPUT\"\nfi\necho \"\"\necho \" ],\"\n\n# Mail testing images (Mailpit, MailHog)\necho ' \"mail\": ['\nMAIL_OUTPUT=$(get_images_json \"mailpit|mailhog|axllent/mailpit\")\nif [ -n \"$MAIL_OUTPUT\" ]; then\n echo \"$MAIL_OUTPUT\"\nfi\necho \"\"\necho \" ],\"\n\n# Nginx images\necho ' \"nginx\": ['\nNGINX_OUTPUT=$(get_images_json \"nginx\")\nif [ -n \"$NGINX_OUTPUT\" ]; then\n echo \"$NGINX_OUTPUT\"\nfi\necho \"\"\necho \" ]\"\n\necho \"}\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":2188,"content_sha256":"c9006406f4f8d955332d78263ee505a5bc4ef3b1a83782f67bebf94fd5b1dd9f"},{"filename":"scripts/detect-network.sh","content":"#!/bin/bash\n# Detect Docker networks and reverse proxy containers (Nginx Proxy Manager, Traefik, Caddy)\n# Usage: ./detect-network.sh\n# Output: JSON with detected reverse proxies and available networks\n\nset -e\n\n# Check if Docker is running\nif ! docker info &>/dev/null 2>&1; then\n echo '{\"docker_running\": false, \"error\": \"Docker is not running or not accessible\"}'\n exit 0\nfi\n\n# Find reverse proxy containers (Nginx Proxy Manager, Traefik, Caddy, etc.)\nNPM_CONTAINERS=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -iE 'nginx.*proxy|npm|proxy.*manager|traefik|caddy|reverse.*proxy' | head -5 || true)\n\n# Get all custom networks (exclude default bridge, host, none)\nALL_NETWORKS=$(docker network ls --format '{{.Name}}' 2>/dev/null | grep -vE '^(bridge|host|none)

Docker Local Development Environment Generator Overview This skill helps you create optimized Docker development environments for your projects. It generates , , and related configurations through an interactive, question-driven workflow. When to use this skill: - Setting up a new Docker development environment - Dockerizing an existing project for local development - Adding services (database, Redis, email testing) to your Docker setup - Updating or merging with existing Docker configurations Key Principle: This skill ALWAYS asks questions before making decisions. You will be notified about…

|| true)\n\n# Start JSON output\necho \"{\"\necho ' \"docker_running\": true,'\n\n# Reverse proxy containers section\necho ' \"reverse_proxy_containers\": ['\nfirst=true\nif [ -n \"$NPM_CONTAINERS\" ]; then\n while IFS= read -r container; do\n if [ -n \"$container\" ]; then\n # Get networks for this container\n networks=$(docker inspect \"$container\" --format '{{range $net, $conf := .NetworkSettings.Networks}}{{$net}} {{end}}' 2>/dev/null | xargs)\n # Get container image\n image=$(docker inspect \"$container\" --format '{{.Config.Image}}' 2>/dev/null)\n\n if [ \"$first\" = true ]; then\n first=false\n else\n echo \",\"\n fi\n echo -n \" {\\\"name\\\": \\\"$container\\\", \\\"networks\\\": \\\"$networks\\\", \\\"image\\\": \\\"$image\\\"}\"\n fi\n done \u003c\u003c\u003c \"$NPM_CONTAINERS\"\nfi\necho \"\"\necho \" ],\"\n\n# Available networks section\necho ' \"available_networks\": ['\nfirst=true\nif [ -n \"$ALL_NETWORKS\" ]; then\n while IFS= read -r net; do\n if [ -n \"$net\" ]; then\n # Get network driver and scope\n driver=$(docker network inspect \"$net\" --format '{{.Driver}}' 2>/dev/null || echo \"unknown\")\n\n if [ \"$first\" = true ]; then\n first=false\n else\n echo \",\"\n fi\n echo -n \" {\\\"name\\\": \\\"$net\\\", \\\"driver\\\": \\\"$driver\\\"}\"\n fi\n done \u003c\u003c\u003c \"$ALL_NETWORKS\"\nfi\necho \"\"\necho \" ],\"\n\n# Suggested network for reverse proxy (if found)\necho ' \"suggested_proxy_network\": '\nif [ -n \"$NPM_CONTAINERS\" ]; then\n # Get the first container's primary network\n first_container=$(echo \"$NPM_CONTAINERS\" | head -1)\n if [ -n \"$first_container\" ]; then\n primary_network=$(docker inspect \"$first_container\" --format '{{range $net, $conf := .NetworkSettings.Networks}}{{$net}}{{end}}' 2>/dev/null | awk '{print $1}')\n if [ -n \"$primary_network\" ]; then\n echo \"\\\"$primary_network\\\"\"\n else\n echo \"null\"\n fi\n else\n echo \"null\"\n fi\nelse\n echo \"null\"\nfi\n\necho \"}\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":2872,"content_sha256":"308643fa7eb50aef1472879e891e7c69d810bb1d851cb87dd7a3688dc69fee27"},{"filename":"scripts/detect-stack.sh","content":"#!/bin/bash\n#\n# detect-stack.sh - Auto-detect project tech stack\n# Runs BEFORE AI analysis to save tokens\n#\n# Output: JSON format for easy parsing\n#\n\nset -e\n\n# Colors for output\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\n# Project root (default to current directory)\nPROJECT_ROOT=\"${1:-.}\"\n\n# Initialize detection results\nFRAMEWORK=\"\"\nFRAMEWORK_VERSION=\"\"\nCMS=\"\"\nLANGUAGE=\"\"\nLANGUAGE_VERSION=\"\"\nPACKAGE_MANAGER=\"\"\nDATABASE=\"\"\nREDIS_DETECTED=false\nQUEUE_DETECTED=false\nEXISTING_DOCKER=false\nSUPPORTED=true\n\n# Helper function to check file exists\nfile_exists() {\n [[ -f \"$PROJECT_ROOT/$1\" ]]\n}\n\n# Helper function to check directory exists\ndir_exists() {\n [[ -d \"$PROJECT_ROOT/$1\" ]]\n}\n\n# Helper function to check if string in file\nstring_in_file() {\n grep -q \"$1\" \"$PROJECT_ROOT/$2\" 2>/dev/null\n}\n\n# Detect PHP version from composer.json\ndetect_php_version() {\n if file_exists \"composer.json\"; then\n local php_require=$(grep -o '\"php\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' \"$PROJECT_ROOT/composer.json\" 2>/dev/null | head -1)\n if [[ -n \"$php_require\" ]]; then\n # Extract version like ^8.2 or >=8.1\n echo \"$php_require\" | grep -oE '[0-9]+\\.[0-9]+' | head -1\n fi\n fi\n}\n\n# Detect Node version\ndetect_node_version() {\n # Check .nvmrc first\n if file_exists \".nvmrc\"; then\n cat \"$PROJECT_ROOT/.nvmrc\" | tr -d 'v\\n'\n return\n fi\n\n # Check package.json engines\n if file_exists \"package.json\"; then\n local node_version=$(grep -o '\"node\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' \"$PROJECT_ROOT/package.json\" 2>/dev/null | head -1)\n if [[ -n \"$node_version\" ]]; then\n echo \"$node_version\" | grep -oE '[0-9]+' | head -1\n fi\n fi\n}\n\n# Detect Python version\ndetect_python_version() {\n # Check pyproject.toml\n if file_exists \"pyproject.toml\"; then\n local py_version=$(grep -E 'python[[:space:]]*=' \"$PROJECT_ROOT/pyproject.toml\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n if [[ -n \"$py_version\" ]]; then\n echo \"$py_version\"\n return\n fi\n fi\n\n # Check runtime.txt (Heroku style)\n if file_exists \"runtime.txt\"; then\n grep -oE '[0-9]+\\.[0-9]+' \"$PROJECT_ROOT/runtime.txt\" | head -1\n return\n fi\n\n # Check .python-version (pyenv)\n if file_exists \".python-version\"; then\n cat \"$PROJECT_ROOT/.python-version\" | grep -oE '[0-9]+\\.[0-9]+' | head -1\n fi\n}\n\n# Detect database from config files\ndetect_database() {\n local db=\"\"\n\n # Check .env file\n if file_exists \".env\"; then\n if grep -qE 'DB_CONNECTION=mysql|DATABASE_URL.*mysql' \"$PROJECT_ROOT/.env\" 2>/dev/null; then\n db=\"mysql\"\n elif grep -qE 'DB_CONNECTION=pgsql|DB_CONNECTION=postgres|DATABASE_URL.*postgres' \"$PROJECT_ROOT/.env\" 2>/dev/null; then\n db=\"postgresql\"\n elif grep -qE 'DB_CONNECTION=sqlite' \"$PROJECT_ROOT/.env\" 2>/dev/null; then\n db=\"sqlite\"\n fi\n fi\n\n # Check Django settings\n if [[ -z \"$db\" ]] && file_exists \"settings.py\"; then\n if grep -q \"mysql\" \"$PROJECT_ROOT/settings.py\" 2>/dev/null; then\n db=\"mysql\"\n elif grep -q \"postgresql\\|psycopg\" \"$PROJECT_ROOT/settings.py\" 2>/dev/null; then\n db=\"postgresql\"\n fi\n fi\n\n # Check for common config patterns\n if [[ -z \"$db\" ]]; then\n if file_exists \"config/database.php\" && grep -q \"mysql\" \"$PROJECT_ROOT/config/database.php\" 2>/dev/null; then\n db=\"mysql\"\n fi\n fi\n\n echo \"$db\"\n}\n\n# Detect Redis usage\ndetect_redis() {\n # Check .env\n if file_exists \".env\"; then\n if grep -qE 'REDIS_HOST|CACHE_DRIVER=redis|SESSION_DRIVER=redis|QUEUE_CONNECTION=redis' \"$PROJECT_ROOT/.env\" 2>/dev/null; then\n echo \"true\"\n return\n fi\n fi\n\n # Check package.json for redis packages\n if file_exists \"package.json\"; then\n if grep -qE '\"redis\"|\"ioredis\"|\"bull\"' \"$PROJECT_ROOT/package.json\" 2>/dev/null; then\n echo \"true\"\n return\n fi\n fi\n\n # Check requirements.txt\n if file_exists \"requirements.txt\"; then\n if grep -qiE '^redis|^celery|^django-redis' \"$PROJECT_ROOT/requirements.txt\" 2>/dev/null; then\n echo \"true\"\n return\n fi\n fi\n\n echo \"false\"\n}\n\n# Detect queue usage\ndetect_queue() {\n # Laravel queues\n if file_exists \".env\" && grep -qE 'QUEUE_CONNECTION=redis|QUEUE_CONNECTION=database' \"$PROJECT_ROOT/.env\" 2>/dev/null; then\n echo \"true\"\n return\n fi\n\n # Laravel jobs directory\n if dir_exists \"app/Jobs\"; then\n echo \"true\"\n return\n fi\n\n # Bull/BullMQ for Node.js\n if file_exists \"package.json\" && grep -qE '\"bull\"|\"bullmq\"' \"$PROJECT_ROOT/package.json\" 2>/dev/null; then\n echo \"true\"\n return\n fi\n\n # Celery for Python\n if file_exists \"requirements.txt\" && grep -qi \"^celery\" \"$PROJECT_ROOT/requirements.txt\" 2>/dev/null; then\n echo \"true\"\n return\n fi\n\n echo \"false\"\n}\n\n# Main detection logic\n\necho -e \"${BLUE}Detecting project tech stack...${NC}\" >&2\n\n# Check for existing Docker files\nif file_exists \"docker-compose.yml\" || file_exists \"docker-compose.yaml\" || file_exists \"Dockerfile\"; then\n EXISTING_DOCKER=true\nfi\n\n# ============================================\n# PHP/Laravel Detection\n# ============================================\nif file_exists \"composer.json\" && file_exists \"artisan\"; then\n LANGUAGE=\"php\"\n LANGUAGE_VERSION=$(detect_php_version)\n PACKAGE_MANAGER=\"composer\"\n\n # Check for Laravel\n if string_in_file \"laravel/framework\" \"composer.json\"; then\n FRAMEWORK=\"laravel\"\n # Detect Laravel version\n FRAMEWORK_VERSION=$(grep -o '\"laravel/framework\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' \"$PROJECT_ROOT/composer.json\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n fi\nfi\n\n# ============================================\n# WordPress Detection\n# ============================================\nif file_exists \"wp-config.php\" || (dir_exists \"wp-content\" && dir_exists \"wp-includes\"); then\n LANGUAGE=\"php\"\n CMS=\"wordpress\"\n PACKAGE_MANAGER=\"composer\"\n\n # Try to detect WP version\n if file_exists \"wp-includes/version.php\"; then\n FRAMEWORK_VERSION=$(grep \"\\$wp_version\" \"$PROJECT_ROOT/wp-includes/version.php\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n fi\nfi\n\n# ============================================\n# Drupal Detection\n# ============================================\nif dir_exists \"core\" && dir_exists \"sites/default\"; then\n if file_exists \"core/lib/Drupal.php\" || file_exists \"core/includes/bootstrap.inc\"; then\n LANGUAGE=\"php\"\n CMS=\"drupal\"\n PACKAGE_MANAGER=\"composer\"\n\n # Detect Drupal version\n if file_exists \"core/lib/Drupal.php\"; then\n FRAMEWORK_VERSION=$(grep \"const VERSION\" \"$PROJECT_ROOT/core/lib/Drupal.php\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n fi\n fi\nfi\n\n# ============================================\n# Joomla Detection\n# ============================================\nif file_exists \"configuration.php\" && dir_exists \"administrator\"; then\n if dir_exists \"libraries/src\" || dir_exists \"libraries/joomla\"; then\n LANGUAGE=\"php\"\n CMS=\"joomla\"\n\n # Detect Joomla version\n if file_exists \"libraries/src/Version.php\"; then\n FRAMEWORK_VERSION=$(grep \"MAJOR_VERSION\\|MINOR_VERSION\" \"$PROJECT_ROOT/libraries/src/Version.php\" 2>/dev/null | grep -oE '[0-9]+' | head -2 | paste -sd'.')\n fi\n fi\nfi\n\n# ============================================\n# Node.js Detection\n# ============================================\nif file_exists \"package.json\" && [[ -z \"$LANGUAGE\" ]]; then\n LANGUAGE=\"nodejs\"\n LANGUAGE_VERSION=$(detect_node_version)\n\n # Detect package manager\n if file_exists \"pnpm-lock.yaml\"; then\n PACKAGE_MANAGER=\"pnpm\"\n elif file_exists \"yarn.lock\"; then\n PACKAGE_MANAGER=\"yarn\"\n elif file_exists \"bun.lockb\"; then\n PACKAGE_MANAGER=\"bun\"\n else\n PACKAGE_MANAGER=\"npm\"\n fi\n\n # Detect framework\n if string_in_file '\"next\"' \"package.json\"; then\n FRAMEWORK=\"nextjs\"\n FRAMEWORK_VERSION=$(grep -o '\"next\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' \"$PROJECT_ROOT/package.json\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n elif string_in_file '\"@nestjs/core\"' \"package.json\"; then\n FRAMEWORK=\"nestjs\"\n FRAMEWORK_VERSION=$(grep -o '\"@nestjs/core\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' \"$PROJECT_ROOT/package.json\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n elif string_in_file '\"express\"' \"package.json\"; then\n FRAMEWORK=\"express\"\n FRAMEWORK_VERSION=$(grep -o '\"express\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' \"$PROJECT_ROOT/package.json\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n elif string_in_file '\"fastify\"' \"package.json\"; then\n FRAMEWORK=\"fastify\"\n elif string_in_file '\"koa\"' \"package.json\"; then\n FRAMEWORK=\"koa\"\n fi\nfi\n\n# ============================================\n# Python Detection\n# ============================================\nif (file_exists \"requirements.txt\" || file_exists \"pyproject.toml\" || file_exists \"Pipfile\") && [[ -z \"$LANGUAGE\" ]]; then\n LANGUAGE=\"python\"\n LANGUAGE_VERSION=$(detect_python_version)\n\n # Detect package manager\n if file_exists \"poetry.lock\"; then\n PACKAGE_MANAGER=\"poetry\"\n elif file_exists \"Pipfile.lock\"; then\n PACKAGE_MANAGER=\"pipenv\"\n else\n PACKAGE_MANAGER=\"pip\"\n fi\n\n # Detect framework\n local req_file=\"\"\n if file_exists \"requirements.txt\"; then\n req_file=\"requirements.txt\"\n elif file_exists \"pyproject.toml\"; then\n req_file=\"pyproject.toml\"\n fi\n\n if [[ -n \"$req_file\" ]]; then\n if grep -qi \"django\" \"$PROJECT_ROOT/$req_file\" 2>/dev/null; then\n FRAMEWORK=\"django\"\n FRAMEWORK_VERSION=$(grep -ioE 'django[=\u003c>~!]*[0-9]+\\.[0-9]+' \"$PROJECT_ROOT/$req_file\" 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+' | head -1)\n elif grep -qi \"fastapi\" \"$PROJECT_ROOT/$req_file\" 2>/dev/null; then\n FRAMEWORK=\"fastapi\"\n elif grep -qi \"flask\" \"$PROJECT_ROOT/$req_file\" 2>/dev/null; then\n FRAMEWORK=\"flask\"\n fi\n fi\nfi\n\n# ============================================\n# Detect Database, Redis, Queue\n# ============================================\nDATABASE=$(detect_database)\nREDIS_DETECTED=$(detect_redis)\nQUEUE_DETECTED=$(detect_queue)\n\n# ============================================\n# Check if stack is officially supported\n# ============================================\nSUPPORTED_STACKS=\"laravel|wordpress|drupal|joomla|nextjs|nestjs|express|fastify|django|fastapi|flask\"\nif [[ -n \"$FRAMEWORK\" ]] && ! echo \"$FRAMEWORK\" | grep -qE \"$SUPPORTED_STACKS\"; then\n SUPPORTED=false\nfi\nif [[ -n \"$CMS\" ]] && ! echo \"$CMS\" | grep -qE \"wordpress|drupal|joomla\"; then\n SUPPORTED=false\nfi\n\n# ============================================\n# Output JSON\n# ============================================\ncat \u003c\u003cEOF\n{\n \"detected\": true,\n \"language\": \"$LANGUAGE\",\n \"languageVersion\": \"$LANGUAGE_VERSION\",\n \"framework\": \"$FRAMEWORK\",\n \"frameworkVersion\": \"$FRAMEWORK_VERSION\",\n \"cms\": \"$CMS\",\n \"packageManager\": \"$PACKAGE_MANAGER\",\n \"database\": \"$DATABASE\",\n \"redis\": $REDIS_DETECTED,\n \"queue\": $QUEUE_DETECTED,\n \"existingDocker\": $EXISTING_DOCKER,\n \"supported\": $SUPPORTED\n}\nEOF\n\n# ============================================\n# Human-readable summary\n# ============================================\necho \"\" >&2\necho -e \"${GREEN}Detection Results:${NC}\" >&2\necho \"==================\" >&2\n\nif [[ -n \"$LANGUAGE\" ]]; then\n echo -e \"Language: ${BLUE}$LANGUAGE${NC}\" >&2\n [[ -n \"$LANGUAGE_VERSION\" ]] && echo \" Version: $LANGUAGE_VERSION\" >&2\nfi\n\nif [[ -n \"$FRAMEWORK\" ]]; then\n echo -e \"Framework: ${BLUE}$FRAMEWORK${NC}\" >&2\n [[ -n \"$FRAMEWORK_VERSION\" ]] && echo \" Version: $FRAMEWORK_VERSION\" >&2\nfi\n\nif [[ -n \"$CMS\" ]]; then\n echo -e \"CMS: ${BLUE}$CMS${NC}\" >&2\nfi\n\nif [[ -n \"$PACKAGE_MANAGER\" ]]; then\n echo \"Package Manager: $PACKAGE_MANAGER\" >&2\nfi\n\nif [[ -n \"$DATABASE\" ]]; then\n echo \"Database: $DATABASE\" >&2\nfi\n\necho \"Redis: $REDIS_DETECTED\" >&2\necho \"Queue: $QUEUE_DETECTED\" >&2\necho \"Existing Docker: $EXISTING_DOCKER\" >&2\n\nif [[ \"$SUPPORTED\" == \"false\" ]]; then\n echo \"\" >&2\n echo -e \"${YELLOW}Warning: This stack is not officially supported.${NC}\" >&2\n echo \"The Docker setup may not be optimal.\" >&2\n echo \"Consider contributing to improve support!\" >&2\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":12631,"content_sha256":"469538e7522352758c6e8cf065b4fc62e7ba1cf4ae2eda578583a03be6436509"},{"filename":"scripts/health-check.sh","content":"#!/bin/bash\n#\n# health-check.sh - Verify all Docker services are healthy\n# Runs AUTOMATICALLY after docker-compose up\n#\n\nset -e\n\n# Colors\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nNC='\\033[0m'\n\n# Counters\nPASSED=0\nFAILED=0\n\necho \"\"\necho \"==========================================\"\necho \"Docker Local Dev - Health Check\"\necho \"==========================================\"\necho \"\"\n\n# Helper function to check service\ncheck_service() {\n local name=$1\n local command=$2\n local container=$3\n\n printf \"Checking %-20s\" \"$name...\"\n\n if [[ -n \"$container\" ]]; then\n if docker compose exec -T \"$container\" sh -c \"$command\" > /dev/null 2>&1; then\n echo -e \"${GREEN}OK${NC}\"\n ((PASSED++))\n return 0\n fi\n else\n if eval \"$command\" > /dev/null 2>&1; then\n echo -e \"${GREEN}OK${NC}\"\n ((PASSED++))\n return 0\n fi\n fi\n\n echo -e \"${RED}FAILED${NC}\"\n ((FAILED++))\n return 1\n}\n\n# Helper function to check HTTP endpoint\ncheck_http() {\n local name=$1\n local url=$2\n local expected_codes=${3:-\"200 301 302\"}\n\n printf \"Checking %-20s\" \"$name...\"\n\n local http_code=$(curl -s -o /dev/null -w '%{http_code}' \"$url\" 2>/dev/null || echo \"000\")\n\n if echo \"$expected_codes\" | grep -q \"$http_code\"; then\n echo -e \"${GREEN}OK${NC} (HTTP $http_code)\"\n ((PASSED++))\n return 0\n fi\n\n echo -e \"${RED}FAILED${NC} (HTTP $http_code)\"\n ((FAILED++))\n return 1\n}\n\n# Detect what services are running\ndetect_services() {\n SERVICES=$(docker compose ps --services 2>/dev/null || echo \"\")\n}\n\ndetect_services\n\n# ============================================\n# Check Database\n# ============================================\nif echo \"$SERVICES\" | grep -qE \"^(db|mysql|mariadb)$\"; then\n DB_SERVICE=$(echo \"$SERVICES\" | grep -E \"^(db|mysql|mariadb)$\" | head -1)\n check_service \"MySQL/MariaDB\" \"mysqladmin ping -h localhost -u root --silent\" \"$DB_SERVICE\"\nelif echo \"$SERVICES\" | grep -qE \"^(postgres|postgresql)$\"; then\n DB_SERVICE=$(echo \"$SERVICES\" | grep -E \"^(postgres|postgresql)$\" | head -1)\n check_service \"PostgreSQL\" \"pg_isready -U postgres\" \"$DB_SERVICE\"\nfi\n\n# ============================================\n# Check Redis\n# ============================================\nif echo \"$SERVICES\" | grep -q \"^redis$\"; then\n check_service \"Redis\" \"redis-cli ping | grep -q PONG\" \"redis\"\nfi\n\n# ============================================\n# Check Web Server\n# ============================================\nif echo \"$SERVICES\" | grep -qE \"^(nginx|web)$\"; then\n # Try common ports\n for port in 80 8080 8000 3000; do\n if curl -s \"http://localhost:$port\" > /dev/null 2>&1; then\n check_http \"Nginx (port $port)\" \"http://localhost:$port\"\n break\n fi\n done\nfi\n\n# ============================================\n# Check PHP-FPM / Application\n# ============================================\nif echo \"$SERVICES\" | grep -qE \"^(app|php|php-fpm)$\"; then\n APP_SERVICE=$(echo \"$SERVICES\" | grep -E \"^(app|php|php-fpm)$\" | head -1)\n\n # Check if PHP is running\n check_service \"PHP-FPM\" \"php -v\" \"$APP_SERVICE\"\n\n # Laravel specific check\n if docker compose exec -T \"$APP_SERVICE\" test -f artisan 2>/dev/null; then\n check_service \"Laravel\" \"php artisan --version\" \"$APP_SERVICE\"\n fi\n\n # WordPress specific check\n if docker compose exec -T \"$APP_SERVICE\" test -f wp-includes/version.php 2>/dev/null; then\n if docker compose exec -T \"$APP_SERVICE\" which wp > /dev/null 2>&1; then\n check_service \"WP-CLI\" \"wp core version\" \"$APP_SERVICE\"\n fi\n fi\nfi\n\n# ============================================\n# Check Node.js Application\n# ============================================\nif echo \"$SERVICES\" | grep -qE \"^(node|app)$\"; then\n NODE_SERVICE=$(echo \"$SERVICES\" | grep -E \"^(node|app)$\" | head -1)\n if docker compose exec -T \"$NODE_SERVICE\" which node > /dev/null 2>&1; then\n check_service \"Node.js\" \"node -v\" \"$NODE_SERVICE\"\n fi\nfi\n\n# ============================================\n# Check Python Application\n# ============================================\nif echo \"$SERVICES\" | grep -qE \"^(python|app|web)$\"; then\n PY_SERVICE=$(echo \"$SERVICES\" | grep -E \"^(python|app|web)$\" | head -1)\n if docker compose exec -T \"$PY_SERVICE\" which python > /dev/null 2>&1; then\n check_service \"Python\" \"python --version\" \"$PY_SERVICE\"\n\n # Django check\n if docker compose exec -T \"$PY_SERVICE\" test -f manage.py 2>/dev/null; then\n check_service \"Django\" \"python manage.py check\" \"$PY_SERVICE\"\n fi\n fi\nfi\n\n# ============================================\n# Check Email Service\n# ============================================\nif echo \"$SERVICES\" | grep -qE \"^(mailpit|mailhog|mail)$\"; then\n MAIL_SERVICE=$(echo \"$SERVICES\" | grep -E \"^(mailpit|mailhog|mail)$\" | head -1)\n\n # Check web UI\n for port in 8025 1080; do\n if curl -s \"http://localhost:$port\" > /dev/null 2>&1; then\n check_http \"Mail UI (port $port)\" \"http://localhost:$port\"\n break\n fi\n done\nfi\n\n# ============================================\n# Check Queue Worker (Supervisor)\n# ============================================\nif echo \"$SERVICES\" | grep -qE \"^(worker|queue|supervisor)$\"; then\n WORKER_SERVICE=$(echo \"$SERVICES\" | grep -E \"^(worker|queue|supervisor)$\" | head -1)\n check_service \"Queue Worker\" \"ps aux | grep -E 'queue:work|celery|bull' | grep -v grep\" \"$WORKER_SERVICE\"\nfi\n\n# ============================================\n# Summary\n# ============================================\necho \"\"\necho \"==========================================\"\necho \"Health Check Summary\"\necho \"==========================================\"\necho -e \"Passed: ${GREEN}$PASSED${NC}\"\necho -e \"Failed: ${RED}$FAILED${NC}\"\necho \"\"\n\nif [[ $FAILED -eq 0 ]]; then\n echo -e \"${GREEN}All services are healthy!${NC}\"\n echo \"\"\n\n # Print access information\n echo \"Your development environment is ready:\"\n echo \"\"\n\n # Detect and show URLs\n for port in 80 8080 8000 3000; do\n if curl -s \"http://localhost:$port\" > /dev/null 2>&1; then\n echo \" Web: http://localhost:$port\"\n break\n fi\n done\n\n # Database port\n for port in 3306 5432; do\n if nc -z localhost $port 2>/dev/null; then\n echo \" Database: localhost:$port\"\n break\n fi\n done\n\n # Mail UI\n for port in 8025 1080; do\n if curl -s \"http://localhost:$port\" > /dev/null 2>&1; then\n echo \" Mail UI: http://localhost:$port\"\n break\n fi\n done\n\n echo \"\"\n exit 0\nelse\n echo -e \"${RED}Some services failed health checks.${NC}\"\n echo \"\"\n echo \"Troubleshooting tips:\"\n echo \" 1. Check container logs: docker compose logs\"\n echo \" 2. Verify containers are running: docker compose ps\"\n echo \" 3. Restart services: docker compose restart\"\n echo \"\"\n exit 1\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":7045,"content_sha256":"51c34c0c35ed2c92742eaf2c2abda2076104611c55d71e26537dedc596d7db9c"},{"filename":"scripts/port-check.sh","content":"#!/bin/bash\n#\n# port-check.sh - Check and find available ports\n# Verifies ports are not in use before Docker setup\n#\n\nset -e\n\n# Colors\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nNC='\\033[0m'\n\n# Default port ranges\nHTTP_START=8080\nHTTP_END=8099\nDB_MYSQL_START=3306\nDB_MYSQL_END=3399\nDB_POSTGRES_START=5432\nDB_POSTGRES_END=5499\nREDIS_START=6379\nREDIS_END=6399\nMAIL_START=8025\nMAIL_END=8099\n\n# Check if a port is in use\nis_port_in_use() {\n local port=$1\n\n # Try multiple methods for compatibility\n if command -v lsof > /dev/null 2>&1; then\n lsof -i \":$port\" > /dev/null 2>&1 && return 0\n fi\n\n if command -v nc > /dev/null 2>&1; then\n nc -z localhost \"$port\" 2>/dev/null && return 0\n fi\n\n if command -v ss > /dev/null 2>&1; then\n ss -tuln | grep -q \":$port \" && return 0\n fi\n\n # Try connecting with bash\n (echo > /dev/tcp/localhost/$port) 2>/dev/null && return 0\n\n return 1\n}\n\n# Find next available port in range\nfind_available_port() {\n local start=$1\n local end=$2\n\n for port in $(seq $start $end); do\n if ! is_port_in_use $port; then\n echo $port\n return 0\n fi\n done\n\n echo \"\"\n return 1\n}\n\n# Check specific port and suggest alternative\ncheck_port() {\n local name=$1\n local port=$2\n local range_start=$3\n local range_end=$4\n\n printf \"Port %-5s (%-15s) \" \"$port\" \"$name\"\n\n if is_port_in_use $port; then\n echo -e \"${YELLOW}IN USE${NC}\"\n\n # Find alternative\n local alt_port=$(find_available_port $range_start $range_end)\n if [[ -n \"$alt_port\" ]]; then\n echo -e \" ${BLUE}Suggesting: $alt_port${NC}\"\n echo \"$alt_port\"\n else\n echo -e \" ${RED}No available ports in range $range_start-$range_end${NC}\"\n echo \"\"\n fi\n return 1\n else\n echo -e \"${GREEN}Available${NC}\"\n echo \"$port\"\n return 0\n fi\n}\n\n# Main function\nmain() {\n echo \"\"\n echo \"==========================================\"\n echo \"Port Availability Check\"\n echo \"==========================================\"\n echo \"\"\n\n local mode=${1:-check}\n\n case $mode in\n check)\n # Check common development ports\n echo \"Checking common development ports...\"\n echo \"\"\n\n check_port \"HTTP\" 8080 $HTTP_START $HTTP_END > /dev/null\n check_port \"MySQL\" 3306 $DB_MYSQL_START $DB_MYSQL_END > /dev/null\n check_port \"PostgreSQL\" 5432 $DB_POSTGRES_START $DB_POSTGRES_END > /dev/null\n check_port \"Redis\" 6379 $REDIS_START $REDIS_END > /dev/null\n check_port \"Mail UI\" 8025 $MAIL_START $MAIL_END > /dev/null\n ;;\n\n suggest)\n # Output JSON with suggested ports\n local http_port=$(find_available_port $HTTP_START $HTTP_END)\n local mysql_port=$(find_available_port $DB_MYSQL_START $DB_MYSQL_END)\n local postgres_port=$(find_available_port $DB_POSTGRES_START $DB_POSTGRES_END)\n local redis_port=$(find_available_port $REDIS_START $REDIS_END)\n local mail_port=$(find_available_port $MAIL_START $MAIL_END)\n\n cat \u003c\u003cEOF\n{\n \"http\": ${http_port:-null},\n \"mysql\": ${mysql_port:-null},\n \"postgresql\": ${postgres_port:-null},\n \"redis\": ${redis_port:-null},\n \"mail\": ${mail_port:-null}\n}\nEOF\n ;;\n\n verify)\n # Verify specific ports from arguments\n shift\n local all_ok=true\n\n for port in \"$@\"; do\n if is_port_in_use $port; then\n echo -e \"Port $port: ${RED}IN USE${NC}\"\n all_ok=false\n else\n echo -e \"Port $port: ${GREEN}Available${NC}\"\n fi\n done\n\n if [[ \"$all_ok\" == \"true\" ]]; then\n exit 0\n else\n exit 1\n fi\n ;;\n\n find)\n # Find a single available port\n local start=${2:-8080}\n local end=${3:-8099}\n find_available_port $start $end\n ;;\n\n *)\n echo \"Usage: $0 [check|suggest|verify|find]\"\n echo \"\"\n echo \"Commands:\"\n echo \" check Check common development ports (default)\"\n echo \" suggest Output JSON with suggested available ports\"\n echo \" verify \u003cports> Verify specific ports are available\"\n echo \" find \u003cstart> \u003cend> Find first available port in range\"\n ;;\n esac\n}\n\nmain \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4603,"content_sha256":"11776264fa3c14b800b490528225205d4462ad54216d07a04ad0603e28e2a82b"},{"filename":"scripts/wait-for-it.sh","content":"#!/bin/bash\n#\n# wait-for-it.sh - Wait for a service to be ready\n# Based on the popular vishnubob/wait-for-it.sh\n#\n\nset -e\n\nTIMEOUT=30\nQUIET=0\nHOST=\"\"\nPORT=\"\"\n\nusage() {\n cat \u003c\u003c EOF\nUsage: $0 host:port [-t timeout] [-q] [-- command args]\n\n -h HOST | --host=HOST Host or IP to test\n -p PORT | --port=PORT Port to test\n -t TIMEOUT | --timeout=TIMEOUT\n Timeout in seconds, zero for no timeout\n -q | --quiet Don't output any status messages\n -- COMMAND ARGS Execute command with args after the test finishes\n\nExamples:\n $0 mysql:3306 -t 60 -- echo \"MySQL is up\"\n $0 -h redis -p 6379 --timeout=30 -- redis-cli ping\nEOF\n exit 1\n}\n\nwait_for() {\n if [[ $TIMEOUT -gt 0 ]]; then\n echoerr \"Waiting $TIMEOUT seconds for $HOST:$PORT\"\n else\n echoerr \"Waiting for $HOST:$PORT without a timeout\"\n fi\n\n local start_ts=$(date +%s)\n while :\n do\n if nc -z \"$HOST\" \"$PORT\" > /dev/null 2>&1; then\n local end_ts=$(date +%s)\n echoerr \"$HOST:$PORT is available after $((end_ts - start_ts)) seconds\"\n return 0\n fi\n\n sleep 1\n\n if [[ $TIMEOUT -gt 0 ]]; then\n local current_ts=$(date +%s)\n if [[ $((current_ts - start_ts)) -ge $TIMEOUT ]]; then\n echoerr \"Timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT\"\n return 1\n fi\n fi\n done\n}\n\nechoerr() {\n if [[ $QUIET -eq 0 ]]; then\n echo \"$@\" 1>&2\n fi\n}\n\n# Parse arguments\nwhile [[ $# -gt 0 ]]\ndo\n case \"$1\" in\n *:* )\n HOST=$(echo \"$1\" | cut -d: -f1)\n PORT=$(echo \"$1\" | cut -d: -f2)\n shift 1\n ;;\n -h | --host)\n HOST=\"$2\"\n shift 2\n ;;\n --host=*)\n HOST=\"${1#*=}\"\n shift 1\n ;;\n -p | --port)\n PORT=\"$2\"\n shift 2\n ;;\n --port=*)\n PORT=\"${1#*=}\"\n shift 1\n ;;\n -t | --timeout)\n TIMEOUT=\"$2\"\n shift 2\n ;;\n --timeout=*)\n TIMEOUT=\"${1#*=}\"\n shift 1\n ;;\n -q | --quiet)\n QUIET=1\n shift 1\n ;;\n --)\n shift\n CLI=(\"$@\")\n break\n ;;\n --help)\n usage\n ;;\n *)\n echoerr \"Unknown argument: $1\"\n usage\n ;;\n esac\ndone\n\nif [[ -z \"$HOST\" || -z \"$PORT\" ]]; then\n echoerr \"Error: you need to provide a host and port to test.\"\n usage\nfi\n\nwait_for\nRESULT=$?\n\nif [[ $RESULT -ne 0 ]]; then\n exit $RESULT\nfi\n\nif [[ ${#CLI[@]} -gt 0 ]]; then\n exec \"${CLI[@]}\"\nfi\n\nexit 0\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":2807,"content_sha256":"d0253405a162dfe4c9f65c1b45fa8c4e86c549e561d06b9e7221ed7dd07d705d"},{"filename":"scripts/wp-setup.sh","content":"#!/bin/bash\n#\n# wp-setup.sh - WordPress initialization script\n# Sets up WP-CLI, downloads core, installs debug plugins\n#\n\nset -e\n\n# Colors\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nNC='\\033[0m'\n\n# Configuration\nWP_PATH=\"${WP_PATH:-/var/www/html}\"\nWP_DEBUG_PLUGINS=\"${WP_DEBUG_PLUGINS:-query-monitor debug-bar}\"\n\necho \"\"\necho \"==========================================\"\necho \"WordPress Setup Script\"\necho \"==========================================\"\necho \"\"\n\n# Check if running inside container or on host\nif [[ -f \"/.dockerenv\" ]]; then\n INSIDE_CONTAINER=true\nelse\n INSIDE_CONTAINER=false\nfi\n\n# Helper function to run WP-CLI\nwp_cli() {\n if [[ \"$INSIDE_CONTAINER\" == \"true\" ]]; then\n wp \"$@\" --path=\"$WP_PATH\" --allow-root\n else\n docker compose exec -T app wp \"$@\" --path=\"$WP_PATH\" --allow-root\n fi\n}\n\n# Check if WP-CLI is installed\ncheck_wpcli() {\n echo -n \"Checking WP-CLI... \"\n\n if wp_cli --version > /dev/null 2>&1; then\n echo -e \"${GREEN}installed${NC}\"\n return 0\n else\n echo -e \"${YELLOW}not found${NC}\"\n return 1\n fi\n}\n\n# Install WP-CLI\ninstall_wpcli() {\n echo \"Installing WP-CLI...\"\n\n if [[ \"$INSIDE_CONTAINER\" == \"true\" ]]; then\n curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar\n chmod +x wp-cli.phar\n mv wp-cli.phar /usr/local/bin/wp\n else\n docker compose exec -T app bash -c \"\n curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar\n chmod +x wp-cli.phar\n mv wp-cli.phar /usr/local/bin/wp\n \"\n fi\n\n echo -e \"${GREEN}WP-CLI installed successfully${NC}\"\n}\n\n# Check if WordPress is downloaded\ncheck_wp_core() {\n echo -n \"Checking WordPress core... \"\n\n if wp_cli core is-installed 2>/dev/null; then\n echo -e \"${GREEN}installed${NC}\"\n return 0\n elif [[ -f \"$WP_PATH/wp-config.php\" ]]; then\n echo -e \"${YELLOW}downloaded but not installed${NC}\"\n return 1\n else\n echo -e \"${YELLOW}not downloaded${NC}\"\n return 2\n fi\n}\n\n# Download WordPress core\ndownload_wp_core() {\n local version=${1:-latest}\n\n echo \"Downloading WordPress $version...\"\n wp_cli core download --version=\"$version\"\n echo -e \"${GREEN}WordPress downloaded${NC}\"\n}\n\n# Configure wp-config.php for Docker\nconfigure_wp_config() {\n echo \"Configuring wp-config.php for Docker...\"\n\n local db_name=\"${WORDPRESS_DB_NAME:-wordpress}\"\n local db_user=\"${WORDPRESS_DB_USER:-wordpress}\"\n local db_pass=\"${WORDPRESS_DB_PASSWORD:-wordpress}\"\n local db_host=\"${WORDPRESS_DB_HOST:-db}\"\n\n # Check if wp-config.php exists\n if [[ -f \"$WP_PATH/wp-config.php\" ]]; then\n echo \"wp-config.php already exists, skipping...\"\n return 0\n fi\n\n wp_cli config create \\\n --dbname=\"$db_name\" \\\n --dbuser=\"$db_user\" \\\n --dbpass=\"$db_pass\" \\\n --dbhost=\"$db_host\" \\\n --extra-php \u003c\u003cPHP\n\n// Docker development settings\ndefine('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', true);\ndefine('SCRIPT_DEBUG', true);\ndefine('SAVEQUERIES', true);\n\n// Disable automatic updates in Docker\ndefine('AUTOMATIC_UPDATER_DISABLED', true);\ndefine('WP_AUTO_UPDATE_CORE', false);\n\n// Memory limit\ndefine('WP_MEMORY_LIMIT', '256M');\ndefine('WP_MAX_MEMORY_LIMIT', '512M');\n\n// Cookie settings for local development\ndefine('COOKIE_DOMAIN', '');\ndefine('ADMIN_COOKIE_PATH', '/');\ndefine('COOKIEPATH', '/');\ndefine('SITECOOKIEPATH', '/');\nPHP\n\n echo -e \"${GREEN}wp-config.php configured for Docker${NC}\"\n}\n\n# Install debug plugins\ninstall_debug_plugins() {\n echo \"\"\n echo \"Installing debug plugins...\"\n\n for plugin in $WP_DEBUG_PLUGINS; do\n echo -n \" Installing $plugin... \"\n\n if wp_cli plugin is-installed \"$plugin\" 2>/dev/null; then\n echo -e \"${YELLOW}already installed${NC}\"\n else\n if wp_cli plugin install \"$plugin\" --activate 2>/dev/null; then\n echo -e \"${GREEN}done${NC}\"\n else\n echo -e \"${RED}failed${NC}\"\n fi\n fi\n done\n}\n\n# Verify WordPress checksums\nverify_checksums() {\n echo \"\"\n echo -n \"Verifying WordPress core checksums... \"\n\n if wp_cli core verify-checksums 2>/dev/null; then\n echo -e \"${GREEN}OK${NC}\"\n return 0\n else\n echo -e \"${YELLOW}Warning: checksums don't match${NC}\"\n return 1\n fi\n}\n\n# Main setup flow\nmain() {\n local mode=${1:-full}\n\n case $mode in\n full)\n # Full setup\n check_wpcli || install_wpcli\n check_wp_core\n local core_status=$?\n if [[ $core_status -eq 2 ]]; then\n download_wp_core\n fi\n configure_wp_config\n install_debug_plugins\n verify_checksums\n ;;\n\n wpcli)\n # Just install WP-CLI\n check_wpcli || install_wpcli\n ;;\n\n plugins)\n # Just install debug plugins\n install_debug_plugins\n ;;\n\n verify)\n # Just verify checksums\n verify_checksums\n ;;\n\n *)\n echo \"Usage: $0 [full|wpcli|plugins|verify]\"\n echo \"\"\n echo \"Commands:\"\n echo \" full Full WordPress setup (default)\"\n echo \" wpcli Install WP-CLI only\"\n echo \" plugins Install debug plugins only\"\n echo \" verify Verify WordPress checksums\"\n exit 1\n ;;\n esac\n\n echo \"\"\n echo -e \"${GREEN}WordPress setup complete!${NC}\"\n echo \"\"\n}\n\nmain \"$@\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":5695,"content_sha256":"3528941519c8191c5003918d4267fcddb37596e5ff7aa319596fb8156e5ffb44"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Docker Local Development Environment Generator","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill helps you create optimized Docker development environments for your projects. It generates ","type":"text"},{"text":"docker-compose.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Dockerfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", and related configurations through an interactive, question-driven workflow.","type":"text"}]},{"type":"paragraph","content":[{"text":"When to use this skill:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setting up a new Docker development environment","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dockerizing an existing project for local development","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Adding services (database, Redis, email testing) to your Docker setup","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Updating or merging with existing Docker configurations","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Key Principle:","type":"text","marks":[{"type":"strong"}]},{"text":" This skill ALWAYS asks questions before making decisions. You will be notified about each configuration choice and can adjust settings to match your exact needs.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Important Notice","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill uses an ","type":"text"},{"text":"interactive approach","type":"text","marks":[{"type":"strong"}]},{"text":". Before generating any files, I will:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run auto-detection scripts to identify your tech stack (saves AI tokens)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Present the detection results for your confirmation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask 10-15 clarifying questions about your preferences","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show you a preview before creating or modifying files","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Why this approach?","type":"text","marks":[{"type":"strong"}]},{"text":" Docker configurations are project-specific. Asking questions ensures the setup matches YOUR requirements, not generic defaults. This prevents issues and saves debugging time later.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"paragraph","content":[{"text":"To generate a Docker development environment:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Navigate to your project root","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tell the AI: \"Use the docker-local-dev skill to set up Docker\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirm or correct the auto-detected tech stack","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Answer the configuration questions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review and approve the generated files","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Supported Tech Stacks","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Stack","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Framework/CMS","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Process Manager","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PHP","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Laravel 10/11/12","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Supervisor","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Queue workers, scheduler","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PHP","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"WordPress","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"WP-CLI","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Debug plugins, error logging","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PHP","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Drupal 10/11","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Drush","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Development services","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PHP","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Joomla 4/5","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"-","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI tools, debug mode","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Node.js","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Express, NestJS, Next.js","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PM2 or Supervisor","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hot reload support","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Django, FastAPI, Flask","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Celery, Supervisor","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"WSGI/ASGI servers","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Unsupported Stack?","type":"text","marks":[{"type":"strong"}]},{"text":" The skill will proceed with generic configuration and suggest contributing improvements. See ","type":"text"},{"text":"CONTRIBUTING.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for details.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Interactive Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 0: Auto-Detection (Script-based)","type":"text"}]},{"type":"paragraph","content":[{"text":"Before using AI","type":"text","marks":[{"type":"strong"}]},{"text":", run detection scripts to save tokens:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# The skill will run this automatically\n./scripts/detect-stack.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Detection checks:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"composer.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Laravel, PHP version","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"wp-config.php","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"wp-content/","type":"text","marks":[{"type":"code_inline"}]},{"text":" → WordPress","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"core/","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"sites/default/","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Drupal","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"configuration.php","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"administrator/","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Joomla","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Node.js, framework, version","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"requirements.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pyproject.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Python, framework","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":", config files → Database type, Redis usage","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Present results to user:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"I detected: Laravel 11 + PHP 8.3 + MySQL + Redis\n\nIs this correct?\n- Yes → proceed with detected settings\n- No → I'll analyze further using AI","type":"text"}]},{"type":"paragraph","content":[{"text":"If stack is NOT officially supported:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"I detected [stack] but this is not in our supported list.\nThe Docker setup may not be optimal.\n\nProceeding with generic configuration...\n\nIf this works for you, please consider contributing to improve support!\nSee: CONTRIBUTING.md","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: Initial Discovery","type":"text"}]},{"type":"paragraph","content":[{"text":"Check for existing Docker files:","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Look for ","type":"text"},{"text":"docker-compose.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"docker-compose.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Dockerfile","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If found, ask:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"I found existing Docker files:\n- docker-compose.yml (modified 2 days ago)\n- Dockerfile\n\nHow should I proceed?\n1. Merge (preserve your custom settings, add new services)\n2. Replace (backup existing, generate fresh)\n3. Cancel (let me review first)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Backup strategy:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Timestamped backups: ","type":"text"},{"text":"docker-compose.yml.backup.2024-01-15-143022","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never overwrite without backup","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1.5: Naming Strategy","type":"text"}]},{"type":"paragraph","content":[{"text":"IMPORTANT: Ask about Docker naming before generating Compose files.","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Container UIs such as OrbStack and Docker Desktop do not only show the container name. They also group containers by the Docker Compose project name and list child rows by service name. In monorepos or on machines with many stacks, generic names like ","type":"text"},{"text":"web","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"app","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"websocket","type":"text","marks":[{"type":"code_inline"}]},{"text":" become hard to scan.","type":"text"}]},{"type":"paragraph","content":[{"text":"Always ask:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"How would you like this stack named in Docker UIs?\n\n1. Project-prefixed names (recommended)\n - Compose project/group: `inventory-office-web`\n - Container name: `inventory-office-web`\n - Service names: explicit when helpful, otherwise role-based inside the project\n\n2. Minimal names\n - Compose project/group: folder name\n - Container name: default Compose-generated name\n - Service names: short generic names like `web`, `app`, `db`","type":"text"}]},{"type":"paragraph","content":[{"text":"Recommended defaults for monorepos or multi-project machines:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set the top-level Compose ","type":"text"},{"text":"name:","type":"text","marks":[{"type":"code_inline"}]},{"text":" field to an explicit project slug such as ","type":"text"},{"text":"inventory-api","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"inventory-office-web","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"inventory-websocket","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set ","type":"text"},{"text":"container_name:","type":"text","marks":[{"type":"code_inline"}]},{"text":" to the same explicit prefix pattern, for example ","type":"text"},{"text":"inventory-api-app","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"inventory-office-web","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"inventory-websocket","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer explicit single-service names when the whole stack is one app/service, for example ","type":"text"},{"text":"office-web:","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"websocket:","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For multi-service stacks, role-based service names are acceptable under a clear project/group name, for example ","type":"text"},{"text":"app","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"web","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"db","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"redis","type":"text","marks":[{"type":"code_inline"}]},{"text":" inside ","type":"text"},{"text":"inventory-api","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Rules:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never rely on the folder name alone for Compose grouping in monorepos","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer kebab-case names","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep the same prefix across project name, image tags, and container names when possible","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If an existing stack already has a stable naming convention, preserve it unless the user asks to rename it","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: Tech Stack Confirmation","type":"text"}]},{"type":"paragraph","content":[{"text":"If auto-detection succeeded:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Detected configuration:\n- Framework: Laravel 11\n- PHP Version: 8.3\n- Database: MySQL (from .env DB_CONNECTION)\n- Redis: Yes (from .env REDIS_HOST)\n- Queue: Yes (jobs table detected)\n\nPlease confirm or adjust these settings.","type":"text"}]},{"type":"paragraph","content":[{"text":"If auto-detection failed or unclear:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"What is your primary tech stack?\n\n1. PHP/Laravel\n2. WordPress\n3. Drupal\n4. Joomla\n5. Node.js (Express/NestJS/Next.js)\n6. Python (Django/FastAPI/Flask)\n7. Other (I'll try generic configuration)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: CMS-Specific Questions","type":"text"}]},{"type":"paragraph","content":[{"text":"WordPress:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"WordPress Development Options:\n\n1. Install debug plugins?\n - Query Monitor (SQL queries, hooks, conditionals)\n - Debug Bar (debug info in admin bar)\n\n2. Enable WP_DEBUG and error logging?\n - WP_DEBUG = true\n - WP_DEBUG_LOG = true\n - SCRIPT_DEBUG = true","type":"text"}]},{"type":"paragraph","content":[{"text":"Drupal:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Drupal Development Options:\n\n1. Install Drush globally in container?\n2. Enable development services (verbose errors, twig debug)?\n3. Disable caching for development?","type":"text"}]},{"type":"paragraph","content":[{"text":"Joomla:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Joomla Development Options:\n\n1. Enable debug mode?\n2. Install Joomla CLI tools?","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3.5: Existing Docker Images Scan","type":"text"}]},{"type":"paragraph","content":[{"text":"Before suggesting service versions","type":"text","marks":[{"type":"strong"}]},{"text":", check locally available images to save disk space:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# The skill will run this automatically\n./scripts/detect-images.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Present results to user:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"I found these images already on your machine:\n\nDatabases:\n- mysql:8.0.35 (2.3 GB)\n- mariadb:11.2 (1.1 GB)\n\nUsing existing images saves disk space and download time.\n\nWhich database would you like to use?\n1. mysql:8.0.35 (already downloaded - saves 2.3 GB)\n2. mariadb:11.2 (already downloaded - saves 1.1 GB)\n3. Different version (will download new image)\n → What version do you need for production compatibility?","type":"text"}]},{"type":"paragraph","content":[{"text":"If no existing images found:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"No database images found locally.\n\nWhich database would you like to use?\n1. MySQL 8.0 (recommended for Laravel/WordPress)\n2. MariaDB 11 (MySQL-compatible, smaller)\n3. PostgreSQL 16 (if your app requires it)","type":"text"}]},{"type":"paragraph","content":[{"text":"Same approach for other services:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check for existing Redis, PHP, Node, Nginx, Mailpit/MailHog images","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Suggest matching versions when available","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always offer \"different version\" option for production compatibility","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Service Configuration (Smart Recommendations)","type":"text"}]},{"type":"paragraph","content":[{"text":"IMPORTANT: Check actual usage before recommending services.","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Before suggesting any optional service, verify if it's actually being used in the project:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check .env for actual service usage\ngrep -E '^(CACHE_DRIVER|CACHE_STORE|SESSION_DRIVER|QUEUE_CONNECTION|MAIL_MAILER)=' .env","type":"text"}]},{"type":"paragraph","content":[{"text":"Database Selection","type":"text","marks":[{"type":"strong"}]},{"text":" (always needed, use images from Phase 3.5):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Which database would you like to use?\n\n1. MySQL 8.0 (recommended for Laravel/WordPress)\n2. MariaDB 11 (MySQL-compatible, lighter)\n3. PostgreSQL 16 (required for some apps)","type":"text"}]},{"type":"paragraph","content":[{"text":"Redis Configuration","type":"text","marks":[{"type":"strong"}]},{"text":" (check actual usage first):","type":"text"}]},{"type":"paragraph","content":[{"text":"First, check if Redis is actually used:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"grep -E '^(CACHE_DRIVER|SESSION_DRIVER|QUEUE_CONNECTION)=' .env","type":"text"}]},{"type":"paragraph","content":[{"text":"If Redis is NOT in use","type":"text","marks":[{"type":"strong"}]},{"text":" (CACHE_DRIVER=file, SESSION_DRIVER=file, QUEUE_CONNECTION=sync):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"I noticed your .env configuration:\n- CACHE_DRIVER=file (not using Redis for cache)\n- SESSION_DRIVER=file (not using Redis for sessions)\n- QUEUE_CONNECTION=sync (not using Redis for queues)\n\nRedis is not currently used in your project.\nDo you want to add Redis anyway?\n1. No, skip Redis (recommended based on your config)\n2. Yes, I plan to switch to Redis later","type":"text"}]},{"type":"paragraph","content":[{"text":"If Redis IS in use","type":"text","marks":[{"type":"strong"}]},{"text":" (any of the above = redis):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Do you need Redis?\n\n1. Yes, for caching only\n2. Yes, for caching + sessions\n3. Yes, for caching + sessions + queues\n4. No, I don't need Redis","type":"text"}]},{"type":"paragraph","content":[{"text":"Email Testing","type":"text","marks":[{"type":"strong"}]},{"text":" (check actual usage first):","type":"text"}]},{"type":"paragraph","content":[{"text":"First, check MAIL_MAILER setting:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"grep -E '^MAIL_MAILER=' .env","type":"text"}]},{"type":"paragraph","content":[{"text":"If using log or array mailer:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Your MAIL_MAILER is set to 'log' (emails logged, not sent).\nDo you want to add email testing service anyway?\n1. No, skip email testing (recommended based on your config)\n2. Yes, add Mailpit for testing","type":"text"}]},{"type":"paragraph","content":[{"text":"If using smtp or other mailer:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Which email testing service would you prefer?\n\n1. Mailpit (modern, actively maintained, recommended)\n - Web UI: http://localhost:8025\n - SMTP: localhost:1025\n\n2. MailHog (widely used, stable)\n - Web UI: http://localhost:8025\n - SMTP: localhost:1025\n\n3. None (I'll configure email separately)","type":"text"}]},{"type":"paragraph","content":[{"text":"Background Task Processing","type":"text","marks":[{"type":"strong"}]},{"text":" (check actual usage first):","type":"text"}]},{"type":"paragraph","content":[{"text":"For Laravel, check if queues are actually used:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"grep -E '^QUEUE_CONNECTION=' .env\n# Also check if jobs table exists or queue jobs are defined","type":"text"}]},{"type":"paragraph","content":[{"text":"If QUEUE_CONNECTION=sync:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Your QUEUE_CONNECTION is set to 'sync' (no background processing).\nDo you need background task processing anyway?\n1. No, skip queue workers (recommended based on your config)\n2. Yes, I plan to switch to async queues later","type":"text"}]},{"type":"paragraph","content":[{"text":"If QUEUE_CONNECTION=database/redis:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Do you need background task processing?\n\n1. Queue workers only (Supervisor)\n2. Scheduler only (cron replacement via Supervisor)\n3. Both queue workers and scheduler\n4. No background processing needed","type":"text"}]},{"type":"paragraph","content":[{"text":"For Node.js:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"How do you want to manage Node.js processes?\n\n1. PM2 (process manager with clustering, recommended)\n2. Supervisor (simple process monitoring)\n3. Direct node command (development only)","type":"text"}]},{"type":"paragraph","content":[{"text":"For Python:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Background task processing options:\n\n1. Celery workers (for Django/FastAPI async tasks)\n2. Supervisor for scheduled tasks (cron replacement)\n3. Both Celery and scheduled tasks\n4. No background processing needed","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 5: Port Exposure & Configuration","type":"text"}]},{"type":"paragraph","content":[{"text":"IMPORTANT: Ask about reverse proxy first before exposing ports.","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Reverse Proxy Check:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Are you using a reverse proxy (Nginx Proxy Manager, Traefik, Caddy)?\n\n1. Yes, I'm using a reverse proxy\n → Ports will remain internal only\n → Services communicate via Docker network\n → You'll configure the proxy to route to containers\n\n2. No, I want to expose ports directly\n → I'll help you choose which ports to expose","type":"text"}]},{"type":"paragraph","content":[{"text":"If using reverse proxy (Option 1):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Since you're using a reverse proxy, ports will be internal only.\n\nDo you still want to expose the database port for external tools (DBeaver/DataGrip)?\n1. Yes, expose database port (3306/5432) for SQL tools\n2. No, keep everything internal","type":"text"}]},{"type":"paragraph","content":[{"text":"If NOT using reverse proxy (Option 2), then ask port strategy:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"How do you want to expose ports?\n\n1. Minimal (recommended for most projects)\n - Nginx: 8080 (web access)\n - Database: 3306/5432 (for SQL tools like DBeaver/DataGrip)\n\n2. Full exposure (all services accessible)\n - Nginx: 8080\n - Database: 3306/5432\n - Redis: 6379\n - Mail UI: 8025\n - PHP-FPM: 9000 (if needed)","type":"text"}]},{"type":"paragraph","content":[{"text":"Port Availability Check:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Checking port availability...\n\nPort 8080: Available\nPort 3306: IN USE (another MySQL instance)\n → Suggesting 3307 instead\n\nPort 6379: Available","type":"text"}]},{"type":"paragraph","content":[{"text":"Configuration Storage:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Where do you want to store configuration?\n\n1. .env file (recommended)\n - Easier to change ports and settings\n - Keep secrets out of docker-compose.yml\n - Example: APP_PORT=8080, DB_PORT=3306\n\n2. Directly in docker-compose.yml\n - Simpler for basic setups\n - All config in one place\n - Less flexible for different environments","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 6: Network Configuration","type":"text"}]},{"type":"paragraph","content":[{"text":"First, scan for existing Docker networks and reverse proxies:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# The skill will run this automatically\n./scripts/detect-network.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"If reverse proxy container detected (Nginx Proxy Manager, Traefik, Caddy):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"I scanned your Docker environment:\n\nReverse proxy found:\n- Container: 'nginx-proxy-manager' on network: 'npm_default'\n\nDo you want to connect this project to 'npm_default'?\n1. Yes, use 'npm_default' for reverse proxy routing (recommended)\n → No port exposure needed\n → Configure routing in your proxy dashboard\n\n2. No, use isolated network\n → I'll ask about port exposure","type":"text"}]},{"type":"paragraph","content":[{"text":"If NO reverse proxy detected:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"No reverse proxy containers detected (Nginx Proxy Manager, Traefik, Caddy).\n\nAvailable Docker networks:\n- myapp_default\n- shared_services\n\nDo you want to:\n1. Create isolated network for this project (recommended)\n2. Use existing network: [select from list]\n3. Create shared network for multiple projects","type":"text"}]},{"type":"paragraph","content":[{"text":"Multiple Projects (if no proxy detected):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Are you running multiple Docker projects on this machine?\n\n1. Yes, I have multiple projects\n → Consider Nginx Proxy Manager for:\n - Custom domains (myapp.local, api.local)\n - Automatic SSL certificates\n - Centralized reverse proxy\n\n2. No, this is my only Docker project\n → Use isolated project network","type":"text"}]},{"type":"paragraph","content":[{"text":"Microservices/API Connection:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Does this project need to connect to other Docker services?\n\n1. Yes, I have other Docker services (APIs, microservices)\n → Create external shared network\n → Services can communicate via container names\n\n2. No, this project is standalone\n → Use project-isolated network","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 7: Volume Mount Strategy","type":"text"}]},{"type":"paragraph","content":[{"text":"Always explain options:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"How would you like to mount your source code?\n\n1. Bind mount (recommended for development)\n - Your local files sync to container immediately\n - Changes reflect instantly without rebuild\n - Best for: Active development, hot reload\n\n2. Named volume (better performance)\n - Files copied into Docker volume\n - Faster file operations (especially on macOS)\n - Requires rebuild to see code changes\n - Best for: Testing, CI/CD\n\nNote: Bind mounts have ~10-20% slower file I/O on macOS,\nbut the instant sync is worth it for development.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 8: Generation & Verification","type":"text"}]},{"type":"paragraph","content":[{"text":"Docker Compose Version Note:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do NOT include ","type":"text"},{"text":"version:","type":"text","marks":[{"type":"code_inline"}]},{"text":" at the top of docker-compose.yml","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Docker Compose v2 deprecated this field","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Modern compose files don't need it","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Compose naming note:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer setting top-level ","type":"text"},{"text":"name:","type":"text","marks":[{"type":"code_inline"}]},{"text":" explicitly so Docker UIs show a stable, searchable group name","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not leave Compose project naming to the working directory when generating configs for monorepos","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"container_name:","type":"text","marks":[{"type":"code_inline"}]},{"text":" is used, keep it aligned with the project/group naming convention chosen in Phase 1.5","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"File generation order:","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create backup of existing files (if any)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate ","type":"text"},{"text":".env.docker","type":"text","marks":[{"type":"code_inline"}]},{"text":" or update ","type":"text"},{"text":".env","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate ","type":"text"},{"text":"Dockerfile","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate ","type":"text"},{"text":"docker-compose.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":" (without version field, with explicit top-level ","type":"text"},{"text":"name:","type":"text","marks":[{"type":"code_inline"}]},{"text":" when requested or when working in a monorepo)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate Nginx configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate Supervisor/PM2 configuration (if needed)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Create helper scripts","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"After docker-compose up (AUTOMATIC):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# These run automatically after containers start\n\n# 1. Verify all ports are available\n./scripts/port-check.sh\n\n# 2. Health check all services\n./scripts/health-check.sh\n\n# 3. Test database with simple CRUD\n./scripts/db-test.sh\n\n# 4. Generate usage documentation\n# Creates USAGE.md with commands for your stack","type":"text"}]},{"type":"paragraph","content":[{"text":"Health Check Output:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Docker Local Dev - Health Check\n================================\n\nChecking Nginx.............. OK\nChecking PHP-FPM............ OK\nChecking MySQL.............. OK\nChecking Redis.............. OK\nChecking Mailpit............ OK\nChecking Queue Worker....... OK\n\nDatabase CRUD Test:\n- CREATE table............ OK\n- INSERT data............. OK\n- UPDATE data............. OK\n- DELETE data............. OK\n- DROP table.............. OK\n\nAll services are healthy!\n\nYour development environment is ready:\n- Web: http://localhost:8080\n- Database: localhost:3306 (user: root, pass: secret)\n- Mail UI: http://localhost:8025","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 9: Documentation Generation","type":"text"}]},{"type":"paragraph","content":[{"text":"Automatically creates USAGE.md:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"# Docker Development Environment\n\n## Quick Commands\n\nStart containers:\ndocker compose up -d\n\nStop containers:\ndocker compose down\n\nView logs:\ndocker compose logs -f\n\n## Accessing Services\n\n| Service | URL/Host | Credentials |\n|---------|----------|-------------|\n| Web | http://localhost:8080 | - |\n| Database | localhost:3306 | root / secret |\n| Redis | localhost:6379 | - |\n| Mail UI | http://localhost:8025 | - |\n\n## Container Networking (Important)\n\nWhen the app runs inside Docker, `localhost` points to the app container. To connect to other services, use the Docker Compose service name (for example `db`, `redis`, `mailpit`) instead of `localhost`.\n\n## Stack-Specific Commands\n\n### Laravel\ndocker compose exec app php artisan migrate\ndocker compose exec app php artisan queue:work\n\n### WordPress\ndocker compose exec app wp plugin list\ndocker compose exec app wp cache flush","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Merge Strategy","type":"text"}]},{"type":"paragraph","content":[{"text":"When merging with existing Docker files:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Preserve user customizations:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Custom environment variables","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Volume mounts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Network configurations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Port mappings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Existing Compose project naming (","type":"text"},{"text":"name:","type":"text","marks":[{"type":"code_inline"}]},{"text":"), service naming, and container naming unless the user explicitly asks to normalize them","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add new services:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Only add services that don't exist","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Don't modify existing service definitions","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show diff before applying:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"diff"},"content":[{"text":"+ redis:\n+ image: redis:alpine\n+ volumes:\n+ - redis_data:/data\n\n services:\n app:\n # existing config preserved","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Require confirmation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"These changes will be applied:\n- Add Redis service\n- Add redis_data volume\n- Update app service to depend on Redis\n\nProceed? [y/N]","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Health Check Protocol","type":"text"}]},{"type":"paragraph","content":[{"text":"Services are verified in this order:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Database","type":"text","marks":[{"type":"strong"}]},{"text":" (MySQL/PostgreSQL)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Connection test: ","type":"text"},{"text":"mysqladmin ping","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"pg_isready","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CRUD test: CREATE/INSERT/UPDATE/DELETE on test table","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Web Server","type":"text","marks":[{"type":"strong"}]},{"text":" (Nginx)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HTTP request to localhost","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Expect 200, 301, or 302 response","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Application","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Stack-specific checks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Laravel: ","type":"text"},{"text":"php artisan about","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WordPress: ","type":"text"},{"text":"wp core version","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Django: ","type":"text"},{"text":"python manage.py check","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Redis","type":"text","marks":[{"type":"strong"}]},{"text":" (if enabled)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"redis-cli ping","type":"text","marks":[{"type":"code_inline"}]},{"text":" → expect PONG","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Queue Worker","type":"text","marks":[{"type":"strong"}]},{"text":" (if enabled)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Process verification","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Test job processing (optional)","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Email Service","type":"text","marks":[{"type":"strong"}]},{"text":" (if enabled)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SMTP connection test","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Web UI accessibility","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Port Already in Use","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Error: Port 3306 is already in use\n\nSolutions:\n1. Stop the conflicting service\n2. Use a different port (skill will suggest alternatives)\n3. Check: lsof -i :3306","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Database Connection Failed","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Error: Cannot connect to MySQL\n\nCheck:\n1. Is the container running? docker compose ps\n2. Is the port exposed? docker compose port db 3306\n3. Are credentials correct? Check .env or docker-compose.yml","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Permission Denied","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Error: Permission denied on mounted volume\n\nSolutions:\n1. Check file ownership: ls -la\n2. Match container user ID: Add user: \"1000:1000\" to service\n3. Use :cached or :delegated mount options on macOS","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference Documentation","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tech Stack Detection","type":"text","marks":[{"type":"link","attrs":{"href":"./references/tech-stack-detection.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Service Configuration Guide","type":"text","marks":[{"type":"link","attrs":{"href":"./references/service-configuration-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CMS Configuration Guide","type":"text","marks":[{"type":"link","attrs":{"href":"./references/cms-configuration-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Networking & Ports Guide","type":"text","marks":[{"type":"link","attrs":{"href":"./references/networking-ports-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Merge & Backup Strategy","type":"text","marks":[{"type":"link","attrs":{"href":"./references/merge-backup-strategy.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Health Check Patterns","type":"text","marks":[{"type":"link","attrs":{"href":"./references/health-check-patterns.md","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Contributing","type":"text"}]},{"type":"paragraph","content":[{"text":"If your tech stack is not fully supported, please consider contributing!","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"CONTRIBUTING.md","type":"text","marks":[{"type":"link","attrs":{"href":"./assets/templates/docs/CONTRIBUTING.md.template","title":null}}]},{"text":" for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"How to add support for new tech stacks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Template structure requirements","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Testing guidelines","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"docker-local-dev","author":"@skillopedia","source":{"stars":60,"repo_name":"awesome-ai-agent-skills","origin_url":"https://github.com/thienanblog/awesome-ai-agent-skills/blob/HEAD/skills/docker-local-dev/SKILL.md","repo_owner":"thienanblog","body_sha256":"96ac6d611a8be07d9d25825774f600b5c752600468be26fc19c212efb3f7a4e4","cluster_key":"f885e3de2e62b3047624311ab5cb49c9de149b378e0b1c0e803f1a556d929392","clean_bundle":{"format":"clean-skill-bundle-v1","source":"thienanblog/awesome-ai-agent-skills/skills/docker-local-dev/SKILL.md","attachments":[{"id":"be68a6cc-2d85-5aec-88ca-bf76382a0876","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/be68a6cc-2d85-5aec-88ca-bf76382a0876/attachment.yml","path":"assets/templates/docker-compose/base.yml","size":301,"sha256":"74432b520267fc9756953ea5e230592b13a5f902e2a79076d7781fb6258681af","contentType":"application/yaml; charset=utf-8"},{"id":"3052c823-3353-5334-852f-dddb38420aa1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3052c823-3353-5334-852f-dddb38420aa1/attachment.yml","path":"assets/templates/docker-compose/node.yml","size":4477,"sha256":"0380bad4e84ae4b6597a2f0440818fc3c64d4d5f89a93c7378b205223be8b424","contentType":"application/yaml; charset=utf-8"},{"id":"69127fd4-927f-5c43-9fb4-ccd8239d807f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/69127fd4-927f-5c43-9fb4-ccd8239d807f/attachment.yml","path":"assets/templates/docker-compose/php-drupal.yml","size":3552,"sha256":"18520af5cc494b2b122976b1d89004373f778a77cdb3113e4a1097f53cec7328","contentType":"application/yaml; charset=utf-8"},{"id":"a599d0cf-8383-5b50-b595-7ec106eb1ad7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a599d0cf-8383-5b50-b595-7ec106eb1ad7/attachment.yml","path":"assets/templates/docker-compose/php-joomla.yml","size":3001,"sha256":"19f4b01b55da8b7bc783065e3ae781fad470710669a31d0bfe5125db84c9d44d","contentType":"application/yaml; charset=utf-8"},{"id":"bd275575-7420-5d06-b7cd-b81da047335a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/bd275575-7420-5d06-b7cd-b81da047335a/attachment.yml","path":"assets/templates/docker-compose/php-laravel.yml","size":1697,"sha256":"7d9067391ee27a5bc99786367e52c1bacbdd81f2b24df9f6e64b13e1379c6755","contentType":"application/yaml; charset=utf-8"},{"id":"bd5778d9-5208-5643-8ef6-9a424d95b3df","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/bd5778d9-5208-5643-8ef6-9a424d95b3df/attachment.yml","path":"assets/templates/docker-compose/php-wordpress.yml","size":4253,"sha256":"4ac553743a8fe7f2cfe330043888a9ccc2bd394dedb2dd8d7a317fb28ea01fe0","contentType":"application/yaml; charset=utf-8"},{"id":"7f146909-4f65-5ff4-b0ca-5fe6aeb14c46","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7f146909-4f65-5ff4-b0ca-5fe6aeb14c46/attachment.yml","path":"assets/templates/docker-compose/python.yml","size":4690,"sha256":"3d78bc78c37c5cd566577f3945c6825bca1fb419523b300a97c1b608155f11c2","contentType":"application/yaml; charset=utf-8"},{"id":"90eab21a-83c8-51ed-9899-353949d55137","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/90eab21a-83c8-51ed-9899-353949d55137/attachment.dockerfile","path":"assets/templates/dockerfile/node.dockerfile","size":856,"sha256":"94f9a8ffa2671fdcef3783daee1f7663610d47e57f00ed43a3da0ab054e12675","contentType":"text/plain; charset=utf-8"},{"id":"6ddacad0-deee-504d-835a-ab67194d91e1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6ddacad0-deee-504d-835a-ab67194d91e1/attachment.dockerfile","path":"assets/templates/dockerfile/php-drupal.dockerfile","size":2105,"sha256":"2e9a9bd43c00230b19312b41e2647cf3fd0794899eff22cf7072b736c8d326cc","contentType":"text/plain; charset=utf-8"},{"id":"b275c65c-7acb-59b7-a176-bf48167bcc63","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b275c65c-7acb-59b7-a176-bf48167bcc63/attachment.dockerfile","path":"assets/templates/dockerfile/php-joomla.dockerfile","size":1833,"sha256":"2660b3b1921a6fbabb97d5a173a1ce75ab8d39ba77a38d025defad2bdf0af575","contentType":"text/plain; charset=utf-8"},{"id":"91305258-c795-5a50-b45c-2dbe5dac277c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/91305258-c795-5a50-b45c-2dbe5dac277c/attachment.dockerfile","path":"assets/templates/dockerfile/php-laravel.dockerfile","size":2072,"sha256":"e75b75ce9aceecc4440e7279d925ea3bc074514b41a5989920ccb81e44714c77","contentType":"text/plain; charset=utf-8"},{"id":"0d3ded98-6b76-52b4-833c-671213f3505e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0d3ded98-6b76-52b4-833c-671213f3505e/attachment.dockerfile","path":"assets/templates/dockerfile/php-wordpress.dockerfile","size":2163,"sha256":"2eb5d2122ecbaf64417864d25d9adaa50137c80499ee987419b8cd042c63fb30","contentType":"text/plain; charset=utf-8"},{"id":"73504bd4-2bca-5e88-862f-ff0a1fee65b0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/73504bd4-2bca-5e88-862f-ff0a1fee65b0/attachment.dockerfile","path":"assets/templates/dockerfile/python-django.dockerfile","size":1374,"sha256":"32b0e6e3c5f02809e3fd3ceb3461785dee631712b6186d471eb973acaa6b5b8c","contentType":"text/plain; charset=utf-8"},{"id":"c7955da8-f9d8-5320-8cda-2d8a67aaa69f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c7955da8-f9d8-5320-8cda-2d8a67aaa69f/attachment.dockerfile","path":"assets/templates/dockerfile/python-fastapi.dockerfile","size":1293,"sha256":"14aa6dbde6987e4c4f6dbb0790a99a4e6073b72df4fd86fd2cf07cb100166d58","contentType":"text/plain; charset=utf-8"},{"id":"2c645ae3-3c23-5cb5-aed3-47c67c6ef847","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2c645ae3-3c23-5cb5-aed3-47c67c6ef847/attachment.template","path":"assets/templates/docs/COMMANDS.md.template","size":1780,"sha256":"d91130d7cba5169bf295046fec9a7c37a89731e34c10d2729c052ffd9d0bd994","contentType":"text/plain; charset=utf-8"},{"id":"73d1e49e-e69d-59fd-9235-e43605b9be4f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/73d1e49e-e69d-59fd-9235-e43605b9be4f/attachment.template","path":"assets/templates/docs/CONTRIBUTING.md.template","size":2226,"sha256":"7fc4e0868e80d4d7a101ef0578936ca5f6a70e5ee714bee920016ceb3018bd7c","contentType":"text/plain; charset=utf-8"},{"id":"12b6da49-856b-5e9c-9fa5-84ef30f6b651","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/12b6da49-856b-5e9c-9fa5-84ef30f6b651/attachment.md","path":"assets/templates/docs/SUPPORTED-STACKS.md","size":2322,"sha256":"149eeddaa4c8210ffbfea9fb9a7aa00a152acc252f3bdce1b70c35a8384e5ccf","contentType":"text/markdown; charset=utf-8"},{"id":"9347687d-9286-5cf6-8905-152c31e23fc0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9347687d-9286-5cf6-8905-152c31e23fc0/attachment.template","path":"assets/templates/docs/USAGE.md.template","size":2651,"sha256":"b4bcbf6cf611b81edafe53a3ac1079cac61f929509dba66e607af44873562255","contentType":"text/plain; charset=utf-8"},{"id":"5865febd-b2ce-5b8d-b4e0-62fc7413c54c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5865febd-b2ce-5b8d-b4e0-62fc7413c54c/attachment.conf","path":"assets/templates/nginx/drupal.conf","size":2768,"sha256":"253adf26daadfc2f152d9b1e6a8bad83b520178a10c279492a1410477bfffc57","contentType":"text/plain; charset=utf-8"},{"id":"44b2ea2e-4821-5dc1-9df5-8b0bb79905e9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/44b2ea2e-4821-5dc1-9df5-8b0bb79905e9/attachment.conf","path":"assets/templates/nginx/joomla.conf","size":2243,"sha256":"246d45d33811b2ec5cda381eef46ac784ca52bf1ceb53fd30c1c1af364d32b6a","contentType":"text/plain; charset=utf-8"},{"id":"e4b28327-6617-566d-80b8-105cd616f859","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e4b28327-6617-566d-80b8-105cd616f859/attachment.conf","path":"assets/templates/nginx/laravel.conf","size":567,"sha256":"f101404cb353563080260f3205b1354ba400b3b0b041ae3957ad82118788200e","contentType":"text/plain; charset=utf-8"},{"id":"59dc5239-d2de-580b-9887-6a9f980692d8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/59dc5239-d2de-580b-9887-6a9f980692d8/attachment.conf","path":"assets/templates/nginx/node-proxy.conf","size":2630,"sha256":"dec6c6c77ee3224b074374e8b5d1d60adf8bd909f1fae092f57ae25195c19b32","contentType":"text/plain; charset=utf-8"},{"id":"040b453e-34a6-50c1-baea-8d202bb37a31","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/040b453e-34a6-50c1-baea-8d202bb37a31/attachment.conf","path":"assets/templates/nginx/python-wsgi.conf","size":2900,"sha256":"083f08662b4d8881a8b2830ec801c3a8a809a0c87079d53e07e862714c49be58","contentType":"text/plain; charset=utf-8"},{"id":"315bbaf3-7dc0-5618-8436-3196baa1d4fb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/315bbaf3-7dc0-5618-8436-3196baa1d4fb/attachment.conf","path":"assets/templates/nginx/wordpress.conf","size":703,"sha256":"99aa5ea28dc56d0f6d6b3ef8f5405986fbd954063f5adc8ca31db89c057f5c95","contentType":"text/plain; charset=utf-8"},{"id":"1f273f79-a589-5ab0-a346-bd15dc4bd102","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1f273f79-a589-5ab0-a346-bd15dc4bd102/attachment.conf","path":"assets/templates/supervisor/generic-cron.conf","size":145,"sha256":"86896cd0e26762a5f268086eafed46b0d434bb2a90aa1a1346682fe50bb74d0c","contentType":"text/plain; charset=utf-8"},{"id":"2717a377-ccaa-5a42-b940-76a6423a0bbb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2717a377-ccaa-5a42-b940-76a6423a0bbb/attachment.conf","path":"assets/templates/supervisor/laravel-scheduler.conf","size":263,"sha256":"9fb90757a2132f5e517c055a9d5828f6f29835b18d0d65e15e339e95283a88a5","contentType":"text/plain; charset=utf-8"},{"id":"07e147c9-11ce-5c1e-b05d-0a2c1a8282d6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/07e147c9-11ce-5c1e-b05d-0a2c1a8282d6/attachment.conf","path":"assets/templates/supervisor/laravel-worker.conf","size":333,"sha256":"279a0cd92718c862b45f8069cded8573b46513fef8b5a6371a1725c753f7c35a","contentType":"text/plain; charset=utf-8"},{"id":"a713f7d3-2b1d-5aae-935f-e3ea3e2ff4a0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a713f7d3-2b1d-5aae-935f-e3ea3e2ff4a0/attachment.conf","path":"assets/templates/supervisor/node-cron.conf","size":1923,"sha256":"2238221015899cc4c483069255b483576aea7593882f5c4cf66b67e0482af5be","contentType":"text/plain; charset=utf-8"},{"id":"8dde7ff8-f4cc-5764-bd88-7a43b6a5e75d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8dde7ff8-f4cc-5764-bd88-7a43b6a5e75d/attachment.conf","path":"assets/templates/supervisor/node-pm2.conf","size":1142,"sha256":"6535371511f84994969400a5bbc05fd679a1f02fac303de0e384669a8c91c411","contentType":"text/plain; charset=utf-8"},{"id":"ba1fb137-35a8-59ea-b1ee-7b3e67da6eb3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ba1fb137-35a8-59ea-b1ee-7b3e67da6eb3/attachment.conf","path":"assets/templates/supervisor/python-celery.conf","size":2207,"sha256":"61fa3ccd6c8830c9261650fe55dd187d7ef82cf30a141b054d4aff1a14ce95f3","contentType":"text/plain; charset=utf-8"},{"id":"b3142361-2015-57d4-bd1d-d01b4f9e022d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b3142361-2015-57d4-bd1d-d01b4f9e022d/attachment.conf","path":"assets/templates/supervisor/python-cron.conf","size":2525,"sha256":"9bcd194cdf95fa14fda09e58875747146eb3dd70c6ecae1143923ee0405cd379","contentType":"text/plain; charset=utf-8"},{"id":"a28c6fdc-9eaa-5fe3-9d32-d85c11c20608","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a28c6fdc-9eaa-5fe3-9d32-d85c11c20608/attachment.txt","path":"assets/templates/wordpress/debug-plugins.txt","size":441,"sha256":"c6b8df98d881c48311d0150fdd730adfa4afebd8c97d3074179cba3a0ab80c92","contentType":"text/plain; charset=utf-8"},{"id":"2bf35919-7bbb-5912-b5c1-78f581a92730","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2bf35919-7bbb-5912-b5c1-78f581a92730/attachment.php","path":"assets/templates/wordpress/wp-config-docker.php","size":1819,"sha256":"f320fa07c73e26f62489a1a074f3f9f8ce864bc7a1454e176f1480775b7abe0c","contentType":"text/plain; charset=utf-8"},{"id":"f58c648f-2487-5cab-b672-0c3ace6a4bdd","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f58c648f-2487-5cab-b672-0c3ace6a4bdd/attachment.md","path":"references/cms-configuration-guide.md","size":11114,"sha256":"e39d535af1be7c8cf6920dc338ffa0f997282b68a1912f0766893458f8581eef","contentType":"text/markdown; charset=utf-8"},{"id":"1eb51393-aef5-59d4-ac75-7f475338937a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1eb51393-aef5-59d4-ac75-7f475338937a/attachment.md","path":"references/health-check-patterns.md","size":8206,"sha256":"0163c592dc062b535712b870c912742ff1be0ec451cfa49f9c5b9098656e69e2","contentType":"text/markdown; charset=utf-8"},{"id":"f0e4dccd-2d21-5595-ac04-185a8fdc5497","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f0e4dccd-2d21-5595-ac04-185a8fdc5497/attachment.md","path":"references/merge-backup-strategy.md","size":5991,"sha256":"1718c029cf81700e63b04dd19adb314baf755040a308c4d36cd489ce84f3616d","contentType":"text/markdown; charset=utf-8"},{"id":"a8cbd1ea-5311-5c77-8f98-73d8b21f9947","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a8cbd1ea-5311-5c77-8f98-73d8b21f9947/attachment.md","path":"references/networking-ports-guide.md","size":7994,"sha256":"7e915ac2839d36b38955819618ad3e816ccf7902e7de7309188907ae53c6981a","contentType":"text/markdown; charset=utf-8"},{"id":"54584cfb-7784-5641-95de-b1d2ed90ff64","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/54584cfb-7784-5641-95de-b1d2ed90ff64/attachment.md","path":"references/service-configuration-guide.md","size":8894,"sha256":"0c0d043f4d1f926528303032745d7b9d523a1f1b81a821989c0946a649a02da0","contentType":"text/markdown; charset=utf-8"},{"id":"518893f6-36da-57eb-ac0f-1e5eddded2ee","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/518893f6-36da-57eb-ac0f-1e5eddded2ee/attachment.md","path":"references/tech-stack-detection.md","size":5449,"sha256":"26741bd0043ecb13a3cef950085d48001c70086c729e1582f8779a3ffccae77c","contentType":"text/markdown; charset=utf-8"},{"id":"a42ca78b-926b-5ace-b308-bf2a1efd959b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a42ca78b-926b-5ace-b308-bf2a1efd959b/attachment.sh","path":"scripts/db-test.sh","size":5772,"sha256":"f62892405bb51ef7411e20d803b014cfcdf70fbc77738b9c1c75a55b96e5c926","contentType":"application/x-sh; charset=utf-8"},{"id":"b0c6cc0c-8f1f-5b4f-abf7-63bbb2367ebc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b0c6cc0c-8f1f-5b4f-abf7-63bbb2367ebc/attachment.sh","path":"scripts/detect-images.sh","size":2188,"sha256":"c9006406f4f8d955332d78263ee505a5bc4ef3b1a83782f67bebf94fd5b1dd9f","contentType":"application/x-sh; charset=utf-8"},{"id":"dea93fc9-3d7a-5e2f-a2e4-fec9a67168ee","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dea93fc9-3d7a-5e2f-a2e4-fec9a67168ee/attachment.sh","path":"scripts/detect-network.sh","size":2872,"sha256":"308643fa7eb50aef1472879e891e7c69d810bb1d851cb87dd7a3688dc69fee27","contentType":"application/x-sh; charset=utf-8"},{"id":"87e07468-38c6-5deb-8a8a-71ecf1817ddb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/87e07468-38c6-5deb-8a8a-71ecf1817ddb/attachment.sh","path":"scripts/detect-stack.sh","size":12631,"sha256":"469538e7522352758c6e8cf065b4fc62e7ba1cf4ae2eda578583a03be6436509","contentType":"application/x-sh; charset=utf-8"},{"id":"dd47cc06-34f1-5a76-b34e-88e62f732db4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dd47cc06-34f1-5a76-b34e-88e62f732db4/attachment.sh","path":"scripts/health-check.sh","size":7045,"sha256":"51c34c0c35ed2c92742eaf2c2abda2076104611c55d71e26537dedc596d7db9c","contentType":"application/x-sh; charset=utf-8"},{"id":"3603a59d-4591-5f54-a87f-c0384c01c403","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3603a59d-4591-5f54-a87f-c0384c01c403/attachment.sh","path":"scripts/port-check.sh","size":4603,"sha256":"11776264fa3c14b800b490528225205d4462ad54216d07a04ad0603e28e2a82b","contentType":"application/x-sh; charset=utf-8"},{"id":"a35c6626-2561-560f-9658-6829f43d9d6a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a35c6626-2561-560f-9658-6829f43d9d6a/attachment.sh","path":"scripts/wait-for-it.sh","size":2807,"sha256":"d0253405a162dfe4c9f65c1b45fa8c4e86c549e561d06b9e7221ed7dd07d705d","contentType":"application/x-sh; charset=utf-8"},{"id":"41864a6b-1194-5fde-90fb-97336d94b25c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/41864a6b-1194-5fde-90fb-97336d94b25c/attachment.sh","path":"scripts/wp-setup.sh","size":5695,"sha256":"3528941519c8191c5003918d4267fcddb37596e5ff7aa319596fb8156e5ffb44","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"f665dfa13f3a929e9099df2cc9694bb5f745cb0fc88c9dcbbe947c003b3c9438","attachment_count":47,"text_attachments":37,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":10,"excluded_attachments":[]},"cluster_size":2,"skill_md_path":"skills/docker-local-dev/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":1},"context":"fork","version":"v1","category":"testing-qa","import_tag":"clean-skills-v1","description":"Generate Docker Compose and Dockerfile configurations for local development through interactive Q&A. Supports PHP/Laravel, WordPress, Drupal, Joomla, Node.js, and Python stacks with Nginx, Supervisor/PM2, databases, Redis, and email testing. Always asks clarifying questions before generating configurations."}},"renderedAt":1782979362708}

Docker Local Development Environment Generator Overview This skill helps you create optimized Docker development environments for your projects. It generates , , and related configurations through an interactive, question-driven workflow. When to use this skill: - Setting up a new Docker development environment - Dockerizing an existing project for local development - Adding services (database, Redis, email testing) to your Docker setup - Updating or merging with existing Docker configurations Key Principle: This skill ALWAYS asks questions before making decisions. You will be notified about…