skills/keel/SKILL.md
Generate Infrastructure as Code (IaC) pipelines that provision the cloud and container infrastructure your app deploys to. Interview-driven: detects your stack and cloud provider, then outputs deterministic IaC files (Terraform, Bicep, Pulumi, or CDK) plus CI/CD pipelines that plan on PR and apply on merge. Use when setting up cloud infrastructure, provisioning container registries, databases, networking, DNS, or any infrastructure that containers deploy onto. Designed to run before /dock — /keel lays the infrastructure, /dock deploys to it.
npx skillsauth add slamb2k/mad-skills keelInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When this skill is invoked, IMMEDIATELY output the banner below before doing anything else. Pick ONE tagline at random — vary your choice each time. CRITICAL: Reproduce the banner EXACTLY character-for-character. The first line of the art has 4 leading spaces — you MUST preserve them.
{tagline}
⠀ ██╗██╗ ██╗███████╗███████╗██╗
██╔╝██║ ██╔╝██╔════╝██╔════╝██║
██╔╝ █████╔╝ █████╗ █████╗ ██║
██╔╝ ██╔═██╗ ██╔══╝ ██╔══╝ ██║
██╔╝ ██║ ██╗███████╗███████╗███████╗
╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝
Taglines:
Provision cloud and container infrastructure through an interview-driven workflow
that generates deterministic IaC files and CI/CD pipelines. The generated pipelines
run plan on every PR and apply on merge to the default branch — no LLM at runtime.
Recommended skill order:
/brace → /rig → /build → /ship → /keel → /dock
/keel lays the infrastructure (registries, compute, networking, databases). /dock creates the deployment pipelines that push containers onto that infrastructure.
Parse optional flags from the request:
--plan-only: Generate IaC files but no CI/CD pipeline--skip-interview: Use detected defaults without interactive prompts--dry-run: Show what would be generated without writing files--tool <name>: Force a specific IaC tool (terraform, bicep, pulumi, cdk)Input display: ┌─ Input │ {fields} └─. Pre-flight: ── Pre-flight ── ✅/⚠️/❌ ──.
Stage headers: ━━ {N} · {Name} ━━━━━━━━━━━━━. Icons: ✅ done · ❌ fail · ⚠️ warn · ⏳ work · ⏭️ skip.
Detect the hosting platform before pre-flight so dependency checks are platform-specific:
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
if echo "$REMOTE_URL" | grep -qiE 'dev\.azure\.com|visualstudio\.com'; then
PLATFORM="azdo"
elif echo "$REMOTE_URL" | grep -qi 'github\.com'; then
PLATFORM="github"
else
PLATFORM="github" # default fallback
fi
Pass {PLATFORM} into all phase prompts. Each phase uses the appropriate
CLI tool: gh for GitHub, az repos/az pipelines for Azure DevOps.
Before starting, check all dependencies in this table. The table contains all dependencies — some are platform-conditional (see notes after table).
| Dependency | Type | Check | Required | Resolution | Detail |
|-----------|------|-------|----------|------------|--------|
| git | cli | git --version | yes | stop | Install from https://git-scm.com |
| sync | skill | ls .claude/skills/sync/SKILL.md ~/.claude/skills/sync/SKILL.md ~/.claude/plugins/marketplaces/slamb2k/skills/sync/SKILL.md 2>/dev/null | no | fallback | Repo sync; falls back to manual git pull |
| terraform | cli | terraform --version | no | fallback | Detected if user wants Terraform; suggest install from https://terraform.io |
| az | cli | az --version | no | fallback | Needed for Bicep; suggest install from https://aka.ms/install-azure-cli |
| pulumi | cli | pulumi version | no | fallback | Detected if user wants Pulumi; suggest install from https://pulumi.com |
| cdk | cli | cdk --version | no | fallback | Detected if user wants AWS CDK; install via npm |
| gh | cli | gh --version | yes | url | https://cli.github.com |
| az devops | cli | az devops -h 2>/dev/null | no | fallback | Falls back to REST API with PAT; see AzDO tooling below |
Platform-conditional rules:
gh: Only required when PLATFORM == github. Skip for AzDO repos.az devops: Only checked when PLATFORM == azdo. Skip for GitHub repos.Only check the IaC tool row that matches the user's choice (or detected default). Skip checks for tools not being used.
For each applicable row, in order:
{PLATFORM}When PLATFORM == azdo, follow the shared AzDO platform guide
(repo root: references/azdo-platform.md) for tooling detection (AZDO_MODE)
and configuration validation (AZDO_ORG, AZDO_PROJECT).
Pass these variables into all phase prompts alongside {PLATFORM}.
Invoke /sync to ensure the working tree is up to date with origin/main.
Falls back to git pull if /sync is unavailable.
Launch an Explore subagent to scan the codebase for existing infrastructure.
Task(
subagent_type: "Explore",
description: "Detect existing IaC and cloud config",
prompt: <read from references/interview-guide.md#detection-prompt>
)
The detection scans for:
Parse INFRA_DETECTION_REPORT. This feeds into the interview.
Present detection results and fill gaps through guided questions.
Read the full interview flow from references/interview-guide.md#interview-questions.
The interview covers these topics in order. Skip questions where detection provided high-confidence answers (but confirm with the user).
Topics covered (full prompts and options in references/interview-guide.md):
If --skip-interview: Use detected defaults + sensible defaults.
Compile all answers into an INFRA_CONFIG object for Phase 3.
Based on INFRA_DETECTION_REPORT and INFRA_CONFIG, generate all IaC files.
Use templates from references/ as starting points.
Generate an infra/ directory with this layout:
Terraform:
infra/
├── main.tf # Provider config, backend, module calls
├── variables.tf # Input variables
├── outputs.tf # Output values (registry URL, endpoints, etc.)
├── versions.tf # Required providers and versions
├── environments/
│ ├── dev.tfvars # Dev variable values
│ ├── staging.tfvars # Staging variable values
│ └── prod.tfvars # Prod variable values
└── modules/
├── registry/ # Container registry
├── compute/ # Container platform (AKS, ECS, Cloud Run, etc.)
├── database/ # Managed database
├── networking/ # VPC, DNS, load balancer
└── monitoring/ # Logging, metrics, alerts
Bicep:
infra/
├── main.bicep # Orchestration (module calls)
├── main.bicepparam # Parameter file template
├── environments/
│ ├── dev.bicepparam
│ ├── staging.bicepparam
│ └── prod.bicepparam
└── modules/
├── registry.bicep
├── compute.bicep
├── database.bicep
├── networking.bicep
└── monitoring.bicep
Pulumi:
infra/
├── Pulumi.yaml # Project config
├── Pulumi.dev.yaml # Dev stack config
├── Pulumi.staging.yaml # Staging stack config
├── Pulumi.prod.yaml # Prod stack config
├── index.ts # Main program (or __main__.py for Python)
└── resources/
├── registry.ts
├── compute.ts
├── database.ts
└── networking.ts
Only generate modules for components selected in the interview.
Generate infra/bootstrap.sh for first-time state backend setup.
Use the provider-specific template from references/iac-pipeline-templates.md#bootstrap-scripts.
Read the appropriate template from references/iac-pipeline-templates.md.
Platform branching:
PLATFORM == github: Generate a GitHub Actions workflow. Output file:
.github/workflows/infra.yml. Use templates from the "GitHub Actions" section.PLATFORM == azdo: Generate an Azure DevOps Pipelines YAML file. Output
file: azure-pipelines-infra.yml. Use templates from the "Azure DevOps
Pipelines" section.Generate a workflow that implements:
On pull request:
terraform init (or equivalent)terraform plan with the target environment's varsOn merge to default branch:
terraform initterraform planterraform apply -auto-approveOn environment promotion (tag or manual dispatch):
Generate an outputs file that /dock's pipelines can consume:
# outputs.tf
output "registry_url" {
value = module.registry.login_server
}
output "registry_name" {
value = module.registry.name
}
output "compute_endpoint" {
value = module.compute.endpoint
}
output "database_connection_string" {
value = module.database.connection_string
sensitive = true
}
These outputs become inputs for /dock's deployment pipelines — the registry URL for pushing images, the compute endpoint for deploying containers, etc.
Generate a script or workflow step that reads IaC outputs and writes them as CI/CD secrets/variables for /dock's pipelines.
Platform branching:
PLATFORM == github: Use gh secret set / gh variable set commands.
Reference references/iac-pipeline-templates.md#infra/sync-outputs.sh.PLATFORM == azdo: Use az pipelines variable-group variable update /
az pipelines variable-group variable create commands. Reference
references/iac-pipeline-templates.md#infra/sync-outputs.sh (Azure DevOps).GitHub example:
# infra/sync-outputs.sh
REGISTRY_URL=$(terraform output -raw registry_url)
gh secret set REGISTRY_URL --body "$REGISTRY_URL"
gh secret set DATABASE_URL --body "$(terraform output -raw database_connection_string)"
Azure DevOps example:
# infra/sync-outputs.sh
REGISTRY_URL=$(terraform output -raw registry_url)
az pipelines variable-group variable update --group-id "$VG_ID" --name REGISTRY_URL --value "$REGISTRY_URL"
az pipelines variable-group variable update --group-id "$VG_ID" --name DATABASE_URL --value "$(terraform output -raw database_connection_string)" --secret true
Validate generated IaC files:
Terraform:
cd infra && terraform init -backend=false && terraform validate
Bicep:
az bicep build --file infra/main.bicep
Pulumi:
cd infra && pulumi preview --stack dev
If the IaC tool is not installed, skip validation and note it in the report.
Also validate generated workflow files for YAML syntax.
Present the user with a summary of all generated files before writing.
If --dry-run: Show file list and content previews without writing.
After all files are generated and verified, present:
┌─ Keel · Report ────────────────────────────────
│
│ ✅ Keel complete
│
│ ☁️ Provider: {cloud_provider}
│ 🔧 IaC Tool: {tool}
│ 🌍 Envs: {env1} → {env2} → {env3}
│ 💾 State: {state_backend}
│
│ 📝 Components: {checklist of selected}
│
│ 📊 Pipeline: PR → plan → Merge → apply dev → Tag → apply prod
│
│ 🔗 Links
│ Infra: {infra/ path}
│ Pipeline: {workflow path}
│ Bootstrap: ./infra/bootstrap.sh
│
│ ⚡ Next: 1. Run bootstrap 2. Configure secrets 3. Push 4. /dock
│
└─────────────────────────────────────────────────
If /keel detects it has been run before (existing infra/ directory):
infra/ and wires IaC pipeline into CI. /ship: Merge triggers IaC apply → /dock deploy.testing
Run the full OMC idea-to-merged-PR pipeline — cancel + deep-interview + ralplan + autopilot + mad-skills:ship — in a single invocation. Explicit-only; this skill never auto-activates. Only run when the user literally types /launch. Do not invoke on phrases like "launch this", "ship it", "full pipeline", or similar — none of those should trigger this skill.
testing
Ship changes through the full PR lifecycle. Use after completing feature work to commit, push, create PR, wait for checks, and merge. Handles the entire workflow: syncs with main, creates feature branch if needed, groups commits logically with semantic messages, creates detailed PR, monitors CI, fixes issues, squash merges, and cleans up. Invoke when work is ready to ship.
development
Generate container-based release pipelines that build once and promote immutable artifacts through environments (dev → staging → prod). Detects your stack, interviews for infrastructure choices, then outputs deterministic CI/CD files (Dockerfile, workflows, deployment manifests) that run without an LLM. Use when setting up deployment pipelines, containerizing an app, creating release workflows, or connecting CI to container-friendly infrastructure (Azure Container Apps, AWS Fargate, Google Cloud Run, Kubernetes, Dokku, Coolify, CapRover, etc.).
development
Initialize any project directory with a standard scaffold for AI-assisted development. Creates specs/ and context/ directories, a project CLAUDE.md with development workflow and guardrails, .gitignore, and branch protection. Recommends claude-mem for persistent memory. Idempotent — safe to run on existing projects. Triggers: "init project", "setup brace", "brace", "initialize", "bootstrap", "scaffold".