@slamb2k/mad-skills 2.0.28 → 2.0.30

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mad-skills",
3
3
  "description": "AI-assisted planning, development and governance tools",
4
- "version": "2.0.28",
4
+ "version": "2.0.30",
5
5
  "author": {
6
6
  "name": "slamb2k",
7
7
  "url": "https://github.com/slamb2k"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slamb2k/mad-skills",
3
- "version": "2.0.28",
3
+ "version": "2.0.30",
4
4
  "description": "Claude Code skills collection — full lifecycle development tools",
5
5
  "type": "module",
6
6
  "repository": {
@@ -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,26 @@ 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 |
93
-
94
- For each row:
95
- 1. Run the Check command
96
- 2. If found: continue silently
97
- 3. If missing: apply Resolution strategy
98
- 4. After all checks: summarize availability
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
+ When `PLATFORM == azdo`, follow `references/azdo-platform.md` for tooling
132
+ detection (`AZDO_MODE`) and configuration validation (`AZDO_ORG`, `AZDO_PROJECT`).
133
+ Pass these variables into all phase prompts alongside `{PLATFORM}`.
99
134
 
100
135
  ---
101
136
 
@@ -206,8 +241,10 @@ Optional — ask only if deploying to platforms that need this:
206
241
  - **Blue-green**: maintain two environments, swap traffic
207
242
  - **Canary**: gradual traffic shift with automatic rollback on error thresholds
208
243
 
209
- **If `--skip-interview` flag**: Use detected defaults + sensible defaults for
210
- everything else (ghcr.io, 3-stage, GitHub Secrets, simple rollback).
244
+ **If `--skip-interview` flag**: Use detected defaults + sensible platform-aware
245
+ defaults for everything else:
246
+ - **GitHub** (`PLATFORM == github`): ghcr.io, 3-stage, GitHub Secrets, simple rollback
247
+ - **Azure DevOps** (`PLATFORM == azdo`): Azure Container Registry, 3-stage, Azure Key Vault, simple rollback
211
248
 
212
249
  Compile all answers into a DOCK_CONFIG object for Phase 3.
213
250
 
@@ -0,0 +1,83 @@
1
+ # Azure DevOps Platform Support
2
+
3
+ Shared procedures for AzDO tooling detection and configuration validation.
4
+ Referenced by SKILL.md during pre-flight when `PLATFORM == azdo`.
5
+
6
+ ## AzDO Tooling Detection
7
+
8
+ When `PLATFORM == azdo`, determine which tooling is available. Set `AZDO_MODE`
9
+ for use in all subsequent phases:
10
+
11
+ ```bash
12
+ if az devops -h &>/dev/null; then
13
+ AZDO_MODE="cli"
14
+ else
15
+ AZDO_MODE="rest"
16
+ fi
17
+ ```
18
+
19
+ - **`cli`**: Use `az repos` / `az pipelines` commands (preferred)
20
+ - **`rest`**: Use Azure DevOps REST API via `curl`. Requires a PAT (personal
21
+ access token) in `AZURE_DEVOPS_EXT_PAT` or `AZDO_PAT` env var. If no PAT
22
+ is found, prompt the user to either install the CLI or set the env var.
23
+
24
+ Report in pre-flight:
25
+ - ✅ `az devops cli` — version found
26
+ - ⚠️ `az devops cli` — not found → using REST API fallback
27
+ - ❌ `az devops cli` — not found, no PAT configured → halt with setup instructions
28
+
29
+ ## AzDO Configuration Validation
30
+
31
+ When `PLATFORM == azdo`, extract organization and project from the remote URL
32
+ and validate they are usable. These values are needed by every `az repos` /
33
+ `az pipelines` command and every REST API call.
34
+
35
+ ```bash
36
+ # Extract org and project from remote URL patterns:
37
+ # https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
38
+ # https://{ORG}@dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
39
+ # {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}
40
+
41
+ REMOTE_URL=$(git remote get-url origin 2>/dev/null)
42
+
43
+ if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
44
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/\([^/]*\)/.*|\1|p')
45
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/[^/]*/\([^/]*\)/.*|\1|p')
46
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
47
+ elif echo "$REMOTE_URL" | grep -q 'vs-ssh\.visualstudio\.com'; then
48
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/\([^/]*\)/.*|\1|p')
49
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/[^/]*/\([^/]*\)/.*|\1|p')
50
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
51
+ elif echo "$REMOTE_URL" | grep -q 'visualstudio\.com'; then
52
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*//\([^.]*\)\.visualstudio\.com.*|\1|p')
53
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*/\([^/]*\)/_git/.*|\1|p')
54
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
55
+ fi
56
+
57
+ if [ -z "$AZDO_ORG" ] || [ -z "$AZDO_PROJECT" ]; then
58
+ echo "❌ Could not extract organization/project from remote URL"
59
+ echo " Remote: $REMOTE_URL"
60
+ echo ""
61
+ echo "Ensure the remote URL follows one of these formats:"
62
+ echo " https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}"
63
+ echo " https://{ORG}.visualstudio.com/{PROJECT}/_git/{REPO}"
64
+ echo " {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}"
65
+ # HALT — cannot proceed without org/project context
66
+ fi
67
+ ```
68
+
69
+ When `AZDO_MODE == cli`, also configure the defaults so commands work correctly:
70
+ ```bash
71
+ az devops configure --defaults organization="$AZDO_ORG_URL" project="$AZDO_PROJECT"
72
+ ```
73
+
74
+ When `AZDO_MODE == rest`, store these for API calls:
75
+ - Base URL: `$AZDO_ORG_URL/$AZDO_PROJECT/_apis`
76
+ - Auth header: `Authorization: Basic $(echo -n ":$PAT" | base64)`
77
+
78
+ Report in pre-flight:
79
+ - ✅ `azdo context` — org: `{AZDO_ORG}`, project: `{AZDO_PROJECT}`
80
+ - ❌ `azdo context` — could not parse from remote URL → halt with instructions
81
+
82
+ Pass `{AZDO_MODE}`, `{AZDO_ORG}`, `{AZDO_PROJECT}`, `{AZDO_ORG_URL}` into
83
+ all phase prompts alongside `{PLATFORM}`.
@@ -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 → suggest ghcr.io (most universal)
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
  ]
@@ -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,32 @@ 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
+ When `PLATFORM == azdo`, follow `references/azdo-platform.md` for tooling
128
+ detection (`AZDO_MODE`) and configuration validation (`AZDO_ORG`, `AZDO_PROJECT`).
129
+ Pass these variables into all phase prompts alongside `{PLATFORM}`.
130
+
87
131
  ---
88
132
 
89
133
  ## Phase 0: Sync
@@ -128,160 +172,16 @@ Read the full interview flow from `references/interview-guide.md#interview-quest
128
172
  The interview covers these topics in order. Skip questions where detection provided
129
173
  high-confidence answers (but confirm with the user).
130
174
 
131
- ### 2.1 Cloud Provider
132
-
133
- ```
134
- Which cloud provider(s) will host your infrastructure?
135
-
136
- Options:
137
- 1. Azure (Recommended if Azure signals detected)
138
- 2. AWS
139
- 3. Google Cloud
140
- 4. Multi-cloud
141
- 5. Self-hosted / VPS (Dokku, Coolify, CapRover)
142
- ```
143
-
144
- If /dock has already run, infer from the deployment targets chosen there.
145
-
146
- ### 2.2 — IaC Tool
147
-
148
- Suggest based on cloud provider:
149
- - Azure → Bicep (native) or Terraform (universal)
150
- - AWS → Terraform or CDK
151
- - GCP → Terraform or Pulumi
152
- - Multi-cloud → Terraform
153
- - Self-hosted → Terraform (for VPS provisioning) or skip IaC
154
-
155
- ```
156
- Which Infrastructure as Code tool do you want to use?
157
-
158
- Options:
159
- 1. Terraform (Recommended — universal, multi-cloud)
160
- 2. Bicep (Azure-native, simpler syntax)
161
- 3. Pulumi (code-first, TypeScript/Python/Go)
162
- 4. AWS CDK (AWS-native, TypeScript/Python)
163
- ```
164
-
165
- ### 2.3 — Infrastructure Components
166
-
167
- Present a checklist based on detected needs. The goal is to provision everything
168
- /dock needs to deploy to, plus supporting services.
169
-
170
- ```
171
- Which infrastructure components do you need?
172
-
173
- Container platform:
174
- [x] Container registry (required for /dock)
175
- [ ] Kubernetes cluster (AKS/EKS/GKE)
176
- [ ] Serverless containers (Container Apps/Fargate/Cloud Run)
177
- [ ] Self-hosted PaaS (Dokku/Coolify/CapRover on VPS)
178
-
179
- Data:
180
- [ ] Managed database (PostgreSQL, MySQL, SQL Server, etc.)
181
- [ ] Cache (Redis, Memcached)
182
- [ ] Object storage (S3, Blob Storage, GCS)
183
- [ ] Message queue (SQS, Service Bus, Pub/Sub)
184
-
185
- Networking:
186
- [ ] DNS zone
187
- [ ] CDN / Front Door
188
- [ ] Virtual network / VPC
189
- [ ] Load balancer (if not using platform-managed)
190
- [ ] API Gateway
191
-
192
- Security:
193
- [ ] Key Vault / Secrets Manager
194
- [ ] Managed identity / IAM roles
195
- [ ] SSL/TLS certificates
196
-
197
- Monitoring:
198
- [ ] Log aggregation (Application Insights, CloudWatch, Cloud Logging)
199
- [ ] Metrics / dashboards
200
- [ ] Alerts
201
- ```
202
-
203
- Auto-check components if /dock detection or the codebase signals them:
204
- - Dockerfile found → container registry checked
205
- - Database migrations found → managed database checked
206
- - Redis client in deps → cache checked
207
- - S3/Blob SDK in deps → object storage checked
208
-
209
- ### 2.4 — Environment Topology
210
-
211
- Align with /dock's environments if it has run. Otherwise ask:
212
-
213
- ```
214
- How many environments do you need?
215
-
216
- Options:
217
- 1. Simple: dev + prod (Recommended for starting out)
218
- 2. Standard: dev + staging + prod
219
- 3. Custom: define your own
220
- ```
221
-
222
- Each environment gets its own IaC state and variable set. Resource sizing
223
- scales with the environment tier (dev=small, staging=medium, prod=large).
175
+ Topics covered (full prompts and options in `references/interview-guide.md`):
224
176
 
225
- ### 2.5State Management
226
-
227
- Where to store IaC state (critical for team collaboration):
228
-
229
- **Terraform:**
230
- ```
231
- Where should Terraform state be stored?
232
-
233
- Options:
234
- 1. Azure Blob Storage (Recommended for Azure)
235
- 2. AWS S3 + DynamoDB (Recommended for AWS)
236
- 3. Google Cloud Storage (Recommended for GCP)
237
- 4. Terraform Cloud
238
- 5. Local (not recommended for teams)
239
- ```
240
-
241
- **Bicep:** No state management needed (Azure Resource Manager handles it).
242
-
243
- **Pulumi:** Pulumi Cloud (default) or self-managed backend.
244
-
245
- **CDK:** CloudFormation handles state.
246
-
247
- ### 2.6 — CI/CD for Infrastructure
248
-
249
- ```
250
- How should infrastructure changes be applied?
251
-
252
- Options:
253
- 1. Plan on PR, apply on merge (Recommended)
254
- 2. Plan on PR, manual apply via approval
255
- 3. Plan and apply on merge (no PR preview)
256
- ```
257
-
258
- All options generate a pipeline. Option 1 is the default and safest for teams.
259
-
260
- ### 2.7 — Naming Convention
261
-
262
- ```
263
- What naming convention should resources use?
264
-
265
- Options:
266
- 1. Azure CAF style: {resource-type}-{project}-{env}-{region} (Recommended)
267
- 2. Simple: {project}-{resource}-{env}
268
- 3. Custom prefix: {user-defined}
269
- ```
270
-
271
- ### 2.8 — Integration with /dock
272
-
273
- If /dock artifacts exist, ask:
274
- ```
275
- I found /dock deployment config. Should /keel provision the infrastructure
276
- those pipelines deploy to?
277
-
278
- Detected targets:
279
- dev: {platform}
280
- staging: {platform}
281
- prod: {platform}
282
-
283
- I'll provision: container registry, {platform}-specific compute, networking.
284
- ```
177
+ 1. **Cloud Provider** Azure, AWS, GCP, Multi-cloud, Self-hosted. Infer from /dock if it has run.
178
+ 2. **IaC Tool** — Suggest by cloud: Azure→Bicep/Terraform, AWS→Terraform/CDK, GCP→Terraform/Pulumi, Multi-cloud→Terraform.
179
+ 3. **Infrastructure Components** Checklist: container platform, data (DB, cache, storage, queues), networking, security, monitoring. Auto-check from codebase signals (Dockerfile→registry, migrations→DB, Redis client→cache).
180
+ 4. **Environment Topology** — Simple (dev+prod), Standard (dev+staging+prod), or Custom. Align with /dock if it has run.
181
+ 5. **State Management** — Terraform: cloud storage per provider. Bicep: ARM handles it. Pulumi: Pulumi Cloud. CDK: CloudFormation.
182
+ 6. **CI/CD Strategy** — Plan on PR + apply on merge (recommended), manual apply, or auto-apply.
183
+ 7. **Naming Convention** CAF style, simple, or custom prefix.
184
+ 8. **Integration with /dock** — If /dock artifacts exist, offer to provision their deployment targets.
285
185
 
286
186
  **If `--skip-interview`**: Use detected defaults + sensible defaults.
287
187
 
@@ -360,6 +260,13 @@ Use the provider-specific template from `references/iac-pipeline-templates.md#bo
360
260
 
361
261
  Read the appropriate template from `references/iac-pipeline-templates.md`.
362
262
 
263
+ **Platform branching:**
264
+ - When `PLATFORM == github`: Generate a GitHub Actions workflow. Output file:
265
+ `.github/workflows/infra.yml`. Use templates from the "GitHub Actions" section.
266
+ - When `PLATFORM == azdo`: Generate an Azure DevOps Pipelines YAML file. Output
267
+ file: `azure-pipelines-infra.yml`. Use templates from the "Azure DevOps
268
+ Pipelines" section.
269
+
363
270
  Generate a workflow that implements:
364
271
 
365
272
  **On pull request:**
@@ -408,8 +315,16 @@ for pushing images, the compute endpoint for deploying containers, etc.
408
315
  ### 3.5 — Environment Variables Bridge
409
316
 
410
317
  Generate a script or workflow step that reads IaC outputs and writes them as
411
- CI/CD secrets/variables for /dock's pipelines:
318
+ CI/CD secrets/variables for /dock's pipelines.
412
319
 
320
+ **Platform branching:**
321
+ - When `PLATFORM == github`: Use `gh secret set` / `gh variable set` commands.
322
+ Reference `references/iac-pipeline-templates.md#infra/sync-outputs.sh`.
323
+ - When `PLATFORM == azdo`: Use `az pipelines variable-group variable update` /
324
+ `az pipelines variable-group variable create` commands. Reference
325
+ `references/iac-pipeline-templates.md#infra/sync-outputs.sh (Azure DevOps)`.
326
+
327
+ **GitHub example:**
413
328
  ```bash
414
329
  # infra/sync-outputs.sh
415
330
  REGISTRY_URL=$(terraform output -raw registry_url)
@@ -417,6 +332,14 @@ gh secret set REGISTRY_URL --body "$REGISTRY_URL"
417
332
  gh secret set DATABASE_URL --body "$(terraform output -raw database_connection_string)"
418
333
  ```
419
334
 
335
+ **Azure DevOps example:**
336
+ ```bash
337
+ # infra/sync-outputs.sh
338
+ REGISTRY_URL=$(terraform output -raw registry_url)
339
+ az pipelines variable-group variable update --group-id "$VG_ID" --name REGISTRY_URL --value "$REGISTRY_URL"
340
+ az pipelines variable-group variable update --group-id "$VG_ID" --name DATABASE_URL --value "$(terraform output -raw database_connection_string)" --secret true
341
+ ```
342
+
420
343
  ---
421
344
 
422
345
  ## Phase 4: Verify
@@ -0,0 +1,83 @@
1
+ # Azure DevOps Platform Support
2
+
3
+ Shared procedures for AzDO tooling detection and configuration validation.
4
+ Referenced by SKILL.md during pre-flight when `PLATFORM == azdo`.
5
+
6
+ ## AzDO Tooling Detection
7
+
8
+ When `PLATFORM == azdo`, determine which tooling is available. Set `AZDO_MODE`
9
+ for use in all subsequent phases:
10
+
11
+ ```bash
12
+ if az devops -h &>/dev/null; then
13
+ AZDO_MODE="cli"
14
+ else
15
+ AZDO_MODE="rest"
16
+ fi
17
+ ```
18
+
19
+ - **`cli`**: Use `az repos` / `az pipelines` commands (preferred)
20
+ - **`rest`**: Use Azure DevOps REST API via `curl`. Requires a PAT (personal
21
+ access token) in `AZURE_DEVOPS_EXT_PAT` or `AZDO_PAT` env var. If no PAT
22
+ is found, prompt the user to either install the CLI or set the env var.
23
+
24
+ Report in pre-flight:
25
+ - ✅ `az devops cli` — version found
26
+ - ⚠️ `az devops cli` — not found → using REST API fallback
27
+ - ❌ `az devops cli` — not found, no PAT configured → halt with setup instructions
28
+
29
+ ## AzDO Configuration Validation
30
+
31
+ When `PLATFORM == azdo`, extract organization and project from the remote URL
32
+ and validate they are usable. These values are needed by every `az repos` /
33
+ `az pipelines` command and every REST API call.
34
+
35
+ ```bash
36
+ # Extract org and project from remote URL patterns:
37
+ # https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
38
+ # https://{ORG}@dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
39
+ # {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}
40
+
41
+ REMOTE_URL=$(git remote get-url origin 2>/dev/null)
42
+
43
+ if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
44
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/\([^/]*\)/.*|\1|p')
45
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/[^/]*/\([^/]*\)/.*|\1|p')
46
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
47
+ elif echo "$REMOTE_URL" | grep -q 'vs-ssh\.visualstudio\.com'; then
48
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/\([^/]*\)/.*|\1|p')
49
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/[^/]*/\([^/]*\)/.*|\1|p')
50
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
51
+ elif echo "$REMOTE_URL" | grep -q 'visualstudio\.com'; then
52
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*//\([^.]*\)\.visualstudio\.com.*|\1|p')
53
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*/\([^/]*\)/_git/.*|\1|p')
54
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
55
+ fi
56
+
57
+ if [ -z "$AZDO_ORG" ] || [ -z "$AZDO_PROJECT" ]; then
58
+ echo "❌ Could not extract organization/project from remote URL"
59
+ echo " Remote: $REMOTE_URL"
60
+ echo ""
61
+ echo "Ensure the remote URL follows one of these formats:"
62
+ echo " https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}"
63
+ echo " https://{ORG}.visualstudio.com/{PROJECT}/_git/{REPO}"
64
+ echo " {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}"
65
+ # HALT — cannot proceed without org/project context
66
+ fi
67
+ ```
68
+
69
+ When `AZDO_MODE == cli`, also configure the defaults so commands work correctly:
70
+ ```bash
71
+ az devops configure --defaults organization="$AZDO_ORG_URL" project="$AZDO_PROJECT"
72
+ ```
73
+
74
+ When `AZDO_MODE == rest`, store these for API calls:
75
+ - Base URL: `$AZDO_ORG_URL/$AZDO_PROJECT/_apis`
76
+ - Auth header: `Authorization: Basic $(echo -n ":$PAT" | base64)`
77
+
78
+ Report in pre-flight:
79
+ - ✅ `azdo context` — org: `{AZDO_ORG}`, project: `{AZDO_PROJECT}`
80
+ - ❌ `azdo context` — could not parse from remote URL → halt with instructions
81
+
82
+ Pass `{AZDO_MODE}`, `{AZDO_ORG}`, `{AZDO_PROJECT}`, `{AZDO_ORG_URL}` into
83
+ all phase prompts alongside `{PLATFORM}`.
@@ -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
  ]
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated": "2026-03-12T13:35:25.365Z",
2
+ "generated": "2026-03-12T17:16:55.117Z",
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": 394,
39
+ "lines": 431,
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": 496,
49
+ "lines": 419,
50
50
  "hasScripts": false,
51
51
  "hasReferences": true,
52
52
  "hasAssets": false,