@slamb2k/mad-skills 2.0.28 → 2.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/skills/dock/SKILL.md +119 -7
- package/skills/dock/references/dockerfile-templates.md +3 -0
- package/skills/dock/references/interview-guide.md +7 -1
- package/skills/dock/tests/evals.json +9 -0
- package/skills/keel/SKILL.md +145 -3
- package/skills/keel/references/iac-pipeline-templates.md +147 -0
- package/skills/keel/tests/evals.json +9 -0
- package/skills/manifest.json +3 -3
package/package.json
CHANGED
package/skills/dock/SKILL.md
CHANGED
|
@@ -81,6 +81,27 @@ Status icons: ✅ done · ❌ failed · ⚠️ degraded · ⏳ working · ⏭️
|
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
84
|
+
## Platform Detection
|
|
85
|
+
|
|
86
|
+
Detect the hosting platform **before** pre-flight so dependency checks are
|
|
87
|
+
platform-specific:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
|
91
|
+
if echo "$REMOTE_URL" | grep -qiE 'dev\.azure\.com|visualstudio\.com'; then
|
|
92
|
+
PLATFORM="azdo"
|
|
93
|
+
elif echo "$REMOTE_URL" | grep -qi 'github\.com'; then
|
|
94
|
+
PLATFORM="github"
|
|
95
|
+
else
|
|
96
|
+
PLATFORM="github" # default fallback
|
|
97
|
+
fi
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Pass `{PLATFORM}` into all phase prompts. Each phase uses the appropriate
|
|
101
|
+
CLI tool and registry defaults based on the detected platform.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
84
105
|
## Pre-flight
|
|
85
106
|
|
|
86
107
|
Before starting, check dependencies:
|
|
@@ -90,12 +111,101 @@ Before starting, check dependencies:
|
|
|
90
111
|
| docker | cli | `docker --version` | no | ask | Needed for local build verification; skip verify phase if absent |
|
|
91
112
|
| git | cli | `git --version` | yes | stop | Install from https://git-scm.com |
|
|
92
113
|
| sync | skill | `~/.claude/skills/sync/SKILL.md` or `~/.claude/plugins/marketplaces/slamb2k/skills/sync/SKILL.md` | no | fallback | Repo sync; falls back to manual git pull |
|
|
114
|
+
| az devops | cli | `az devops -h 2>/dev/null` | no | fallback | Falls back to REST API with PAT; see AzDO tooling below |
|
|
115
|
+
|
|
116
|
+
**Platform-conditional rules:**
|
|
117
|
+
- **`az devops`**: Only checked when `PLATFORM == azdo`. Skip for GitHub repos.
|
|
118
|
+
|
|
119
|
+
For each applicable row, in order:
|
|
120
|
+
1. Skip rows that don't apply to the detected `{PLATFORM}`
|
|
121
|
+
2. Run the Check command (for cli/npm) or test file existence (for agent/skill)
|
|
122
|
+
3. If found: continue silently
|
|
123
|
+
4. If missing: apply Resolution strategy
|
|
124
|
+
- **stop**: notify user with Detail, halt execution
|
|
125
|
+
- **url**: notify user with Detail (install link), halt execution
|
|
126
|
+
- **install**: notify user, run the command in Detail, continue if successful
|
|
127
|
+
- **ask**: notify user, offer to run command in Detail, continue either way (or halt if required)
|
|
128
|
+
- **fallback**: notify user with Detail, continue with degraded behavior
|
|
129
|
+
5. After all checks: summarize what's available and what's degraded
|
|
130
|
+
|
|
131
|
+
### AzDO Tooling Detection
|
|
132
|
+
|
|
133
|
+
When `PLATFORM == azdo`, determine which tooling is available. Set `AZDO_MODE`
|
|
134
|
+
for use in all subsequent phases:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
if az devops -h &>/dev/null; then
|
|
138
|
+
AZDO_MODE="cli"
|
|
139
|
+
else
|
|
140
|
+
AZDO_MODE="rest"
|
|
141
|
+
fi
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- **`cli`**: Use `az repos` / `az pipelines` commands (preferred)
|
|
145
|
+
- **`rest`**: Use Azure DevOps REST API via `curl`. Requires a PAT (personal
|
|
146
|
+
access token) in `AZURE_DEVOPS_EXT_PAT` or `AZDO_PAT` env var. If no PAT
|
|
147
|
+
is found, prompt the user to either install the CLI or set the env var.
|
|
148
|
+
|
|
149
|
+
Report in pre-flight:
|
|
150
|
+
- ✅ `az devops cli` — version found
|
|
151
|
+
- ⚠️ `az devops cli` — not found → using REST API fallback
|
|
152
|
+
- ❌ `az devops cli` — not found, no PAT configured → halt with setup instructions
|
|
153
|
+
|
|
154
|
+
### AzDO Configuration Validation
|
|
155
|
+
|
|
156
|
+
When `PLATFORM == azdo`, extract organization and project from the remote URL
|
|
157
|
+
and validate they are usable. These values are needed by every `az repos` /
|
|
158
|
+
`az pipelines` command and every REST API call.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Extract org and project from remote URL patterns:
|
|
162
|
+
# https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
|
|
163
|
+
# https://{ORG}@dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
|
|
164
|
+
# {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}
|
|
165
|
+
|
|
166
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
|
167
|
+
|
|
168
|
+
if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
|
|
169
|
+
AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/\([^/]*\)/.*|\1|p')
|
|
170
|
+
AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/[^/]*/\([^/]*\)/.*|\1|p')
|
|
171
|
+
AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
|
|
172
|
+
elif echo "$REMOTE_URL" | grep -q 'vs-ssh\.visualstudio\.com'; then
|
|
173
|
+
AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/\([^/]*\)/.*|\1|p')
|
|
174
|
+
AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/[^/]*/\([^/]*\)/.*|\1|p')
|
|
175
|
+
AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
|
|
176
|
+
elif echo "$REMOTE_URL" | grep -q 'visualstudio\.com'; then
|
|
177
|
+
AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*//\([^.]*\)\.visualstudio\.com.*|\1|p')
|
|
178
|
+
AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*/\([^/]*\)/_git/.*|\1|p')
|
|
179
|
+
AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
if [ -z "$AZDO_ORG" ] || [ -z "$AZDO_PROJECT" ]; then
|
|
183
|
+
echo "❌ Could not extract organization/project from remote URL"
|
|
184
|
+
echo " Remote: $REMOTE_URL"
|
|
185
|
+
echo ""
|
|
186
|
+
echo "Ensure the remote URL follows one of these formats:"
|
|
187
|
+
echo " https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}"
|
|
188
|
+
echo " https://{ORG}.visualstudio.com/{PROJECT}/_git/{REPO}"
|
|
189
|
+
echo " {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}"
|
|
190
|
+
# HALT — cannot proceed without org/project context
|
|
191
|
+
fi
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
When `AZDO_MODE == cli`, also configure the defaults so commands work correctly:
|
|
195
|
+
```bash
|
|
196
|
+
az devops configure --defaults organization="$AZDO_ORG_URL" project="$AZDO_PROJECT"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
When `AZDO_MODE == rest`, store these for API calls:
|
|
200
|
+
- Base URL: `$AZDO_ORG_URL/$AZDO_PROJECT/_apis`
|
|
201
|
+
- Auth header: `Authorization: Basic $(echo -n ":$PAT" | base64)`
|
|
202
|
+
|
|
203
|
+
Report in pre-flight:
|
|
204
|
+
- ✅ `azdo context` — org: `{AZDO_ORG}`, project: `{AZDO_PROJECT}`
|
|
205
|
+
- ❌ `azdo context` — could not parse from remote URL → halt with instructions
|
|
93
206
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
2. If found: continue silently
|
|
97
|
-
3. If missing: apply Resolution strategy
|
|
98
|
-
4. After all checks: summarize availability
|
|
207
|
+
Pass `{AZDO_MODE}`, `{AZDO_ORG}`, `{AZDO_PROJECT}`, `{AZDO_ORG_URL}` into
|
|
208
|
+
all phase prompts alongside `{PLATFORM}`.
|
|
99
209
|
|
|
100
210
|
---
|
|
101
211
|
|
|
@@ -206,8 +316,10 @@ Optional — ask only if deploying to platforms that need this:
|
|
|
206
316
|
- **Blue-green**: maintain two environments, swap traffic
|
|
207
317
|
- **Canary**: gradual traffic shift with automatic rollback on error thresholds
|
|
208
318
|
|
|
209
|
-
**If `--skip-interview` flag**: Use detected defaults + sensible
|
|
210
|
-
everything else
|
|
319
|
+
**If `--skip-interview` flag**: Use detected defaults + sensible platform-aware
|
|
320
|
+
defaults for everything else:
|
|
321
|
+
- **GitHub** (`PLATFORM == github`): ghcr.io, 3-stage, GitHub Secrets, simple rollback
|
|
322
|
+
- **Azure DevOps** (`PLATFORM == azdo`): Azure Container Registry, 3-stage, Azure Key Vault, simple rollback
|
|
211
323
|
|
|
212
324
|
Compile all answers into a DOCK_CONFIG object for Phase 3.
|
|
213
325
|
|
|
@@ -141,6 +141,9 @@ CMD ["gunicorn", "--bind", "0.0.0.0:{PORT}", "--workers", "4", "app:app"]
|
|
|
141
141
|
FROM python:3.12-slim AS deps
|
|
142
142
|
WORKDIR /app
|
|
143
143
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
|
144
|
+
# NOTE: ghcr.io/astral-sh/uv is the official uv installer image, not your app registry.
|
|
145
|
+
# If ghcr.io is blocked (e.g., corporate firewall), mirror this image to your
|
|
146
|
+
# internal registry: COPY --from={INTERNAL_REGISTRY}/astral-sh/uv:latest /uv /usr/local/bin/uv
|
|
144
147
|
COPY pyproject.toml uv.lock ./
|
|
145
148
|
RUN uv sync --frozen --no-dev --no-install-project
|
|
146
149
|
|
|
@@ -117,7 +117,9 @@ Mapping:
|
|
|
117
117
|
- GitHub Actions → suggest ghcr.io
|
|
118
118
|
- Azure Pipelines → suggest Azure Container Registry
|
|
119
119
|
- GitLab CI → suggest GitLab Container Registry
|
|
120
|
-
- No CI →
|
|
120
|
+
- No CI → detect platform from git remote URL:
|
|
121
|
+
- AzDO remote → suggest Azure Container Registry
|
|
122
|
+
- GitHub remote or unknown → suggest ghcr.io (most universal)
|
|
121
123
|
|
|
122
124
|
### Environment Topology
|
|
123
125
|
|
|
@@ -181,6 +183,10 @@ Options:
|
|
|
181
183
|
5. Doppler
|
|
182
184
|
6. 1Password (via CLI)
|
|
183
185
|
7. HashiCorp Vault
|
|
186
|
+
|
|
187
|
+
Default selection based on platform:
|
|
188
|
+
- `PLATFORM == github` → default to GitHub Secrets / Variables
|
|
189
|
+
- `PLATFORM == azdo` → default to Azure Key Vault
|
|
184
190
|
```
|
|
185
191
|
|
|
186
192
|
### Rollback Strategy
|
|
@@ -32,5 +32,14 @@
|
|
|
32
32
|
{ "type": "semantic", "value": "Supports different deployment targets per environment" },
|
|
33
33
|
{ "type": "semantic", "value": "References both Azure Container Apps and Dokku as deployment platforms" }
|
|
34
34
|
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "azdo-awareness",
|
|
38
|
+
"prompt": "/dock for a Python FastAPI app in an Azure DevOps repo with azure-pipelines.yml already present",
|
|
39
|
+
"assertions": [
|
|
40
|
+
{ "type": "semantic", "value": "Detects or acknowledges Azure DevOps as the platform based on the repository context" },
|
|
41
|
+
{ "type": "semantic", "value": "Suggests Azure Container Registry rather than ghcr.io as the default registry" },
|
|
42
|
+
{ "type": "semantic", "value": "References Azure Pipelines as the CI system for the generated workflow" }
|
|
43
|
+
]
|
|
35
44
|
}
|
|
36
45
|
]
|
package/skills/keel/SKILL.md
CHANGED
|
@@ -68,9 +68,31 @@ Stage headers: `━━ {N} · {Name} ━━━━━━━━━━━━━`. I
|
|
|
68
68
|
|
|
69
69
|
---
|
|
70
70
|
|
|
71
|
+
## Platform Detection
|
|
72
|
+
|
|
73
|
+
Detect the hosting platform **before** pre-flight so dependency checks are
|
|
74
|
+
platform-specific:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
|
78
|
+
if echo "$REMOTE_URL" | grep -qiE 'dev\.azure\.com|visualstudio\.com'; then
|
|
79
|
+
PLATFORM="azdo"
|
|
80
|
+
elif echo "$REMOTE_URL" | grep -qi 'github\.com'; then
|
|
81
|
+
PLATFORM="github"
|
|
82
|
+
else
|
|
83
|
+
PLATFORM="github" # default fallback
|
|
84
|
+
fi
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Pass `{PLATFORM}` into all phase prompts. Each phase uses the appropriate
|
|
88
|
+
CLI tool: `gh` for GitHub, `az repos`/`az pipelines` for Azure DevOps.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
71
92
|
## Pre-flight
|
|
72
93
|
|
|
73
|
-
Before starting, check dependencies
|
|
94
|
+
Before starting, check all dependencies in this table. The table contains
|
|
95
|
+
**all** dependencies — some are platform-conditional (see notes after table).
|
|
74
96
|
|
|
75
97
|
| Dependency | Type | Check | Required | Resolution | Detail |
|
|
76
98
|
|-----------|------|-------|----------|------------|--------|
|
|
@@ -80,10 +102,107 @@ Before starting, check dependencies:
|
|
|
80
102
|
| az | cli | `az --version` | no | fallback | Needed for Bicep; suggest install from https://aka.ms/install-azure-cli |
|
|
81
103
|
| pulumi | cli | `pulumi version` | no | fallback | Detected if user wants Pulumi; suggest install from https://pulumi.com |
|
|
82
104
|
| cdk | cli | `cdk --version` | no | fallback | Detected if user wants AWS CDK; install via npm |
|
|
105
|
+
| gh | cli | `gh --version` | yes | url | https://cli.github.com |
|
|
106
|
+
| az devops | cli | `az devops -h 2>/dev/null` | no | fallback | Falls back to REST API with PAT; see AzDO tooling below |
|
|
107
|
+
|
|
108
|
+
**Platform-conditional rules:**
|
|
109
|
+
- **`gh`**: Only required when `PLATFORM == github`. Skip for AzDO repos.
|
|
110
|
+
- **`az devops`**: Only checked when `PLATFORM == azdo`. Skip for GitHub repos.
|
|
83
111
|
|
|
84
|
-
Only check the tool that matches the user's choice (or detected default).
|
|
112
|
+
Only check the IaC tool row that matches the user's choice (or detected default).
|
|
85
113
|
Skip checks for tools not being used.
|
|
86
114
|
|
|
115
|
+
For each applicable row, in order:
|
|
116
|
+
1. Skip rows that don't apply to the detected `{PLATFORM}`
|
|
117
|
+
2. Run the Check command (for cli/npm) or test file existence (for agent/skill)
|
|
118
|
+
3. If found: continue silently
|
|
119
|
+
4. If missing: apply Resolution strategy
|
|
120
|
+
- **stop**: notify user with Detail, halt execution
|
|
121
|
+
- **url**: notify user with Detail (install link), halt execution
|
|
122
|
+
- **install**: notify user, run the command in Detail, continue if successful
|
|
123
|
+
- **ask**: notify user, offer to run command in Detail, continue either way (or halt if required)
|
|
124
|
+
- **fallback**: notify user with Detail, continue with degraded behavior
|
|
125
|
+
5. After all checks: summarize what's available and what's degraded
|
|
126
|
+
|
|
127
|
+
### AzDO Tooling Detection
|
|
128
|
+
|
|
129
|
+
When `PLATFORM == azdo`, determine which tooling is available. Set `AZDO_MODE`
|
|
130
|
+
for use in all subsequent phases:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
if az devops -h &>/dev/null; then
|
|
134
|
+
AZDO_MODE="cli"
|
|
135
|
+
else
|
|
136
|
+
AZDO_MODE="rest"
|
|
137
|
+
fi
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- **`cli`**: Use `az repos` / `az pipelines` commands (preferred)
|
|
141
|
+
- **`rest`**: Use Azure DevOps REST API via `curl`. Requires a PAT (personal
|
|
142
|
+
access token) in `AZURE_DEVOPS_EXT_PAT` or `AZDO_PAT` env var. If no PAT
|
|
143
|
+
is found, prompt the user to either install the CLI or set the env var.
|
|
144
|
+
|
|
145
|
+
Report in pre-flight:
|
|
146
|
+
- ✅ `az devops cli` — version found
|
|
147
|
+
- ⚠️ `az devops cli` — not found → using REST API fallback
|
|
148
|
+
- ❌ `az devops cli` — not found, no PAT configured → halt with setup instructions
|
|
149
|
+
|
|
150
|
+
### AzDO Configuration Validation
|
|
151
|
+
|
|
152
|
+
When `PLATFORM == azdo`, extract organization and project from the remote URL
|
|
153
|
+
and validate they are usable. These values are needed by every `az repos` /
|
|
154
|
+
`az pipelines` command and every REST API call.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Extract org and project from remote URL patterns:
|
|
158
|
+
# https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
|
|
159
|
+
# https://{ORG}@dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
|
|
160
|
+
# {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}
|
|
161
|
+
|
|
162
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
|
|
163
|
+
|
|
164
|
+
if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
|
|
165
|
+
AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/\([^/]*\)/.*|\1|p')
|
|
166
|
+
AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/[^/]*/\([^/]*\)/.*|\1|p')
|
|
167
|
+
AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
|
|
168
|
+
elif echo "$REMOTE_URL" | grep -q 'vs-ssh\.visualstudio\.com'; then
|
|
169
|
+
AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/\([^/]*\)/.*|\1|p')
|
|
170
|
+
AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/[^/]*/\([^/]*\)/.*|\1|p')
|
|
171
|
+
AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
|
|
172
|
+
elif echo "$REMOTE_URL" | grep -q 'visualstudio\.com'; then
|
|
173
|
+
AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*//\([^.]*\)\.visualstudio\.com.*|\1|p')
|
|
174
|
+
AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*/\([^/]*\)/_git/.*|\1|p')
|
|
175
|
+
AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
if [ -z "$AZDO_ORG" ] || [ -z "$AZDO_PROJECT" ]; then
|
|
179
|
+
echo "❌ Could not extract organization/project from remote URL"
|
|
180
|
+
echo " Remote: $REMOTE_URL"
|
|
181
|
+
echo ""
|
|
182
|
+
echo "Ensure the remote URL follows one of these formats:"
|
|
183
|
+
echo " https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}"
|
|
184
|
+
echo " https://{ORG}.visualstudio.com/{PROJECT}/_git/{REPO}"
|
|
185
|
+
echo " {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}"
|
|
186
|
+
# HALT — cannot proceed without org/project context
|
|
187
|
+
fi
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
When `AZDO_MODE == cli`, also configure the defaults so commands work correctly:
|
|
191
|
+
```bash
|
|
192
|
+
az devops configure --defaults organization="$AZDO_ORG_URL" project="$AZDO_PROJECT"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
When `AZDO_MODE == rest`, store these for API calls:
|
|
196
|
+
- Base URL: `$AZDO_ORG_URL/$AZDO_PROJECT/_apis`
|
|
197
|
+
- Auth header: `Authorization: Basic $(echo -n ":$PAT" | base64)`
|
|
198
|
+
|
|
199
|
+
Report in pre-flight:
|
|
200
|
+
- ✅ `azdo context` — org: `{AZDO_ORG}`, project: `{AZDO_PROJECT}`
|
|
201
|
+
- ❌ `azdo context` — could not parse from remote URL → halt with instructions
|
|
202
|
+
|
|
203
|
+
Pass `{AZDO_MODE}`, `{AZDO_ORG}`, `{AZDO_PROJECT}`, `{AZDO_ORG_URL}` into
|
|
204
|
+
all phase prompts alongside `{PLATFORM}`.
|
|
205
|
+
|
|
87
206
|
---
|
|
88
207
|
|
|
89
208
|
## Phase 0: Sync
|
|
@@ -360,6 +479,13 @@ Use the provider-specific template from `references/iac-pipeline-templates.md#bo
|
|
|
360
479
|
|
|
361
480
|
Read the appropriate template from `references/iac-pipeline-templates.md`.
|
|
362
481
|
|
|
482
|
+
**Platform branching:**
|
|
483
|
+
- When `PLATFORM == github`: Generate a GitHub Actions workflow. Output file:
|
|
484
|
+
`.github/workflows/infra.yml`. Use templates from the "GitHub Actions" section.
|
|
485
|
+
- When `PLATFORM == azdo`: Generate an Azure DevOps Pipelines YAML file. Output
|
|
486
|
+
file: `azure-pipelines-infra.yml`. Use templates from the "Azure DevOps
|
|
487
|
+
Pipelines" section.
|
|
488
|
+
|
|
363
489
|
Generate a workflow that implements:
|
|
364
490
|
|
|
365
491
|
**On pull request:**
|
|
@@ -408,8 +534,16 @@ for pushing images, the compute endpoint for deploying containers, etc.
|
|
|
408
534
|
### 3.5 — Environment Variables Bridge
|
|
409
535
|
|
|
410
536
|
Generate a script or workflow step that reads IaC outputs and writes them as
|
|
411
|
-
CI/CD secrets/variables for /dock's pipelines
|
|
537
|
+
CI/CD secrets/variables for /dock's pipelines.
|
|
412
538
|
|
|
539
|
+
**Platform branching:**
|
|
540
|
+
- When `PLATFORM == github`: Use `gh secret set` / `gh variable set` commands.
|
|
541
|
+
Reference `references/iac-pipeline-templates.md#infra/sync-outputs.sh`.
|
|
542
|
+
- When `PLATFORM == azdo`: Use `az pipelines variable-group variable update` /
|
|
543
|
+
`az pipelines variable-group variable create` commands. Reference
|
|
544
|
+
`references/iac-pipeline-templates.md#infra/sync-outputs.sh (Azure DevOps)`.
|
|
545
|
+
|
|
546
|
+
**GitHub example:**
|
|
413
547
|
```bash
|
|
414
548
|
# infra/sync-outputs.sh
|
|
415
549
|
REGISTRY_URL=$(terraform output -raw registry_url)
|
|
@@ -417,6 +551,14 @@ gh secret set REGISTRY_URL --body "$REGISTRY_URL"
|
|
|
417
551
|
gh secret set DATABASE_URL --body "$(terraform output -raw database_connection_string)"
|
|
418
552
|
```
|
|
419
553
|
|
|
554
|
+
**Azure DevOps example:**
|
|
555
|
+
```bash
|
|
556
|
+
# infra/sync-outputs.sh
|
|
557
|
+
REGISTRY_URL=$(terraform output -raw registry_url)
|
|
558
|
+
az pipelines variable-group variable update --group-id "$VG_ID" --name REGISTRY_URL --value "$REGISTRY_URL"
|
|
559
|
+
az pipelines variable-group variable update --group-id "$VG_ID" --name DATABASE_URL --value "$(terraform output -raw database_connection_string)" --secret true
|
|
560
|
+
```
|
|
561
|
+
|
|
420
562
|
---
|
|
421
563
|
|
|
422
564
|
## Phase 4: Verify
|
|
@@ -10,6 +10,8 @@ Template variables:
|
|
|
10
10
|
- `{STATE_BACKEND}`: backend config details
|
|
11
11
|
- `{INFRA_DIR}`: path to IaC files (default: infra/)
|
|
12
12
|
- `{ENVIRONMENTS}`: list of environments (dev, staging, prod)
|
|
13
|
+
- `{PLATFORM}`: github or azdo — determines which pipeline template to use
|
|
14
|
+
- `{SERVICE_CONNECTION}`: Azure DevOps service connection name (AzDO only)
|
|
13
15
|
|
|
14
16
|
---
|
|
15
17
|
|
|
@@ -411,6 +413,96 @@ stages:
|
|
|
411
413
|
environmentServiceNameAzureRM: $(serviceConnection)
|
|
412
414
|
```
|
|
413
415
|
|
|
416
|
+
### Bicep
|
|
417
|
+
|
|
418
|
+
File: `azure-pipelines-infra.yml`
|
|
419
|
+
|
|
420
|
+
```yaml
|
|
421
|
+
trigger:
|
|
422
|
+
branches:
|
|
423
|
+
include: [{DEFAULT_BRANCH}]
|
|
424
|
+
paths:
|
|
425
|
+
include: [infra/*]
|
|
426
|
+
|
|
427
|
+
pr:
|
|
428
|
+
paths:
|
|
429
|
+
include: [infra/*]
|
|
430
|
+
|
|
431
|
+
variables:
|
|
432
|
+
infraDir: infra
|
|
433
|
+
serviceConnection: "{SERVICE_CONNECTION}"
|
|
434
|
+
location: "{REGION}"
|
|
435
|
+
|
|
436
|
+
stages:
|
|
437
|
+
- stage: WhatIf
|
|
438
|
+
condition: eq(variables['Build.Reason'], 'PullRequest')
|
|
439
|
+
jobs:
|
|
440
|
+
- job: BicepWhatIf
|
|
441
|
+
pool:
|
|
442
|
+
vmImage: ubuntu-latest
|
|
443
|
+
steps:
|
|
444
|
+
- task: AzureCLI@2
|
|
445
|
+
displayName: Bicep What-If
|
|
446
|
+
inputs:
|
|
447
|
+
azureSubscription: $(serviceConnection)
|
|
448
|
+
scriptType: bash
|
|
449
|
+
scriptLocation: inlineScript
|
|
450
|
+
inlineScript: |
|
|
451
|
+
az deployment sub what-if \
|
|
452
|
+
--location $(location) \
|
|
453
|
+
--template-file $(infraDir)/main.bicep \
|
|
454
|
+
--parameters $(infraDir)/environments/dev.bicepparam \
|
|
455
|
+
--no-pretty-print
|
|
456
|
+
|
|
457
|
+
- stage: DeployDev
|
|
458
|
+
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/{DEFAULT_BRANCH}'))
|
|
459
|
+
jobs:
|
|
460
|
+
- deployment: DeployDev
|
|
461
|
+
environment: dev
|
|
462
|
+
pool:
|
|
463
|
+
vmImage: ubuntu-latest
|
|
464
|
+
strategy:
|
|
465
|
+
runOnce:
|
|
466
|
+
deploy:
|
|
467
|
+
steps:
|
|
468
|
+
- checkout: self
|
|
469
|
+
- task: AzureCLI@2
|
|
470
|
+
displayName: Deploy Dev
|
|
471
|
+
inputs:
|
|
472
|
+
azureSubscription: $(serviceConnection)
|
|
473
|
+
scriptType: bash
|
|
474
|
+
scriptLocation: inlineScript
|
|
475
|
+
inlineScript: |
|
|
476
|
+
az deployment sub create \
|
|
477
|
+
--location $(location) \
|
|
478
|
+
--template-file $(infraDir)/main.bicep \
|
|
479
|
+
--parameters $(infraDir)/environments/dev.bicepparam
|
|
480
|
+
|
|
481
|
+
- stage: DeployEnv
|
|
482
|
+
condition: eq(variables['Build.Reason'], 'Manual')
|
|
483
|
+
jobs:
|
|
484
|
+
- deployment: DeployEnv
|
|
485
|
+
environment: $(targetEnvironment)
|
|
486
|
+
pool:
|
|
487
|
+
vmImage: ubuntu-latest
|
|
488
|
+
strategy:
|
|
489
|
+
runOnce:
|
|
490
|
+
deploy:
|
|
491
|
+
steps:
|
|
492
|
+
- checkout: self
|
|
493
|
+
- task: AzureCLI@2
|
|
494
|
+
displayName: Deploy $(targetEnvironment)
|
|
495
|
+
inputs:
|
|
496
|
+
azureSubscription: $(serviceConnection)
|
|
497
|
+
scriptType: bash
|
|
498
|
+
scriptLocation: inlineScript
|
|
499
|
+
inlineScript: |
|
|
500
|
+
az deployment sub create \
|
|
501
|
+
--location $(location) \
|
|
502
|
+
--template-file $(infraDir)/main.bicep \
|
|
503
|
+
--parameters $(infraDir)/environments/$(targetEnvironment).bicepparam
|
|
504
|
+
```
|
|
505
|
+
|
|
414
506
|
---
|
|
415
507
|
|
|
416
508
|
## Bootstrap Scripts
|
|
@@ -517,3 +609,58 @@ DB_URL=$(terraform output -raw database_connection_string 2>/dev/null) || true
|
|
|
517
609
|
|
|
518
610
|
echo "Outputs synced to GitHub."
|
|
519
611
|
```
|
|
612
|
+
|
|
613
|
+
### infra/sync-outputs.sh (Azure DevOps)
|
|
614
|
+
|
|
615
|
+
Reads IaC outputs and sets them as Azure DevOps variable group variables for /dock pipelines:
|
|
616
|
+
|
|
617
|
+
```bash
|
|
618
|
+
#!/bin/bash
|
|
619
|
+
# Sync Terraform outputs to Azure DevOps variable group for /dock
|
|
620
|
+
set -euo pipefail
|
|
621
|
+
|
|
622
|
+
cd "$(dirname "$0")"
|
|
623
|
+
|
|
624
|
+
PROJECT="{PROJECT}"
|
|
625
|
+
VG_NAME="${PROJECT}-infra-outputs"
|
|
626
|
+
|
|
627
|
+
echo "Syncing infrastructure outputs to Azure DevOps..."
|
|
628
|
+
|
|
629
|
+
# Resolve or create the variable group
|
|
630
|
+
VG_ID=$(az pipelines variable-group list \
|
|
631
|
+
--query "[?name=='${VG_NAME}'].id | [0]" -o tsv 2>/dev/null)
|
|
632
|
+
|
|
633
|
+
if [ -z "$VG_ID" ]; then
|
|
634
|
+
echo "Creating variable group: ${VG_NAME}"
|
|
635
|
+
VG_ID=$(az pipelines variable-group create \
|
|
636
|
+
--name "$VG_NAME" \
|
|
637
|
+
--variables placeholder=true \
|
|
638
|
+
--query "id" -o tsv)
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
# Helper: update or create a variable in the group
|
|
642
|
+
upsert_var() {
|
|
643
|
+
local name="$1" value="$2" secret="${3:-false}"
|
|
644
|
+
if az pipelines variable-group variable list --group-id "$VG_ID" \
|
|
645
|
+
--query "\"${name}\"" -o tsv &>/dev/null; then
|
|
646
|
+
az pipelines variable-group variable update \
|
|
647
|
+
--group-id "$VG_ID" --name "$name" --value "$value" --secret "$secret" --output none
|
|
648
|
+
else
|
|
649
|
+
az pipelines variable-group variable create \
|
|
650
|
+
--group-id "$VG_ID" --name "$name" --value "$value" --secret "$secret" --output none
|
|
651
|
+
fi
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
# Public values
|
|
655
|
+
REGISTRY_URL=$(terraform output -raw registry_url 2>/dev/null) || true
|
|
656
|
+
[ -n "$REGISTRY_URL" ] && upsert_var "REGISTRY_URL" "$REGISTRY_URL"
|
|
657
|
+
|
|
658
|
+
COMPUTE_ENDPOINT=$(terraform output -raw compute_endpoint 2>/dev/null) || true
|
|
659
|
+
[ -n "$COMPUTE_ENDPOINT" ] && upsert_var "COMPUTE_ENDPOINT" "$COMPUTE_ENDPOINT"
|
|
660
|
+
|
|
661
|
+
# Sensitive values
|
|
662
|
+
DB_URL=$(terraform output -raw database_connection_string 2>/dev/null) || true
|
|
663
|
+
[ -n "$DB_URL" ] && upsert_var "DATABASE_URL" "$DB_URL" true
|
|
664
|
+
|
|
665
|
+
echo "Outputs synced to Azure DevOps variable group: ${VG_NAME} (ID: ${VG_ID})"
|
|
666
|
+
```
|
|
@@ -31,5 +31,14 @@
|
|
|
31
31
|
{ "type": "semantic", "value": "Describes or generates a CI/CD pipeline that plans on pull requests and applies on merge" },
|
|
32
32
|
{ "type": "semantic", "value": "Mentions state management for Terraform such as S3 backend or remote state" }
|
|
33
33
|
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "azdo-awareness",
|
|
37
|
+
"prompt": "/keel for a Bicep project hosted in Azure DevOps",
|
|
38
|
+
"assertions": [
|
|
39
|
+
{ "type": "semantic", "value": "Detects or acknowledges Azure DevOps as the hosting platform rather than assuming GitHub" },
|
|
40
|
+
{ "type": "semantic", "value": "Mentions Azure Pipelines or Azure DevOps pipeline generation rather than GitHub Actions" },
|
|
41
|
+
{ "type": "regex", "value": "(?i)(azure devops|azdo|azure.pipelines|az pipelines)" }
|
|
42
|
+
]
|
|
34
43
|
}
|
|
35
44
|
]
|
package/skills/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-03-
|
|
2
|
+
"generated": "2026-03-12T15:57:08.604Z",
|
|
3
3
|
"count": 10,
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"name": "dock",
|
|
37
37
|
"directory": "dock",
|
|
38
38
|
"description": ">- 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.).",
|
|
39
|
-
"lines":
|
|
39
|
+
"lines": 506,
|
|
40
40
|
"hasScripts": false,
|
|
41
41
|
"hasReferences": true,
|
|
42
42
|
"hasAssets": false,
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"name": "keel",
|
|
47
47
|
"directory": "keel",
|
|
48
48
|
"description": ">- 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.",
|
|
49
|
-
"lines":
|
|
49
|
+
"lines": 638,
|
|
50
50
|
"hasScripts": false,
|
|
51
51
|
"hasReferences": true,
|
|
52
52
|
"hasAssets": false,
|