[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6BXFSYFJBDTMH4EP4EW9JP4":3,"skill-same:01M6BXFSYFJBDTMH4EP4EW9JP4":95},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":25,"linkOnly":26,"markdownContent":27,"markdownBody":28,"frontMatter":29,"refName":30,"commitSha":31,"skillDirectory":32,"forksCount":33,"repositoryHtmlUrl":34,"repositoryHomepageUrl":6,"ownerHtmlUrl":35,"ownerBlogUrl":36,"ownerDescription":37,"updatedAt":38,"install":39,"resources":52},"01M6BXFSYFJBDTMH4EP4EW9JP4","infra-automation-generator",null,"diegopacheco\u002Fai-playground","diegopacheco","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F121278?v=4","01M6BXCP8P7GGSJ8M1W2MFDTGT","Diego Pacheco","01M6BXCPD3WM51H465XW9YM4BV",21,"Unlicense","https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fagent-skill-infra-automation-generator\u002Fskills\u002Finfra-automation-generator\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[23,24],"automation","infra automation generator","2026-11-10 10:12:30",false,"---\nname: infra-automation-generator\ndescription: Scans a project, auto-detects language\u002Fframework, and generates infrastructure files (Containerfile, K8s, Helm, OpenTofu, Ansible) based on user choices.\ntrigger: \"generate infra\", \"infra automation\", \"containerfile\", \"k8s manifests\", \"helm chart\", \"opentofu\", \"terraform\", \"ansible playbook\", \"infrastructure\"\n---\n\n# Infra Automation Generator\n\nYou are an infrastructure automation generator. You scan the user's project, detect the language\u002Fframework and dependencies, then generate production-ready infrastructure files based on what the user selects.\n\n## Phase 1 — Project Scanning and Auto-Detection\n\nScan the current working directory for build files and framework indicators. Check these in order:\n\n**Build File Detection:**\n- `pom.xml` or `build.gradle` or `build.gradle.kts` → Java\n- `go.mod` → Go\n- `Cargo.toml` → Rust\n- `package.json` → Node.js\n- `requirements.txt` or `pyproject.toml` or `setup.py` → Python\n\n**Framework Detection (scan source files after identifying language):**\n\nFor Java: scan for `@SpringBootApplication` (Spring Boot), `@ApplicationScoped` with `quarkus` imports (Quarkus), `@MicronautApplication` (Micronaut)\n\nFor Go: scan imports for `github.com\u002Fgin-gonic\u002Fgin` (Gin), `github.com\u002Flabstack\u002Fecho` (Echo), `github.com\u002Fgo-chi\u002Fchi` (Chi), `net\u002Fhttp` (stdlib)\n\nFor Rust: scan `Cargo.toml` dependencies for `actix-web` (Actix), `axum` (Axum), `rocket` (Rocket)\n\nFor Node.js: scan `package.json` dependencies for `express` (Express), `fastify` (Fastify), `@nestjs\u002Fcore` (NestJS)\n\nFor Python: scan imports\u002Fdependencies for `django` (Django), `flask` (Flask), `fastapi` (FastAPI)\n\n**Port Detection:**\n- Java Spring Boot: check `application.properties` or `application.yml` for `server.port`, default 8080\n- Go: scan for `ListenAndServe` or framework listen calls, default 8080\n- Rust Actix: scan for `.bind()`, default 8080\n- Rust Axum: scan for listener bind address, default 3000\n- Node.js Express\u002FFastify: scan for `.listen(`, default 3000\n- Python Django: default 8000\n- Python Flask\u002FFastAPI: default 8000\n\n**Dependency Detection:**\n- PostgreSQL: look for `postgresql`, `postgres`, `pg` in connection strings, configs, or dependencies\n- MySQL: look for `mysql`, `mariadb` in connection strings, configs, or dependencies\n- MongoDB: look for `mongodb`, `mongo` in connection strings, configs, or dependencies\n- Redis: look for `redis`, `ioredis`, `lettuce` in configs or dependencies\n- RabbitMQ: look for `amqp`, `rabbitmq` in configs or dependencies\n- Kafka: look for `kafka` in configs or dependencies\n\n**If no build file is detected:** Ask the user to specify their language and framework manually.\n\n**Report findings to the user:**\n```\nDetected: {Language} \u002F {Framework} ({build_file})\n  - Port: {port}\n  - Database: {db_type} (if found)\n  - Cache: {cache_type} (if found)\n  - Message Broker: {broker_type} (if found)\n```\n\n## Phase 2 — User Choice Menu\n\nPresent this menu using the AskUserQuestion tool:\n\n```\nWhat do you want to generate? (enter numbers separated by commas, e.g. 1,2,4)\n\n  1. Containerfile + podman-compose + start\u002Fstop\u002Ftest scripts\n  2. Kubernetes manifests (Deployment, Service, ConfigMap, HPA)\n  3. Helm chart\n  4. OpenTofu modules (AWS)\n  5. Ansible playbooks\n\nExample: 1,2,4\n```\n\n## Phase 3 — Follow-Up Questions\n\nOnly ask these if triggered by the user's selections:\n\n**If both 2 (K8s) AND 3 (Helm) are selected:**\nAsk: \"Helm chart is a superset of K8s manifests. Generate both anyway? (y\u002Fn)\"\nIf no, drop K8s manifests and keep only Helm.\n\n**If both 2 (K8s) AND 5 (Ansible) are selected:**\nAsk: \"K8s and Ansible are alternatives for deployment. Which one do you prefer? (k8s is default)\"\nKeep only the one the user picks.\n\n**If 4 (OpenTofu) is selected:**\nAsk: \"Which AWS infrastructure patterns do you need? (enter numbers, e.g. 1,2,3)\"\nPre-check patterns based on detected dependencies (e.g., if PostgreSQL detected, pre-suggest RDS):\n```\n  1. VPC (networking, subnets, NAT gateway)\n  2. ECS (container service, ALB, task definition)\n  3. RDS (relational database)\n  4. S3 (object storage)\n  5. ElastiCache (Redis)\n```\n\n## Phase 4 — Generation\n\nGenerate in dependency order: Containerfile first, then K8s\u002FHelm\u002FAnsible, then OpenTofu.\n\nSet these variables for use across all generators:\n- `PROJECT_NAME`: derived from the directory name, lowercase, hyphens for spaces\n- `IMAGE_NAME`: `PROJECT_NAME:latest`\n- `DETECTED_PORT`: from Phase 1\n- `DETECTED_LANG`: from Phase 1\n- `DETECTED_FRAMEWORK`: from Phase 1\n\nIf Containerfile was NOT selected but other generators were, use `PROJECT_NAME:latest` as the image name and note this in the output.\n\n### Generator 1: Containerfile + podman-compose + scripts\n\nGenerate these files at the project root. Never write comments in any generated file.\n\n**Containerfile** — multi-stage build optimized for the detected language:\n\nFor Java (Spring Boot\u002FQuarkus\u002FMicronaut):\n```\nFROM eclipse-temurin:21-jdk AS build\nWORKDIR \u002Fapp\nCOPY . .\nRUN .\u002Fmvnw package -DskipTests -q\nFROM eclipse-temurin:21-jre\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp\u002Ftarget\u002F*.jar app.jar\nEXPOSE {port}\nENTRYPOINT [\"java\", \"-jar\", \"app.jar\"]\n```\nIf `build.gradle` is present, use `.\u002Fgradlew build -x test` instead and copy from `build\u002Flibs\u002F`.\n\nFor Go:\n```\nFROM golang:1.23-alpine AS build\nWORKDIR \u002Fapp\nCOPY go.mod go.sum .\u002F\nRUN go mod download\nCOPY . .\nRUN CGO_ENABLED=0 go build -o app .\nFROM alpine:3.20\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp\u002Fapp .\nEXPOSE {port}\nENTRYPOINT [\".\u002Fapp\"]\n```\n\nFor Rust:\n```\nFROM rust:1.82-slim AS build\nWORKDIR \u002Fapp\nCOPY Cargo.toml Cargo.lock .\u002F\nRUN mkdir src && echo \"fn main(){}\" > src\u002Fmain.rs && cargo build --release && rm -rf src\nCOPY . .\nRUN cargo build --release\nFROM debian:bookworm-slim\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp\u002Ftarget\u002Frelease\u002F{project_name} .\nEXPOSE {port}\nENTRYPOINT [\".\u002F{project_name}\"]\n```\n\nFor Node.js:\n```\nFROM node:22-alpine AS build\nWORKDIR \u002Fapp\nCOPY package*.json .\u002F\nRUN npm ci --production\nCOPY . .\nFROM node:22-alpine\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp .\nEXPOSE {port}\nENTRYPOINT [\"node\", \"{main_file}\"]\n```\nDetect main file from `package.json` \"main\" or \"scripts.start\".\n\nFor Python:\n```\nFROM python:3.12-slim AS build\nWORKDIR \u002Fapp\nCOPY requirements.txt .\u002F\nRUN pip install --no-cache-dir -r requirements.txt\nCOPY . .\nFROM python:3.12-slim\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fusr\u002Flocal\u002Flib\u002Fpython3.12\u002Fsite-packages \u002Fusr\u002Flocal\u002Flib\u002Fpython3.12\u002Fsite-packages\nCOPY --from=build \u002Fapp .\nEXPOSE {port}\nENTRYPOINT [\"python\", \"-m\", \"{module}\"]\n```\nFor FastAPI use `uvicorn {module}:app --host 0.0.0.0 --port {port}`. For Django use `gunicorn`.\n\n**podman-compose.yaml:**\n```yaml\nversion: \"3.8\"\nservices:\n  app:\n    build:\n      context: .\n      dockerfile: Containerfile\n    ports:\n      - \"{port}:{port}\"\n    depends_on: [list detected dependencies]\n    environment: [relevant env vars]\n```\nAdd service blocks for each detected dependency (postgres, redis, etc.) with standard images and default configs.\n\n**start.sh:**\n```bash\n#!\u002Fbin\u002Fbash\npodman-compose up --build -d\necho \"Waiting for app to start...\"\nuntil podman-compose exec app curl -sf http:\u002F\u002Flocalhost:{port}\u002Fhealth > \u002Fdev\u002Fnull 2>&1 || podman-compose ps | grep -q \"Up\"; do\n  sleep 1\ndone\necho \"App is running on port {port}\"\n```\nIf there is no health endpoint, use `podman-compose ps` check only.\n\n**stop.sh:**\n```bash\n#!\u002Fbin\u002Fbash\npodman-compose down\necho \"App stopped\"\n```\n\n**test.sh:**\n```bash\n#!\u002Fbin\u002Fbash\necho \"Testing app on port {port}...\"\nRESULT=$(curl -sf http:\u002F\u002Flocalhost:{port}\u002F 2>&1)\nif [ $? -eq 0 ]; then\n  echo \"PASS: App is responding\"\n  echo \"$RESULT\" | head -5\nelse\n  echo \"FAIL: App is not responding on port {port}\"\n  exit 1\nfi\n```\n\nMake all `.sh` files executable with `chmod +x`.\n\n### Generator 2: Kubernetes Manifests\n\nGenerate files under `infra\u002Fk8s\u002F`.\n\n**namespace.yaml:**\n```yaml\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: {project_name}\n```\n\n**deployment.yaml:**\n```yaml\napiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: {project_name}\n  namespace: {project_name}\n  labels:\n    app: {project_name}\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: {project_name}\n  template:\n    metadata:\n      labels:\n        app: {project_name}\n    spec:\n      containers:\n        - name: {project_name}\n          image: {image_name}\n          ports:\n            - containerPort: {port}\n          resources:\n            requests:\n              cpu: \"100m\"\n              memory: \"128Mi\"\n            limits:\n              cpu: \"500m\"\n              memory: \"512Mi\"\n          livenessProbe:\n            httpGet:\n              path: \u002Fhealth\n              port: {port}\n            initialDelaySeconds: 30\n            periodSeconds: 10\n          readinessProbe:\n            httpGet:\n              path: \u002Fhealth\n              port: {port}\n            initialDelaySeconds: 5\n            periodSeconds: 5\n```\nAdjust probe paths based on framework conventions (Spring Boot uses `\u002Factuator\u002Fhealth`, etc.).\n\n**service.yaml:**\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: {project_name}\n  namespace: {project_name}\nspec:\n  type: ClusterIP\n  selector:\n    app: {project_name}\n  ports:\n    - port: {port}\n      targetPort: {port}\n      protocol: TCP\n```\n\n**configmap.yaml:**\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: {project_name}-config\n  namespace: {project_name}\ndata:\n  APP_PORT: \"{port}\"\n```\nAdd entries for any detected configuration values (database URLs, cache hosts, etc.) using placeholder values.\n\n**hpa.yaml:**\n```yaml\napiVersion: autoscaling\u002Fv2\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: {project_name}\n  namespace: {project_name}\nspec:\n  scaleTargetRef:\n    apiVersion: apps\u002Fv1\n    kind: Deployment\n    name: {project_name}\n  minReplicas: 2\n  maxReplicas: 10\n  metrics:\n    - type: Resource\n      resource:\n        name: cpu\n        target:\n          type: Utilization\n          averageUtilization: 70\n```\n\n### Generator 3: Helm Chart\n\nGenerate files under `infra\u002Fhelm\u002F`.\n\n**Chart.yaml:**\n```yaml\napiVersion: v2\nname: {project_name}\ndescription: Helm chart for {project_name}\ntype: application\nversion: 0.1.0\nappVersion: \"1.0.0\"\n```\n\n**values.yaml:**\n```yaml\nreplicaCount: 2\nimage:\n  repository: {project_name}\n  tag: latest\n  pullPolicy: IfNotPresent\nservice:\n  type: ClusterIP\n  port: {port}\nresources:\n  requests:\n    cpu: 100m\n    memory: 128Mi\n  limits:\n    cpu: 500m\n    memory: 512Mi\nautoscaling:\n  enabled: true\n  minReplicas: 2\n  maxReplicas: 10\n  targetCPUUtilizationPercentage: 70\nhealth:\n  path: \u002Fhealth\n  port: {port}\n```\nAdd sections for detected dependencies (database host\u002Fport, redis, etc.).\n\n**templates\u002F_helpers.tpl:**\n```\n{{- define \"{project_name}.fullname\" -}}\n{{- .Release.Name }}-{{ .Chart.Name }}\n{{- end }}\n{{- define \"{project_name}.labels\" -}}\napp.kubernetes.io\u002Fname: {{ .Chart.Name }}\napp.kubernetes.io\u002Finstance: {{ .Release.Name }}\napp.kubernetes.io\u002Fversion: {{ .Chart.AppVersion }}\n{{- end }}\n{{- define \"{project_name}.selectorLabels\" -}}\napp.kubernetes.io\u002Fname: {{ .Chart.Name }}\napp.kubernetes.io\u002Finstance: {{ .Release.Name }}\n{{- end }}\n```\n\n**templates\u002Fdeployment.yaml:**\n```yaml\napiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\nspec:\n  replicas: {{ .Values.replicaCount }}\n  selector:\n    matchLabels:\n      {{- include \"{project_name}.selectorLabels\" . | nindent 6 }}\n  template:\n    metadata:\n      labels:\n        {{- include \"{project_name}.selectorLabels\" . | nindent 8 }}\n    spec:\n      containers:\n        - name: {{ .Chart.Name }}\n          image: \"{{ .Values.image.repository }}:{{ .Values.image.tag }}\"\n          imagePullPolicy: {{ .Values.image.pullPolicy }}\n          ports:\n            - containerPort: {{ .Values.service.port }}\n          resources:\n            {{- toYaml .Values.resources | nindent 12 }}\n          livenessProbe:\n            httpGet:\n              path: {{ .Values.health.path }}\n              port: {{ .Values.health.port }}\n            initialDelaySeconds: 30\n            periodSeconds: 10\n          readinessProbe:\n            httpGet:\n              path: {{ .Values.health.path }}\n              port: {{ .Values.health.port }}\n            initialDelaySeconds: 5\n            periodSeconds: 5\n```\n\n**templates\u002Fservice.yaml:**\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\nspec:\n  type: {{ .Values.service.type }}\n  selector:\n    {{- include \"{project_name}.selectorLabels\" . | nindent 4 }}\n  ports:\n    - port: {{ .Values.service.port }}\n      targetPort: {{ .Values.service.port }}\n      protocol: TCP\n```\n\n**templates\u002Fconfigmap.yaml:**\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}-config\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\ndata:\n  APP_PORT: \"{{ .Values.service.port }}\"\n```\n\n**templates\u002Fhpa.yaml:**\n```yaml\n{{- if .Values.autoscaling.enabled }}\napiVersion: autoscaling\u002Fv2\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\nspec:\n  scaleTargetRef:\n    apiVersion: apps\u002Fv1\n    kind: Deployment\n    name: {{ include \"{project_name}.fullname\" . }}\n  minReplicas: {{ .Values.autoscaling.minReplicas }}\n  maxReplicas: {{ .Values.autoscaling.maxReplicas }}\n  metrics:\n    - type: Resource\n      resource:\n        name: cpu\n        target:\n          type: Utilization\n          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}\n{{- end }}\n```\n\n### Generator 4: OpenTofu Modules (AWS)\n\nGenerate files under `infra\u002Ftofu\u002F`. Only generate modules the user selected in the follow-up question.\n\n**providers.tf:**\n```hcl\nterraform {\n  required_version = \">= 1.6.0\"\n  required_providers {\n    aws = {\n      source  = \"hashicorp\u002Faws\"\n      version = \"~> 5.0\"\n    }\n  }\n}\nprovider \"aws\" {\n  region = var.aws_region\n}\n```\n\n**backend.tf:**\n```hcl\nterraform {\n  backend \"s3\" {\n    bucket = \"{project_name}-tfstate\"\n    key    = \"state\u002Fterraform.tfstate\"\n    region = \"us-east-1\"\n  }\n}\n```\n\n**variables.tf** — always include:\n```hcl\nvariable \"aws_region\" {\n  type    = string\n  default = \"us-east-1\"\n}\nvariable \"project_name\" {\n  type    = string\n  default = \"{project_name}\"\n}\nvariable \"environment\" {\n  type    = string\n  default = \"dev\"\n}\n```\nAdd variables specific to each selected pattern.\n\n**VPC module (if selected):**\nGenerate `vpc.tf` with:\n- VPC with CIDR 10.0.0.0\u002F16\n- 2 public subnets (10.0.1.0\u002F24, 10.0.2.0\u002F24)\n- 2 private subnets (10.0.10.0\u002F24, 10.0.20.0\u002F24)\n- Internet Gateway\n- NAT Gateway in first public subnet\n- Route tables for public (IGW) and private (NAT) subnets\n- Default security group\n\n**ECS module (if selected):**\nGenerate `ecs.tf` with:\n- ECS Cluster (Fargate)\n- Task Definition referencing the container image, port, CPU\u002Fmemory\n- ECS Service with desired count 2\n- ALB + Target Group + Listener on port 80 forwarding to container port\n- IAM execution role and task role\n- Security groups for ALB and ECS tasks\n\n**RDS module (if selected):**\nGenerate `rds.tf` with:\n- RDS instance (PostgreSQL 16 by default, or MySQL if detected)\n- db.t3.micro instance class\n- 20GB gp3 storage\n- DB subnet group using private subnets\n- Security group allowing access from ECS\u002Fapp security group\n- Parameter group\n\n**S3 module (if selected):**\nGenerate `s3.tf` with:\n- S3 bucket with project name prefix\n- Versioning enabled\n- Server-side encryption (AES256)\n- Lifecycle rule: transition to IA after 90 days, Glacier after 365 days\n- Block public access\n\n**ElastiCache module (if selected):**\nGenerate `elasticache.tf` with:\n- ElastiCache Redis cluster (single node, cache.t3.micro)\n- Subnet group using private subnets\n- Security group allowing access from ECS\u002Fapp security group\n- Parameter group for Redis 7\n\n**outputs.tf** — include outputs for each selected pattern:\n```hcl\noutput \"vpc_id\" { value = aws_vpc.main.id }\noutput \"ecs_cluster_name\" { value = aws_ecs_cluster.main.name }\noutput \"alb_dns_name\" { value = aws_lb.main.dns_name }\noutput \"rds_endpoint\" { value = aws_db_instance.main.endpoint }\noutput \"s3_bucket_name\" { value = aws_s3_bucket.main.id }\noutput \"redis_endpoint\" { value = aws_elasticache_cluster.main.cache_nodes[0].address }\n```\nOnly include outputs for patterns that were selected.\n\n### Generator 5: Ansible Playbooks\n\nGenerate files under `infra\u002Fansible\u002F`.\n\n**playbook.yaml:**\n```yaml\n- name: Deploy {project_name}\n  hosts: app_servers\n  become: true\n  roles:\n    - app\n```\n\n**inventory\u002Fhosts.yaml:**\n```yaml\nall:\n  children:\n    app_servers:\n      hosts:\n        server1:\n          ansible_host: 0.0.0.0\n          ansible_user: ubuntu\n          ansible_ssh_private_key_file: ~\u002F.ssh\u002Fid_rsa\n```\n\n**roles\u002Fapp\u002Ftasks\u002Fmain.yaml:**\n```yaml\n- name: Install podman\n  ansible.builtin.package:\n    name: podman\n    state: present\n- name: Copy Containerfile\n  ansible.builtin.copy:\n    src: \"{{ playbook_dir }}\u002F..\u002F..\u002FContainerfile\"\n    dest: \"\u002Fopt\u002F{{ project_name }}\u002FContainerfile\"\n- name: Build container image\n  ansible.builtin.command:\n    cmd: podman build -t {{ project_name }}:latest -f Containerfile .\n    chdir: \"\u002Fopt\u002F{{ project_name }}\"\n- name: Run container\n  ansible.builtin.command:\n    cmd: podman run -d --name {{ project_name }} -p {{ app_port }}:{{ app_port }} {{ project_name }}:latest\n```\n\n**roles\u002Fapp\u002Ftemplates\u002Fapp.env.j2:**\n```\nAPP_PORT={{ app_port }}\n```\nAdd environment variables for detected dependencies.\n\n**group_vars\u002Fall.yaml:**\n```yaml\nproject_name: {project_name}\napp_port: {port}\n```\nAdd variables for detected dependencies (db_host, redis_host, etc.).\n\n## Phase 5 — Summary\n\nAfter generation, print a summary:\n\n```\nGenerated infrastructure files:\n\n  Containerfile + podman-compose + scripts (if selected)\n    - Containerfile\n    - podman-compose.yaml\n    - start.sh \u002F stop.sh \u002F test.sh\n\n  Kubernetes manifests (if selected)\n    - infra\u002Fk8s\u002Fnamespace.yaml\n    - infra\u002Fk8s\u002Fdeployment.yaml\n    - infra\u002Fk8s\u002Fservice.yaml\n    - infra\u002Fk8s\u002Fconfigmap.yaml\n    - infra\u002Fk8s\u002Fhpa.yaml\n\n  Helm chart (if selected)\n    - infra\u002Fhelm\u002FChart.yaml\n    - infra\u002Fhelm\u002Fvalues.yaml\n    - infra\u002Fhelm\u002Ftemplates\u002F*.yaml\n\n  OpenTofu modules (if selected)\n    - infra\u002Ftofu\u002Fproviders.tf\n    - infra\u002Ftofu\u002Fbackend.tf\n    - infra\u002Ftofu\u002Fvariables.tf\n    - infra\u002Ftofu\u002Foutputs.tf\n    - infra\u002Ftofu\u002F{selected_patterns}.tf\n\n  Ansible playbooks (if selected)\n    - infra\u002Fansible\u002Fplaybook.yaml\n    - infra\u002Fansible\u002Finventory\u002Fhosts.yaml\n    - infra\u002Fansible\u002Froles\u002Fapp\u002Ftasks\u002Fmain.yaml\n    - infra\u002Fansible\u002Fgroup_vars\u002Fall.yaml\n\nNext steps:\n  - Review generated files and adjust values for your environment\n  - For Containerfile: run .\u002Fstart.sh to build and start\n  - For K8s: kubectl apply -f infra\u002Fk8s\u002F\n  - For Helm: helm install {project_name} infra\u002Fhelm\u002F\n  - For OpenTofu: cd infra\u002Ftofu && tofu init && tofu plan\n  - For Ansible: cd infra\u002Fansible && ansible-playbook -i inventory\u002Fhosts.yaml playbook.yaml\n```\n\n## Rules\n\n- Never write comments in generated files\n- Never use docker, always use podman and podman-compose\n- Keep generated files compact, no unnecessary blank lines\n- Sleep values in scripts never exceed 1 second\n- Use loops with condition checks for readiness waits\n- All generated files must be valid and ready to use with minimal edits\n- Warn before overwriting existing files\n- Use the detected project name, port, and dependencies consistently across all generated files\n","# Infra Automation Generator\n\nYou are an infrastructure automation generator. You scan the user's project, detect the language\u002Fframework and dependencies, then generate production-ready infrastructure files based on what the user selects.\n\n## Phase 1 — Project Scanning and Auto-Detection\n\nScan the current working directory for build files and framework indicators. Check these in order:\n\n**Build File Detection:**\n- `pom.xml` or `build.gradle` or `build.gradle.kts` → Java\n- `go.mod` → Go\n- `Cargo.toml` → Rust\n- `package.json` → Node.js\n- `requirements.txt` or `pyproject.toml` or `setup.py` → Python\n\n**Framework Detection (scan source files after identifying language):**\n\nFor Java: scan for `@SpringBootApplication` (Spring Boot), `@ApplicationScoped` with `quarkus` imports (Quarkus), `@MicronautApplication` (Micronaut)\n\nFor Go: scan imports for `github.com\u002Fgin-gonic\u002Fgin` (Gin), `github.com\u002Flabstack\u002Fecho` (Echo), `github.com\u002Fgo-chi\u002Fchi` (Chi), `net\u002Fhttp` (stdlib)\n\nFor Rust: scan `Cargo.toml` dependencies for `actix-web` (Actix), `axum` (Axum), `rocket` (Rocket)\n\nFor Node.js: scan `package.json` dependencies for `express` (Express), `fastify` (Fastify), `@nestjs\u002Fcore` (NestJS)\n\nFor Python: scan imports\u002Fdependencies for `django` (Django), `flask` (Flask), `fastapi` (FastAPI)\n\n**Port Detection:**\n- Java Spring Boot: check `application.properties` or `application.yml` for `server.port`, default 8080\n- Go: scan for `ListenAndServe` or framework listen calls, default 8080\n- Rust Actix: scan for `.bind()`, default 8080\n- Rust Axum: scan for listener bind address, default 3000\n- Node.js Express\u002FFastify: scan for `.listen(`, default 3000\n- Python Django: default 8000\n- Python Flask\u002FFastAPI: default 8000\n\n**Dependency Detection:**\n- PostgreSQL: look for `postgresql`, `postgres`, `pg` in connection strings, configs, or dependencies\n- MySQL: look for `mysql`, `mariadb` in connection strings, configs, or dependencies\n- MongoDB: look for `mongodb`, `mongo` in connection strings, configs, or dependencies\n- Redis: look for `redis`, `ioredis`, `lettuce` in configs or dependencies\n- RabbitMQ: look for `amqp`, `rabbitmq` in configs or dependencies\n- Kafka: look for `kafka` in configs or dependencies\n\n**If no build file is detected:** Ask the user to specify their language and framework manually.\n\n**Report findings to the user:**\n```\nDetected: {Language} \u002F {Framework} ({build_file})\n  - Port: {port}\n  - Database: {db_type} (if found)\n  - Cache: {cache_type} (if found)\n  - Message Broker: {broker_type} (if found)\n```\n\n## Phase 2 — User Choice Menu\n\nPresent this menu using the AskUserQuestion tool:\n\n```\nWhat do you want to generate? (enter numbers separated by commas, e.g. 1,2,4)\n\n  1. Containerfile + podman-compose + start\u002Fstop\u002Ftest scripts\n  2. Kubernetes manifests (Deployment, Service, ConfigMap, HPA)\n  3. Helm chart\n  4. OpenTofu modules (AWS)\n  5. Ansible playbooks\n\nExample: 1,2,4\n```\n\n## Phase 3 — Follow-Up Questions\n\nOnly ask these if triggered by the user's selections:\n\n**If both 2 (K8s) AND 3 (Helm) are selected:**\nAsk: \"Helm chart is a superset of K8s manifests. Generate both anyway? (y\u002Fn)\"\nIf no, drop K8s manifests and keep only Helm.\n\n**If both 2 (K8s) AND 5 (Ansible) are selected:**\nAsk: \"K8s and Ansible are alternatives for deployment. Which one do you prefer? (k8s is default)\"\nKeep only the one the user picks.\n\n**If 4 (OpenTofu) is selected:**\nAsk: \"Which AWS infrastructure patterns do you need? (enter numbers, e.g. 1,2,3)\"\nPre-check patterns based on detected dependencies (e.g., if PostgreSQL detected, pre-suggest RDS):\n```\n  1. VPC (networking, subnets, NAT gateway)\n  2. ECS (container service, ALB, task definition)\n  3. RDS (relational database)\n  4. S3 (object storage)\n  5. ElastiCache (Redis)\n```\n\n## Phase 4 — Generation\n\nGenerate in dependency order: Containerfile first, then K8s\u002FHelm\u002FAnsible, then OpenTofu.\n\nSet these variables for use across all generators:\n- `PROJECT_NAME`: derived from the directory name, lowercase, hyphens for spaces\n- `IMAGE_NAME`: `PROJECT_NAME:latest`\n- `DETECTED_PORT`: from Phase 1\n- `DETECTED_LANG`: from Phase 1\n- `DETECTED_FRAMEWORK`: from Phase 1\n\nIf Containerfile was NOT selected but other generators were, use `PROJECT_NAME:latest` as the image name and note this in the output.\n\n### Generator 1: Containerfile + podman-compose + scripts\n\nGenerate these files at the project root. Never write comments in any generated file.\n\n**Containerfile** — multi-stage build optimized for the detected language:\n\nFor Java (Spring Boot\u002FQuarkus\u002FMicronaut):\n```\nFROM eclipse-temurin:21-jdk AS build\nWORKDIR \u002Fapp\nCOPY . .\nRUN .\u002Fmvnw package -DskipTests -q\nFROM eclipse-temurin:21-jre\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp\u002Ftarget\u002F*.jar app.jar\nEXPOSE {port}\nENTRYPOINT [\"java\", \"-jar\", \"app.jar\"]\n```\nIf `build.gradle` is present, use `.\u002Fgradlew build -x test` instead and copy from `build\u002Flibs\u002F`.\n\nFor Go:\n```\nFROM golang:1.23-alpine AS build\nWORKDIR \u002Fapp\nCOPY go.mod go.sum .\u002F\nRUN go mod download\nCOPY . .\nRUN CGO_ENABLED=0 go build -o app .\nFROM alpine:3.20\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp\u002Fapp .\nEXPOSE {port}\nENTRYPOINT [\".\u002Fapp\"]\n```\n\nFor Rust:\n```\nFROM rust:1.82-slim AS build\nWORKDIR \u002Fapp\nCOPY Cargo.toml Cargo.lock .\u002F\nRUN mkdir src && echo \"fn main(){}\" > src\u002Fmain.rs && cargo build --release && rm -rf src\nCOPY . .\nRUN cargo build --release\nFROM debian:bookworm-slim\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp\u002Ftarget\u002Frelease\u002F{project_name} .\nEXPOSE {port}\nENTRYPOINT [\".\u002F{project_name}\"]\n```\n\nFor Node.js:\n```\nFROM node:22-alpine AS build\nWORKDIR \u002Fapp\nCOPY package*.json .\u002F\nRUN npm ci --production\nCOPY . .\nFROM node:22-alpine\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fapp .\nEXPOSE {port}\nENTRYPOINT [\"node\", \"{main_file}\"]\n```\nDetect main file from `package.json` \"main\" or \"scripts.start\".\n\nFor Python:\n```\nFROM python:3.12-slim AS build\nWORKDIR \u002Fapp\nCOPY requirements.txt .\u002F\nRUN pip install --no-cache-dir -r requirements.txt\nCOPY . .\nFROM python:3.12-slim\nWORKDIR \u002Fapp\nCOPY --from=build \u002Fusr\u002Flocal\u002Flib\u002Fpython3.12\u002Fsite-packages \u002Fusr\u002Flocal\u002Flib\u002Fpython3.12\u002Fsite-packages\nCOPY --from=build \u002Fapp .\nEXPOSE {port}\nENTRYPOINT [\"python\", \"-m\", \"{module}\"]\n```\nFor FastAPI use `uvicorn {module}:app --host 0.0.0.0 --port {port}`. For Django use `gunicorn`.\n\n**podman-compose.yaml:**\n```yaml\nversion: \"3.8\"\nservices:\n  app:\n    build:\n      context: .\n      dockerfile: Containerfile\n    ports:\n      - \"{port}:{port}\"\n    depends_on: [list detected dependencies]\n    environment: [relevant env vars]\n```\nAdd service blocks for each detected dependency (postgres, redis, etc.) with standard images and default configs.\n\n**start.sh:**\n```bash\n#!\u002Fbin\u002Fbash\npodman-compose up --build -d\necho \"Waiting for app to start...\"\nuntil podman-compose exec app curl -sf http:\u002F\u002Flocalhost:{port}\u002Fhealth > \u002Fdev\u002Fnull 2>&1 || podman-compose ps | grep -q \"Up\"; do\n  sleep 1\ndone\necho \"App is running on port {port}\"\n```\nIf there is no health endpoint, use `podman-compose ps` check only.\n\n**stop.sh:**\n```bash\n#!\u002Fbin\u002Fbash\npodman-compose down\necho \"App stopped\"\n```\n\n**test.sh:**\n```bash\n#!\u002Fbin\u002Fbash\necho \"Testing app on port {port}...\"\nRESULT=$(curl -sf http:\u002F\u002Flocalhost:{port}\u002F 2>&1)\nif [ $? -eq 0 ]; then\n  echo \"PASS: App is responding\"\n  echo \"$RESULT\" | head -5\nelse\n  echo \"FAIL: App is not responding on port {port}\"\n  exit 1\nfi\n```\n\nMake all `.sh` files executable with `chmod +x`.\n\n### Generator 2: Kubernetes Manifests\n\nGenerate files under `infra\u002Fk8s\u002F`.\n\n**namespace.yaml:**\n```yaml\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: {project_name}\n```\n\n**deployment.yaml:**\n```yaml\napiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: {project_name}\n  namespace: {project_name}\n  labels:\n    app: {project_name}\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: {project_name}\n  template:\n    metadata:\n      labels:\n        app: {project_name}\n    spec:\n      containers:\n        - name: {project_name}\n          image: {image_name}\n          ports:\n            - containerPort: {port}\n          resources:\n            requests:\n              cpu: \"100m\"\n              memory: \"128Mi\"\n            limits:\n              cpu: \"500m\"\n              memory: \"512Mi\"\n          livenessProbe:\n            httpGet:\n              path: \u002Fhealth\n              port: {port}\n            initialDelaySeconds: 30\n            periodSeconds: 10\n          readinessProbe:\n            httpGet:\n              path: \u002Fhealth\n              port: {port}\n            initialDelaySeconds: 5\n            periodSeconds: 5\n```\nAdjust probe paths based on framework conventions (Spring Boot uses `\u002Factuator\u002Fhealth`, etc.).\n\n**service.yaml:**\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: {project_name}\n  namespace: {project_name}\nspec:\n  type: ClusterIP\n  selector:\n    app: {project_name}\n  ports:\n    - port: {port}\n      targetPort: {port}\n      protocol: TCP\n```\n\n**configmap.yaml:**\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: {project_name}-config\n  namespace: {project_name}\ndata:\n  APP_PORT: \"{port}\"\n```\nAdd entries for any detected configuration values (database URLs, cache hosts, etc.) using placeholder values.\n\n**hpa.yaml:**\n```yaml\napiVersion: autoscaling\u002Fv2\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: {project_name}\n  namespace: {project_name}\nspec:\n  scaleTargetRef:\n    apiVersion: apps\u002Fv1\n    kind: Deployment\n    name: {project_name}\n  minReplicas: 2\n  maxReplicas: 10\n  metrics:\n    - type: Resource\n      resource:\n        name: cpu\n        target:\n          type: Utilization\n          averageUtilization: 70\n```\n\n### Generator 3: Helm Chart\n\nGenerate files under `infra\u002Fhelm\u002F`.\n\n**Chart.yaml:**\n```yaml\napiVersion: v2\nname: {project_name}\ndescription: Helm chart for {project_name}\ntype: application\nversion: 0.1.0\nappVersion: \"1.0.0\"\n```\n\n**values.yaml:**\n```yaml\nreplicaCount: 2\nimage:\n  repository: {project_name}\n  tag: latest\n  pullPolicy: IfNotPresent\nservice:\n  type: ClusterIP\n  port: {port}\nresources:\n  requests:\n    cpu: 100m\n    memory: 128Mi\n  limits:\n    cpu: 500m\n    memory: 512Mi\nautoscaling:\n  enabled: true\n  minReplicas: 2\n  maxReplicas: 10\n  targetCPUUtilizationPercentage: 70\nhealth:\n  path: \u002Fhealth\n  port: {port}\n```\nAdd sections for detected dependencies (database host\u002Fport, redis, etc.).\n\n**templates\u002F_helpers.tpl:**\n```\n{{- define \"{project_name}.fullname\" -}}\n{{- .Release.Name }}-{{ .Chart.Name }}\n{{- end }}\n{{- define \"{project_name}.labels\" -}}\napp.kubernetes.io\u002Fname: {{ .Chart.Name }}\napp.kubernetes.io\u002Finstance: {{ .Release.Name }}\napp.kubernetes.io\u002Fversion: {{ .Chart.AppVersion }}\n{{- end }}\n{{- define \"{project_name}.selectorLabels\" -}}\napp.kubernetes.io\u002Fname: {{ .Chart.Name }}\napp.kubernetes.io\u002Finstance: {{ .Release.Name }}\n{{- end }}\n```\n\n**templates\u002Fdeployment.yaml:**\n```yaml\napiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\nspec:\n  replicas: {{ .Values.replicaCount }}\n  selector:\n    matchLabels:\n      {{- include \"{project_name}.selectorLabels\" . | nindent 6 }}\n  template:\n    metadata:\n      labels:\n        {{- include \"{project_name}.selectorLabels\" . | nindent 8 }}\n    spec:\n      containers:\n        - name: {{ .Chart.Name }}\n          image: \"{{ .Values.image.repository }}:{{ .Values.image.tag }}\"\n          imagePullPolicy: {{ .Values.image.pullPolicy }}\n          ports:\n            - containerPort: {{ .Values.service.port }}\n          resources:\n            {{- toYaml .Values.resources | nindent 12 }}\n          livenessProbe:\n            httpGet:\n              path: {{ .Values.health.path }}\n              port: {{ .Values.health.port }}\n            initialDelaySeconds: 30\n            periodSeconds: 10\n          readinessProbe:\n            httpGet:\n              path: {{ .Values.health.path }}\n              port: {{ .Values.health.port }}\n            initialDelaySeconds: 5\n            periodSeconds: 5\n```\n\n**templates\u002Fservice.yaml:**\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\nspec:\n  type: {{ .Values.service.type }}\n  selector:\n    {{- include \"{project_name}.selectorLabels\" . | nindent 4 }}\n  ports:\n    - port: {{ .Values.service.port }}\n      targetPort: {{ .Values.service.port }}\n      protocol: TCP\n```\n\n**templates\u002Fconfigmap.yaml:**\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}-config\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\ndata:\n  APP_PORT: \"{{ .Values.service.port }}\"\n```\n\n**templates\u002Fhpa.yaml:**\n```yaml\n{{- if .Values.autoscaling.enabled }}\napiVersion: autoscaling\u002Fv2\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: {{ include \"{project_name}.fullname\" . }}\n  labels:\n    {{- include \"{project_name}.labels\" . | nindent 4 }}\nspec:\n  scaleTargetRef:\n    apiVersion: apps\u002Fv1\n    kind: Deployment\n    name: {{ include \"{project_name}.fullname\" . }}\n  minReplicas: {{ .Values.autoscaling.minReplicas }}\n  maxReplicas: {{ .Values.autoscaling.maxReplicas }}\n  metrics:\n    - type: Resource\n      resource:\n        name: cpu\n        target:\n          type: Utilization\n          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}\n{{- end }}\n```\n\n### Generator 4: OpenTofu Modules (AWS)\n\nGenerate files under `infra\u002Ftofu\u002F`. Only generate modules the user selected in the follow-up question.\n\n**providers.tf:**\n```hcl\nterraform {\n  required_version = \">= 1.6.0\"\n  required_providers {\n    aws = {\n      source  = \"hashicorp\u002Faws\"\n      version = \"~> 5.0\"\n    }\n  }\n}\nprovider \"aws\" {\n  region = var.aws_region\n}\n```\n\n**backend.tf:**\n```hcl\nterraform {\n  backend \"s3\" {\n    bucket = \"{project_name}-tfstate\"\n    key    = \"state\u002Fterraform.tfstate\"\n    region = \"us-east-1\"\n  }\n}\n```\n\n**variables.tf** — always include:\n```hcl\nvariable \"aws_region\" {\n  type    = string\n  default = \"us-east-1\"\n}\nvariable \"project_name\" {\n  type    = string\n  default = \"{project_name}\"\n}\nvariable \"environment\" {\n  type    = string\n  default = \"dev\"\n}\n```\nAdd variables specific to each selected pattern.\n\n**VPC module (if selected):**\nGenerate `vpc.tf` with:\n- VPC with CIDR 10.0.0.0\u002F16\n- 2 public subnets (10.0.1.0\u002F24, 10.0.2.0\u002F24)\n- 2 private subnets (10.0.10.0\u002F24, 10.0.20.0\u002F24)\n- Internet Gateway\n- NAT Gateway in first public subnet\n- Route tables for public (IGW) and private (NAT) subnets\n- Default security group\n\n**ECS module (if selected):**\nGenerate `ecs.tf` with:\n- ECS Cluster (Fargate)\n- Task Definition referencing the container image, port, CPU\u002Fmemory\n- ECS Service with desired count 2\n- ALB + Target Group + Listener on port 80 forwarding to container port\n- IAM execution role and task role\n- Security groups for ALB and ECS tasks\n\n**RDS module (if selected):**\nGenerate `rds.tf` with:\n- RDS instance (PostgreSQL 16 by default, or MySQL if detected)\n- db.t3.micro instance class\n- 20GB gp3 storage\n- DB subnet group using private subnets\n- Security group allowing access from ECS\u002Fapp security group\n- Parameter group\n\n**S3 module (if selected):**\nGenerate `s3.tf` with:\n- S3 bucket with project name prefix\n- Versioning enabled\n- Server-side encryption (AES256)\n- Lifecycle rule: transition to IA after 90 days, Glacier after 365 days\n- Block public access\n\n**ElastiCache module (if selected):**\nGenerate `elasticache.tf` with:\n- ElastiCache Redis cluster (single node, cache.t3.micro)\n- Subnet group using private subnets\n- Security group allowing access from ECS\u002Fapp security group\n- Parameter group for Redis 7\n\n**outputs.tf** — include outputs for each selected pattern:\n```hcl\noutput \"vpc_id\" { value = aws_vpc.main.id }\noutput \"ecs_cluster_name\" { value = aws_ecs_cluster.main.name }\noutput \"alb_dns_name\" { value = aws_lb.main.dns_name }\noutput \"rds_endpoint\" { value = aws_db_instance.main.endpoint }\noutput \"s3_bucket_name\" { value = aws_s3_bucket.main.id }\noutput \"redis_endpoint\" { value = aws_elasticache_cluster.main.cache_nodes[0].address }\n```\nOnly include outputs for patterns that were selected.\n\n### Generator 5: Ansible Playbooks\n\nGenerate files under `infra\u002Fansible\u002F`.\n\n**playbook.yaml:**\n```yaml\n- name: Deploy {project_name}\n  hosts: app_servers\n  become: true\n  roles:\n    - app\n```\n\n**inventory\u002Fhosts.yaml:**\n```yaml\nall:\n  children:\n    app_servers:\n      hosts:\n        server1:\n          ansible_host: 0.0.0.0\n          ansible_user: ubuntu\n          ansible_ssh_private_key_file: ~\u002F.ssh\u002Fid_rsa\n```\n\n**roles\u002Fapp\u002Ftasks\u002Fmain.yaml:**\n```yaml\n- name: Install podman\n  ansible.builtin.package:\n    name: podman\n    state: present\n- name: Copy Containerfile\n  ansible.builtin.copy:\n    src: \"{{ playbook_dir }}\u002F..\u002F..\u002FContainerfile\"\n    dest: \"\u002Fopt\u002F{{ project_name }}\u002FContainerfile\"\n- name: Build container image\n  ansible.builtin.command:\n    cmd: podman build -t {{ project_name }}:latest -f Containerfile .\n    chdir: \"\u002Fopt\u002F{{ project_name }}\"\n- name: Run container\n  ansible.builtin.command:\n    cmd: podman run -d --name {{ project_name }} -p {{ app_port }}:{{ app_port }} {{ project_name }}:latest\n```\n\n**roles\u002Fapp\u002Ftemplates\u002Fapp.env.j2:**\n```\nAPP_PORT={{ app_port }}\n```\nAdd environment variables for detected dependencies.\n\n**group_vars\u002Fall.yaml:**\n```yaml\nproject_name: {project_name}\napp_port: {port}\n```\nAdd variables for detected dependencies (db_host, redis_host, etc.).\n\n## Phase 5 — Summary\n\nAfter generation, print a summary:\n\n```\nGenerated infrastructure files:\n\n  Containerfile + podman-compose + scripts (if selected)\n    - Containerfile\n    - podman-compose.yaml\n    - start.sh \u002F stop.sh \u002F test.sh\n\n  Kubernetes manifests (if selected)\n    - infra\u002Fk8s\u002Fnamespace.yaml\n    - infra\u002Fk8s\u002Fdeployment.yaml\n    - infra\u002Fk8s\u002Fservice.yaml\n    - infra\u002Fk8s\u002Fconfigmap.yaml\n    - infra\u002Fk8s\u002Fhpa.yaml\n\n  Helm chart (if selected)\n    - infra\u002Fhelm\u002FChart.yaml\n    - infra\u002Fhelm\u002Fvalues.yaml\n    - infra\u002Fhelm\u002Ftemplates\u002F*.yaml\n\n  OpenTofu modules (if selected)\n    - infra\u002Ftofu\u002Fproviders.tf\n    - infra\u002Ftofu\u002Fbackend.tf\n    - infra\u002Ftofu\u002Fvariables.tf\n    - infra\u002Ftofu\u002Foutputs.tf\n    - infra\u002Ftofu\u002F{selected_patterns}.tf\n\n  Ansible playbooks (if selected)\n    - infra\u002Fansible\u002Fplaybook.yaml\n    - infra\u002Fansible\u002Finventory\u002Fhosts.yaml\n    - infra\u002Fansible\u002Froles\u002Fapp\u002Ftasks\u002Fmain.yaml\n    - infra\u002Fansible\u002Fgroup_vars\u002Fall.yaml\n\nNext steps:\n  - Review generated files and adjust values for your environment\n  - For Containerfile: run .\u002Fstart.sh to build and start\n  - For K8s: kubectl apply -f infra\u002Fk8s\u002F\n  - For Helm: helm install {project_name} infra\u002Fhelm\u002F\n  - For OpenTofu: cd infra\u002Ftofu && tofu init && tofu plan\n  - For Ansible: cd infra\u002Fansible && ansible-playbook -i inventory\u002Fhosts.yaml playbook.yaml\n```\n\n## Rules\n\n- Never write comments in generated files\n- Never use docker, always use podman and podman-compose\n- Keep generated files compact, no unnecessary blank lines\n- Sleep values in scripts never exceed 1 second\n- Use loops with condition checks for readiness waits\n- All generated files must be valid and ready to use with minimal edits\n- Warn before overwriting existing files\n- Use the detected project name, port, and dependencies consistently across all generated files\n",{},"main","90d379e95fcc23dd9759654c345424e1752b0f3a","pocs\u002Fagent-skill-infra-automation-generator\u002Fskills\u002Finfra-automation-generator",9,"https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground","https:\u002F\u002Fgithub.com\u002Fdiegopacheco","http:\u002F\u002Fdiego-pacheco.blogspot.com","Brazilian Hands-on Software Architect | 20+ years in Tech | Author | Father. Opinions are my own!","2026-11-07 09:01:46",{"source":7,"skillName":5,"commands":40,"prompt":50,"zipAvailable":51},[41,44,47],{"tool":42,"command":43},"npx","npx skills add diegopacheco\u002Fai-playground --skill infra-automation-generator",{"tool":45,"command":46},"bunx","bunx skills add diegopacheco\u002Fai-playground --skill infra-automation-generator",{"tool":48,"command":49},"pnpm","pnpm dlx skills add diegopacheco\u002Fai-playground --skill infra-automation-generator","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fagent-skill-infra-automation-generator\u002Fskills\u002Finfra-automation-generator\u002FSKILL.md",true,[53,59,65,69,73,77,81,85,91],{"path":54,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":57,"sourceHtmlUrl":58},"install.sh","SCRIPT","脚本",498,"https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fagent-skill-infra-automation-generator\u002Fskills\u002Finfra-automation-generator\u002Finstall.sh",{"path":60,"resourceType":61,"resourceTypeDesc":62,"sizeBytes":63,"sourceHtmlUrl":64},"LICENSE.upstream","LICENSE","许可证",1070,"https:\u002F\u002Fgithub.com\u002Fvox-foundation\u002Fvox\u002Fblob\u002Fmain\u002Fassets\u002Fskills\u002Fbrainstorming\u002FLICENSE.upstream",{"path":66,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":67,"sourceHtmlUrl":68},"scripts\u002Fframe-template.html",7703,"https:\u002F\u002Fgithub.com\u002Fvox-foundation\u002Fvox\u002Fblob\u002Fmain\u002Fassets\u002Fskills\u002Fbrainstorming\u002Fscripts\u002Fframe-template.html",{"path":70,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":71,"sourceHtmlUrl":72},"scripts\u002Fhelper.js",2707,"https:\u002F\u002Fgithub.com\u002Fvox-foundation\u002Fvox\u002Fblob\u002Fmain\u002Fassets\u002Fskills\u002Fbrainstorming\u002Fscripts\u002Fhelper.js",{"path":74,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":75,"sourceHtmlUrl":76},"scripts\u002Fserver.cjs",11218,"https:\u002F\u002Fgithub.com\u002Fvox-foundation\u002Fvox\u002Fblob\u002Fmain\u002Fassets\u002Fskills\u002Fbrainstorming\u002Fscripts\u002Fserver.cjs",{"path":78,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":79,"sourceHtmlUrl":80},"scripts\u002Fstart-server.sh",4606,"https:\u002F\u002Fgithub.com\u002Fvox-foundation\u002Fvox\u002Fblob\u002Fmain\u002Fassets\u002Fskills\u002Fbrainstorming\u002Fscripts\u002Fstart-server.sh",{"path":82,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":83,"sourceHtmlUrl":84},"scripts\u002Fstop-server.sh",1314,"https:\u002F\u002Fgithub.com\u002Fvox-foundation\u002Fvox\u002Fblob\u002Fmain\u002Fassets\u002Fskills\u002Fbrainstorming\u002Fscripts\u002Fstop-server.sh",{"path":86,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":89,"sourceHtmlUrl":90},"uninstall.md","REFERENCE","参考文档",399,"https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fagent-skill-infra-automation-generator\u002Fskills\u002Finfra-automation-generator\u002Funinstall.md",{"path":92,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":93,"sourceHtmlUrl":94},"visual-companion.md",12047,"https:\u002F\u002Fgithub.com\u002Fvox-foundation\u002Fvox\u002Fblob\u002Fmain\u002Fassets\u002Fskills\u002Fbrainstorming\u002Fvisual-companion.md",[96,112,122,135,151,170],{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":105,"publishedAt":25},"01M6WJ537DN520XY8MSW9ZV0RP","word-count","Counts the words, lines and characters of a text or a file.","https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fskills-gateway-router\u002Fupstream-skills\u002Ftext-skills\u002Fword-count\u002FSKILL.md",[102],{"categoryNo":103,"categoryName":104,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000002","文档处理",[106,107,108,109,110,111],"count","word count","Word","characters","text","file",{"skillNo":113,"name":114,"nameZh":6,"description":115,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":118,"publishedAt":25},"01M6WJ4Y2GT5SECWAKKYHGES4F","slugify","Turns a title or a sentence into a URL slug, lowercase words joined by hyphens with accents removed.","https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fskills-gateway-router\u002Fupstream-skills\u002Ftext-skills\u002Fslugify\u002FSKILL.md",[],[114,119,120,121],"title","with","URL",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":128,"publishedAt":25},"01M6WJ4V9CWHT9NVCD441VK5MH","json-format","Validates a JSON file and pretty prints it with two space indentation, or reports where the JSON is invalid.","https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fskills-gateway-router\u002Fupstream-skills\u002Fdev-skills\u002Fjson-format\u002FSKILL.md",[],[129,130,131,111,132,133,134,120],"json","format","json format","reports","invalid","it",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":141,"publishedAt":25},"01M6WJ4QMRKW8EFP71F4KS2DHE","commit-message","Writes a conventional commit message for the staged git changes.","https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fskills-gateway-router\u002Fupstream-skills\u002Fdev-skills\u002Fcommit-message\u002FSKILL.md",[],[142,143,144,145,146,147,148,149,150],"commit","commit message","message","git","conventional commit","for","changes","staged","conventional commit message",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":157,"publishedAt":25},"01M6WJ4MAM2CJH0GWYXFBB9GQC","skills-gateway","Gateway and router to a catalog of skills that are not registered directly. Use it before any task a specialized skill could handle, such as text, JSON or git chores. It finds the right skill and loads its instructions.","https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fskills-gateway-router\u002F.claude\u002Fskills\u002Fskills-gateway\u002FSKILL.md",[],[158,159,134,160,161,145,162,129,110,163,164,165,166,167,168,169],"skills gateway","gateway","task","loads","Bash","node","specialized","instructions","catalog","use","router","as",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":176,"publishedAt":25},"01M6M2PYJJQKMWGX7XT7ZJK0H3","decision-log","Record, list, show and supersede architecture decisions that persist across sessions in the shared agent infra state store. Use when the user says \"record this decision\", \"log an ADR\", \"what did we decide about\", \"list decisions\", or \"supersede decision\".","https:\u002F\u002Fgithub.com\u002Fdiegopacheco\u002Fai-playground\u002Fblob\u002Fmain\u002Fpocs\u002Fagent-skills-infra-poc\u002Fskills\u002Fdecision-log\u002FSKILL.md",[],[177,178,179,180,181,182,183,184,185,186,167,187,188,189,190,191,192,193,194,195],"decision log","log","decision","persist","architecture","state","adr","decide","decisions","list","show","infra","record","across","shared","store","about","supersede","sessions"]