@slamb2k/mad-skills 2.0.26 → 2.0.28

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.26",
4
+ "version": "2.0.28",
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.26",
3
+ "version": "2.0.28",
4
4
  "description": "Claude Code skills collection — full lifecycle development tools",
5
5
  "type": "module",
6
6
  "repository": {
@@ -273,13 +273,24 @@ Parse VERIFY_REPORT. Present the final summary using the format in
273
273
 
274
274
  **Only if the project is a git repo** (from SCAN_REPORT `git_initialized`).
275
275
 
276
- Check if the default branch (main or master) has branch protection:
276
+ Detect the default branch and hosting platform:
277
277
 
278
278
  ```bash
279
279
  default_branch=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
280
+
281
+ REMOTE_URL=$(git remote get-url origin 2>/dev/null)
282
+ if echo "$REMOTE_URL" | grep -qiE 'dev\.azure\.com|visualstudio\.com'; then
283
+ PLATFORM="azdo"
284
+ elif echo "$REMOTE_URL" | grep -qi 'github\.com'; then
285
+ PLATFORM="github"
286
+ else
287
+ PLATFORM="unknown"
288
+ fi
280
289
  ```
281
290
 
282
- If a GitHub remote is detected (`git remote get-url origin` matches github.com):
291
+ ### GitHub
292
+
293
+ If `PLATFORM == github`:
283
294
  1. Check for existing branch protection via `gh api repos/{owner}/{repo}/branches/{default_branch}/protection` (404 = unprotected)
284
295
  2. If unprotected, ask via AskUserQuestion:
285
296
 
@@ -299,6 +310,108 @@ If a GitHub remote is detected (`git remote get-url origin` matches github.com):
299
310
  -F allow_deletions=false
300
311
  ```
301
312
 
313
+ ### Azure DevOps
314
+
315
+ If `PLATFORM == azdo`:
316
+
317
+ Extract org and project from the remote URL (same pattern as `/ship`):
318
+ ```bash
319
+ if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
320
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/\([^/]*\)/.*|\1|p')
321
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/[^/]*/\([^/]*\)/.*|\1|p')
322
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
323
+ elif echo "$REMOTE_URL" | grep -q 'vs-ssh\.visualstudio\.com'; then
324
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/\([^/]*\)/.*|\1|p')
325
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/[^/]*/\([^/]*\)/.*|\1|p')
326
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
327
+ elif echo "$REMOTE_URL" | grep -q 'visualstudio\.com'; then
328
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*//\([^.]*\)\.visualstudio\.com.*|\1|p')
329
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*/\([^/]*\)/_git/.*|\1|p')
330
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
331
+ fi
332
+ REPO_NAME=$(basename -s .git "$REMOTE_URL")
333
+ ```
334
+
335
+ If org/project extraction fails, report ⚠️ and skip branch policies.
336
+
337
+ 1. Check for existing branch policies. Use `az repos` CLI if available,
338
+ otherwise fall back to REST API:
339
+
340
+ **CLI:**
341
+ ```bash
342
+ az repos policy list \
343
+ --org "$AZDO_ORG_URL" --project "$AZDO_PROJECT" \
344
+ --repository-id "$REPO_NAME" --branch "$default_branch" \
345
+ --query "[].type.displayName" -o tsv
346
+ ```
347
+
348
+ **REST fallback:**
349
+ ```bash
350
+ AUTH="Authorization: Basic $(echo -n ":$PAT" | base64)"
351
+ # Get repository ID first
352
+ REPO_ID=$(curl -s -H "$AUTH" \
353
+ "$AZDO_ORG_URL/$AZDO_PROJECT/_apis/git/repositories/$REPO_NAME?api-version=7.0" \
354
+ | jq -r '.id')
355
+ # List branch policies
356
+ curl -s -H "$AUTH" \
357
+ "$AZDO_ORG_URL/$AZDO_PROJECT/_apis/policy/configurations?api-version=7.0" \
358
+ | jq "[.value[] | select(.settings.scope[]?.refName == \"refs/heads/$default_branch\" and .settings.scope[]?.repositoryId == \"$REPO_ID\")]"
359
+ ```
360
+
361
+ 2. If no "Minimum number of reviewers" policy exists, ask via AskUserQuestion:
362
+
363
+ Question: "Default branch `{default_branch}` has no minimum reviewer policy. Add it?"
364
+ Options:
365
+ - "Yes, require PR reviews (Recommended)" — require 1 approval, block direct push
366
+ - "Skip" — leave unprotected
367
+
368
+ 3. If user accepts, create the policy:
369
+
370
+ **CLI:**
371
+ ```bash
372
+ REPO_ID=$(az repos show --repository "$REPO_NAME" \
373
+ --org "$AZDO_ORG_URL" --project "$AZDO_PROJECT" \
374
+ --query 'id' -o tsv)
375
+
376
+ az repos policy approver-count create \
377
+ --org "$AZDO_ORG_URL" --project "$AZDO_PROJECT" \
378
+ --repository-id "$REPO_ID" --branch "$default_branch" \
379
+ --minimum-approver-count 1 \
380
+ --creator-vote-counts false \
381
+ --allow-downvotes false \
382
+ --reset-on-source-push true \
383
+ --blocking true --enabled true
384
+ ```
385
+
386
+ **REST fallback:**
387
+ ```bash
388
+ curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
389
+ "$AZDO_ORG_URL/$AZDO_PROJECT/_apis/policy/configurations?api-version=7.0" \
390
+ -d "{
391
+ \"isEnabled\": true,
392
+ \"isBlocking\": true,
393
+ \"type\": {\"id\": \"fa4e907d-c16b-4a4c-9dfa-4906e5d171dd\"},
394
+ \"settings\": {
395
+ \"minimumApproverCount\": 1,
396
+ \"creatorVoteCounts\": false,
397
+ \"allowDownvotes\": false,
398
+ \"resetOnSourcePush\": true,
399
+ \"scope\": [{
400
+ \"repositoryId\": \"$REPO_ID\",
401
+ \"refName\": \"refs/heads/$default_branch\",
402
+ \"matchKind\": \"exact\"
403
+ }]
404
+ }
405
+ }"
406
+ ```
407
+
408
+ ### Unknown Platform
409
+
410
+ If `PLATFORM == unknown`, skip branch protection and report:
411
+ ```
412
+ ⏭️ Branch protection — skipped (unrecognized remote, not GitHub or Azure DevOps)
413
+ ```
414
+
302
415
  Include result in the final report under a "🔒 Branch protection" section.
303
416
 
304
417
  ---
@@ -1,12 +1,12 @@
1
1
  {
2
- "generated": "2026-03-12T03:25:58.495Z",
2
+ "generated": "2026-03-12T13:35:25.365Z",
3
3
  "count": 10,
4
4
  "skills": [
5
5
  {
6
6
  "name": "brace",
7
7
  "directory": "brace",
8
8
  "description": "'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\".'",
9
- "lines": 328,
9
+ "lines": 441,
10
10
  "hasScripts": false,
11
11
  "hasReferences": true,
12
12
  "hasAssets": true,
@@ -66,7 +66,7 @@
66
66
  "name": "rig",
67
67
  "directory": "rig",
68
68
  "description": "'Idempotently bootstrap any repository with standard development tools, hooks, and workflows. Use when starting work on a new repo, onboarding to an existing project, or ensuring a repo has proper CI/CD setup. Configures: git hooks (lefthook), commit message templates, PR templates, and GitHub Actions for lint/format/type-check/build. Prompts for user confirmation before changes. Triggers: \"bootstrap repo\", \"setup hooks\", \"configure CI\", \"rig\", \"standardize repo\".'",
69
- "lines": 231,
69
+ "lines": 338,
70
70
  "hasScripts": false,
71
71
  "hasReferences": true,
72
72
  "hasAssets": true,
@@ -68,9 +68,31 @@ get approval, then act.
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 all dependencies in this table:
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
  |-----------|------|-------|----------|------------|--------|
@@ -78,17 +100,102 @@ Before starting, check all dependencies in this table:
78
100
  | 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 |
79
101
  | lefthook | npm | `npx lefthook --help` | yes | install | `npm install -g lefthook` |
80
102
  | gh | cli | `gh --version` | yes | url | https://cli.github.com |
103
+ | az devops | cli | `az devops -h 2>/dev/null` | no | fallback | Falls back to REST API with PAT; see AzDO tooling below |
81
104
 
82
- For each row, in order:
83
- 1. Run the Check command (for cli/npm) or test file existence (for agent/skill)
84
- 2. If found: continue silently
85
- 3. If missing: apply Resolution strategy
105
+ **Platform-conditional rules:**
106
+ - **`gh`**: Only required when `PLATFORM == github`. Skip for AzDO repos.
107
+ - **`az devops`**: Only checked when `PLATFORM == azdo`. Skip for GitHub repos.
108
+
109
+ For each applicable row, in order:
110
+ 1. Skip rows that don't apply to the detected `{PLATFORM}`
111
+ 2. Run the Check command (for cli/npm) or test file existence (for agent/skill)
112
+ 3. If found: continue silently
113
+ 4. If missing: apply Resolution strategy
86
114
  - **stop**: notify user with Detail, halt execution
87
115
  - **url**: notify user with Detail (install link), halt execution
88
116
  - **install**: notify user, run the command in Detail, continue if successful
89
117
  - **ask**: notify user, offer to run command in Detail, continue either way (or halt if required)
90
118
  - **fallback**: notify user with Detail, continue with degraded behavior
91
- 4. After all checks: summarize what's available and what's degraded
119
+ 5. After all checks: summarize what's available and what's degraded
120
+
121
+ ### AzDO Tooling Detection
122
+
123
+ When `PLATFORM == azdo`, determine which tooling is available. Set `AZDO_MODE`
124
+ for use in all subsequent phases:
125
+
126
+ ```bash
127
+ if az devops -h &>/dev/null; then
128
+ AZDO_MODE="cli"
129
+ else
130
+ AZDO_MODE="rest"
131
+ fi
132
+ ```
133
+
134
+ - **`cli`**: Use `az repos` / `az pipelines` commands (preferred)
135
+ - **`rest`**: Use Azure DevOps REST API via `curl`. Requires a PAT (personal
136
+ access token) in `AZURE_DEVOPS_EXT_PAT` or `AZDO_PAT` env var. If no PAT
137
+ is found, prompt the user to either install the CLI or set the env var.
138
+
139
+ Report in pre-flight:
140
+ - ✅ `az devops cli` — version found
141
+ - ⚠️ `az devops cli` — not found → using REST API fallback
142
+ - ❌ `az devops cli` — not found, no PAT configured → halt with setup instructions
143
+
144
+ ### AzDO Configuration Validation
145
+
146
+ When `PLATFORM == azdo`, extract organization and project from the remote URL
147
+ and validate they are usable. These values are needed by every `az repos` /
148
+ `az pipelines` command and every REST API call.
149
+
150
+ ```bash
151
+ # Extract org and project from remote URL patterns:
152
+ # https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
153
+ # https://{ORG}@dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
154
+ # {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}
155
+
156
+ REMOTE_URL=$(git remote get-url origin 2>/dev/null)
157
+
158
+ if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
159
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/\([^/]*\)/.*|\1|p')
160
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/[^/]*/\([^/]*\)/.*|\1|p')
161
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
162
+ elif echo "$REMOTE_URL" | grep -q 'vs-ssh\.visualstudio\.com'; then
163
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/\([^/]*\)/.*|\1|p')
164
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*vs-ssh\.visualstudio\.com:v3/[^/]*/\([^/]*\)/.*|\1|p')
165
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
166
+ elif echo "$REMOTE_URL" | grep -q 'visualstudio\.com'; then
167
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*//\([^.]*\)\.visualstudio\.com.*|\1|p')
168
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*/\([^/]*\)/_git/.*|\1|p')
169
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
170
+ fi
171
+
172
+ if [ -z "$AZDO_ORG" ] || [ -z "$AZDO_PROJECT" ]; then
173
+ echo "❌ Could not extract organization/project from remote URL"
174
+ echo " Remote: $REMOTE_URL"
175
+ echo ""
176
+ echo "Ensure the remote URL follows one of these formats:"
177
+ echo " https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}"
178
+ echo " https://{ORG}.visualstudio.com/{PROJECT}/_git/{REPO}"
179
+ echo " {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}"
180
+ # HALT — cannot proceed without org/project context
181
+ fi
182
+ ```
183
+
184
+ When `AZDO_MODE == cli`, also configure the defaults so commands work correctly:
185
+ ```bash
186
+ az devops configure --defaults organization="$AZDO_ORG_URL" project="$AZDO_PROJECT"
187
+ ```
188
+
189
+ When `AZDO_MODE == rest`, store these for API calls:
190
+ - Base URL: `$AZDO_ORG_URL/$AZDO_PROJECT/_apis`
191
+ - Auth header: `Authorization: Basic $(echo -n ":$PAT" | base64)`
192
+
193
+ Report in pre-flight:
194
+ - ✅ `azdo context` — org: `{AZDO_ORG}`, project: `{AZDO_PROJECT}`
195
+ - ❌ `azdo context` — could not parse from remote URL → halt with instructions
196
+
197
+ Pass `{AZDO_MODE}`, `{AZDO_ORG}`, `{AZDO_PROJECT}`, `{AZDO_ORG_URL}` into
198
+ all phase prompts alongside `{PLATFORM}`.
92
199
 
93
200
  ---
94
201
 
@@ -1,3 +1,11 @@
1
+ # Azure Pipelines CI template for Node.js projects
2
+ #
3
+ # NOTE: Azure Pipelines does not have a built-in concurrency control equivalent
4
+ # to GitHub Actions' `concurrency` key. To cancel redundant runs on the same
5
+ # branch/PR, configure "Run number" or use the Azure DevOps UI/REST API to
6
+ # cancel superseded builds. For PR triggers, enable "Cancel running CI builds
7
+ # for a push" in the pipeline settings.
8
+
1
9
  trigger:
2
10
  branches:
3
11
  include:
@@ -22,8 +30,18 @@ stages:
22
30
  versionSpec: '20.x'
23
31
  displayName: 'Setup Node.js'
24
32
 
33
+ - task: Cache@2
34
+ inputs:
35
+ key: 'npm | "$(Agent.OS)" | package-lock.json'
36
+ restoreKeys: |
37
+ npm | "$(Agent.OS)"
38
+ path: '$(Pipeline.Workspace)/.npm'
39
+ displayName: 'Cache npm packages'
40
+
25
41
  - script: npm ci
26
42
  displayName: 'Install dependencies'
43
+ env:
44
+ npm_config_cache: '$(Pipeline.Workspace)/.npm'
27
45
 
28
46
  - script: npm run lint --if-present
29
47
  displayName: 'Run linter'
@@ -36,8 +54,18 @@ stages:
36
54
  versionSpec: '20.x'
37
55
  displayName: 'Setup Node.js'
38
56
 
57
+ - task: Cache@2
58
+ inputs:
59
+ key: 'npm | "$(Agent.OS)" | package-lock.json'
60
+ restoreKeys: |
61
+ npm | "$(Agent.OS)"
62
+ path: '$(Pipeline.Workspace)/.npm'
63
+ displayName: 'Cache npm packages'
64
+
39
65
  - script: npm ci
40
66
  displayName: 'Install dependencies'
67
+ env:
68
+ npm_config_cache: '$(Pipeline.Workspace)/.npm'
41
69
 
42
70
  - script: npm run format:check --if-present || npx prettier --check .
43
71
  displayName: 'Check formatting'
@@ -50,8 +78,18 @@ stages:
50
78
  versionSpec: '20.x'
51
79
  displayName: 'Setup Node.js'
52
80
 
81
+ - task: Cache@2
82
+ inputs:
83
+ key: 'npm | "$(Agent.OS)" | package-lock.json'
84
+ restoreKeys: |
85
+ npm | "$(Agent.OS)"
86
+ path: '$(Pipeline.Workspace)/.npm'
87
+ displayName: 'Cache npm packages'
88
+
53
89
  - script: npm ci
54
90
  displayName: 'Install dependencies'
91
+ env:
92
+ npm_config_cache: '$(Pipeline.Workspace)/.npm'
55
93
 
56
94
  - script: npm run typecheck --if-present || npm run type-check --if-present || npx tsc --noEmit
57
95
  displayName: 'Run type check'
@@ -67,8 +105,18 @@ stages:
67
105
  versionSpec: '20.x'
68
106
  displayName: 'Setup Node.js'
69
107
 
108
+ - task: Cache@2
109
+ inputs:
110
+ key: 'npm | "$(Agent.OS)" | package-lock.json'
111
+ restoreKeys: |
112
+ npm | "$(Agent.OS)"
113
+ path: '$(Pipeline.Workspace)/.npm'
114
+ displayName: 'Cache npm packages'
115
+
70
116
  - script: npm ci
71
117
  displayName: 'Install dependencies'
118
+ env:
119
+ npm_config_cache: '$(Pipeline.Workspace)/.npm'
72
120
 
73
121
  - script: npm run build
74
122
  displayName: 'Build'
@@ -84,8 +132,18 @@ stages:
84
132
  versionSpec: '20.x'
85
133
  displayName: 'Setup Node.js'
86
134
 
135
+ - task: Cache@2
136
+ inputs:
137
+ key: 'npm | "$(Agent.OS)" | package-lock.json'
138
+ restoreKeys: |
139
+ npm | "$(Agent.OS)"
140
+ path: '$(Pipeline.Workspace)/.npm'
141
+ displayName: 'Cache npm packages'
142
+
87
143
  - script: npm ci
88
144
  displayName: 'Install dependencies'
145
+ env:
146
+ npm_config_cache: '$(Pipeline.Workspace)/.npm'
89
147
 
90
148
  - script: npm test --if-present
91
149
  displayName: 'Run tests'
@@ -25,5 +25,14 @@
25
25
  { "type": "contains", "value": "██" },
26
26
  { "type": "semantic", "value": "Mentions that the process is idempotent (safe to run multiple times) and will prompt the user for confirmation before making changes" }
27
27
  ]
28
+ },
29
+ {
30
+ "name": "azdo-awareness",
31
+ "prompt": "Bootstrap CI for my Azure DevOps repository at https://dev.azure.com/myorg/myproject/_git/myrepo",
32
+ "assertions": [
33
+ { "type": "contains", "value": "██" },
34
+ { "type": "regex", "value": "(Azure Pipelines|Azure DevOps|azdo)", "flags": "i" },
35
+ { "type": "semantic", "value": "Recognizes the repository is hosted on Azure DevOps and mentions Azure Pipelines as the CI platform instead of or alongside GitHub Actions" }
36
+ ]
28
37
  }
29
38
  ]