@slamb2k/mad-skills 2.0.25 → 2.0.26

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.25",
4
+ "version": "2.0.26",
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.25",
3
+ "version": "2.0.26",
4
4
  "description": "Claude Code skills collection — full lifecycle development tools",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated": "2026-03-11T04:39:55.849Z",
2
+ "generated": "2026-03-12T03:25:58.495Z",
3
3
  "count": 10,
4
4
  "skills": [
5
5
  {
@@ -76,7 +76,7 @@
76
76
  "name": "ship",
77
77
  "directory": "ship",
78
78
  "description": "\"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.\"",
79
- "lines": 262,
79
+ "lines": 356,
80
80
  "hasScripts": false,
81
81
  "hasReferences": true,
82
82
  "hasAssets": false,
@@ -85,8 +85,8 @@
85
85
  {
86
86
  "name": "speccy",
87
87
  "directory": "speccy",
88
- "description": "Deep-dive interview skill for creating comprehensive specifications. Reviews existing code and docs, then interviews the user through multiple rounds of targeted questions covering technical implementation, UI/UX, concerns, and tradeoffs. Produces a structured spec via create-specification. Use when starting a new feature, system, or major change that needs a spec.",
89
- "lines": 245,
88
+ "description": "Deep-dive interview skill for creating comprehensive specifications. Reviews existing code and docs, then interviews the user through multiple rounds of targeted questions covering technical implementation, UI/UX, concerns, and tradeoffs. Produces a structured spec in specs/. Use when starting a new feature, system, or major change that needs a spec.",
89
+ "lines": 241,
90
90
  "hasScripts": false,
91
91
  "hasReferences": true,
92
92
  "hasAssets": false,
@@ -72,51 +72,140 @@ Parse optional flags from the request:
72
72
 
73
73
  ---
74
74
 
75
+ ## Platform Detection
76
+
77
+ Detect the hosting platform **before** pre-flight so dependency checks are
78
+ platform-specific:
79
+
80
+ ```bash
81
+ REMOTE_URL=$(git remote get-url origin 2>/dev/null)
82
+ if echo "$REMOTE_URL" | grep -qiE 'dev\.azure\.com|visualstudio\.com'; then
83
+ PLATFORM="azdo"
84
+ elif echo "$REMOTE_URL" | grep -qi 'github\.com'; then
85
+ PLATFORM="github"
86
+ else
87
+ PLATFORM="github" # default fallback
88
+ fi
89
+ ```
90
+
91
+ Pass `{PLATFORM}` into all stage prompts. Each stage uses the appropriate
92
+ CLI tool: `gh` for GitHub, `az repos`/`az pipelines` for Azure DevOps.
93
+
94
+ ---
95
+
75
96
  ## Pre-flight
76
97
 
77
- Before starting, check all dependencies in this table:
98
+ Before starting, check all dependencies in this table. The table contains
99
+ **all** dependencies — some are platform-conditional (see notes after table).
78
100
 
79
101
  | Dependency | Type | Check | Required | Resolution | Detail |
80
102
  |-----------|------|-------|----------|------------|--------|
81
103
  | git | cli | `git --version` | yes | stop | Install from https://git-scm.com |
82
104
  | gh | cli | `gh --version` | yes | url | https://cli.github.com |
83
- | ship-analyzer | agent | `~/.claude/agents/ship-analyzer.md` | no | fallback | Uses general-purpose agent |
84
-
85
- For each row, in order:
86
- 1. Run the Check command (for cli/npm) or test file existence (for agent/skill)
87
- 2. If found: continue silently
88
- 3. If missing: apply Resolution strategy
105
+ | az devops | cli | `az devops -h 2>/dev/null` | no | fallback | Falls back to REST API with PAT; see AzDO tooling below |
106
+ | ship-analyzer | agent | `~/.claude/agents/ship-analyzer.md` or `~/.claude/plugins/marketplaces/slamb2k/agents/ship-analyzer.md` | no | fallback | Uses general-purpose agent |
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.
111
+
112
+ For each applicable row, in order:
113
+ 1. Skip rows that don't apply to the detected `{PLATFORM}`
114
+ 2. Run the Check command (for cli/npm) or test file existence (for agent/skill)
115
+ 3. If found: continue silently
116
+ 4. If missing: apply Resolution strategy
89
117
  - **stop**: notify user with Detail, halt execution
90
118
  - **url**: notify user with Detail (install link), halt execution
91
119
  - **install**: notify user, run the command in Detail, continue if successful
92
120
  - **ask**: notify user, offer to run command in Detail, continue either way (or halt if required)
93
121
  - **fallback**: notify user with Detail, continue with degraded behavior
94
- 4. After all checks: summarize what's available and what's degraded
122
+ 5. After all checks: summarize what's available and what's degraded
95
123
 
96
- Read `default_branch` and `remote` from Stage 1's SYNC_REPORT. These are
97
- substituted into all stage prompts as `{REMOTE}` and `{DEFAULT_BRANCH}`.
98
-
99
- ### Platform Detection
124
+ ### AzDO Tooling Detection
100
125
 
101
- After sync, detect the hosting platform from the remote URL:
126
+ When `PLATFORM == azdo`, determine which tooling is available. Set `AZDO_MODE`
127
+ for use in all subsequent stages:
102
128
 
103
129
  ```bash
104
- REMOTE_URL=$(git remote get-url {REMOTE} 2>/dev/null)
105
- if echo "$REMOTE_URL" | grep -qiE 'dev\.azure\.com|visualstudio\.com'; then
106
- PLATFORM="azdo"
107
- elif echo "$REMOTE_URL" | grep -qi 'github\.com'; then
108
- PLATFORM="github"
130
+ if az devops -h &>/dev/null; then
131
+ AZDO_MODE="cli"
109
132
  else
110
- PLATFORM="github" # default fallback
133
+ AZDO_MODE="rest"
111
134
  fi
112
135
  ```
113
136
 
114
- Pass `{PLATFORM}` into all stage prompts. Each stage uses the appropriate
115
- CLI tool: `gh` for GitHub, `az repos`/`az pipelines` for Azure DevOps.
137
+ - **`cli`**: Use `az repos` / `az pipelines` commands (preferred)
138
+ - **`rest`**: Use Azure DevOps REST API via `curl`. Requires a PAT (personal
139
+ access token) in `AZURE_DEVOPS_EXT_PAT` or `AZDO_PAT` env var. If no PAT
140
+ is found, prompt the user to either install the CLI or set the env var.
141
+
142
+ Report in pre-flight:
143
+ - ✅ `az devops cli` — version found
144
+ - ⚠️ `az devops cli` — not found → using REST API fallback
145
+ - ❌ `az devops cli` — not found, no PAT configured → halt with setup instructions
146
+
147
+ ### AzDO Configuration Validation
148
+
149
+ When `PLATFORM == azdo`, extract organization and project from the remote URL
150
+ and validate they are usable. These values are needed by every `az repos` /
151
+ `az pipelines` command and every REST API call.
152
+
153
+ ```bash
154
+ # Extract org and project from remote URL patterns:
155
+ # https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
156
+ # https://{ORG}@dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
157
+ # {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}
158
+
159
+ REMOTE_URL=$(git remote get-url origin 2>/dev/null)
160
+
161
+ if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
162
+ # HTTPS format: https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
163
+ # Also handles: https://{ORG}@dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}
164
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/\([^/]*\)/.*|\1|p')
165
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*dev\.azure\.com/[^/]*/\([^/]*\)/.*|\1|p')
166
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
167
+ elif echo "$REMOTE_URL" | grep -q 'vs-ssh\.visualstudio\.com'; then
168
+ # SSH format: {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}
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
+ # Legacy HTTPS format: https://{ORG}.visualstudio.com/{PROJECT}/_git/{REPO}
174
+ AZDO_ORG=$(echo "$REMOTE_URL" | sed -n 's|.*//\([^.]*\)\.visualstudio\.com.*|\1|p')
175
+ AZDO_PROJECT=$(echo "$REMOTE_URL" | sed -n 's|.*/\([^/]*\)/_git/.*|\1|p')
176
+ AZDO_ORG_URL="https://dev.azure.com/$AZDO_ORG"
177
+ fi
116
178
 
117
- > **Azure DevOps prerequisite:** The `az devops` extension must be installed
118
- > and configured (`az devops configure --defaults organization=... project=...`).
119
- > If `az repos` commands fail, report the setup requirement to the user.
179
+ if [ -z "$AZDO_ORG" ] || [ -z "$AZDO_PROJECT" ]; then
180
+ echo "❌ Could not extract organization/project from remote URL"
181
+ echo " Remote: $REMOTE_URL"
182
+ echo ""
183
+ echo "Ensure the remote URL follows one of these formats:"
184
+ echo " https://dev.azure.com/{ORG}/{PROJECT}/_git/{REPO}"
185
+ echo " https://{ORG}.visualstudio.com/{PROJECT}/_git/{REPO}"
186
+ echo " {ORG}@vs-ssh.visualstudio.com:v3/{ORG}/{PROJECT}/{REPO}"
187
+ # HALT — cannot proceed without org/project context
188
+ fi
189
+ ```
190
+
191
+ When `AZDO_MODE == cli`, also configure the defaults so commands work correctly:
192
+ ```bash
193
+ az devops configure --defaults organization="$AZDO_ORG_URL" project="$AZDO_PROJECT"
194
+ ```
195
+
196
+ When `AZDO_MODE == rest`, store these for API calls:
197
+ - Base URL: `$AZDO_ORG_URL/$AZDO_PROJECT/_apis`
198
+ - Auth header: `Authorization: Basic $(echo -n ":$PAT" | base64)`
199
+
200
+ Report in pre-flight:
201
+ - ✅ `azdo context` — org: `{AZDO_ORG}`, project: `{AZDO_PROJECT}`
202
+ - ❌ `azdo context` — could not parse from remote URL → halt with instructions
203
+
204
+ Pass `{AZDO_MODE}`, `{AZDO_ORG}`, `{AZDO_PROJECT}`, `{AZDO_ORG_URL}` into
205
+ all stage prompts alongside `{PLATFORM}`.
206
+
207
+ Read `default_branch` and `remote` from Stage 1's SYNC_REPORT. These are
208
+ substituted into all stage prompts as `{REMOTE}` and `{DEFAULT_BRANCH}`.
120
209
 
121
210
  ---
122
211
 
@@ -155,14 +244,16 @@ Task(
155
244
  > **Fallback:** If `ship-analyzer` is not available, use `subagent_type: "general-purpose"`.
156
245
 
157
246
  Substitute `{USER_INTENT}`, `{FILES_TO_INCLUDE}`, `{FILES_TO_EXCLUDE}`,
158
- `{REMOTE}`, `{DEFAULT_BRANCH}`, `{PLATFORM}` into the prompt.
247
+ `{REMOTE}`, `{DEFAULT_BRANCH}`, `{PLATFORM}`, `{AZDO_MODE}`, `{AZDO_ORG}`,
248
+ `{AZDO_PROJECT}`, `{AZDO_ORG_URL}`, `{PAT}` into the prompt.
159
249
 
160
250
  Parse SHIP_REPORT. Abort if failed.
161
251
 
162
252
  **Rollback:** If push succeeds but PR creation fails, report the error and
163
253
  suggest the manual PR creation command. Do NOT revert the push.
164
254
  - GitHub: `gh pr create --head {branch}`
165
- - Azure DevOps: `az repos pr create --source-branch {branch} --target-branch {DEFAULT_BRANCH}`
255
+ - Azure DevOps (cli): `az repos pr create --source-branch {branch} --target-branch {DEFAULT_BRANCH} --org {AZDO_ORG_URL} --project {AZDO_PROJECT}`
256
+ - Azure DevOps (rest): Create PR via `{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/git/repositories/{repo}/pullrequests?api-version=7.0`
166
257
 
167
258
  **If `--pr-only` flag: Stop here and report PR URL to user.**
168
259
 
@@ -182,7 +273,8 @@ Task(
182
273
  )
183
274
  ```
184
275
 
185
- Substitute `{PR_NUMBER}` into the prompt.
276
+ Substitute `{PR_NUMBER}`, `{BRANCH}`, `{PLATFORM}`, `{AZDO_MODE}`,
277
+ `{AZDO_ORG}`, `{AZDO_ORG_URL}`, `{AZDO_PROJECT}`, `{PAT}` into the prompt.
186
278
 
187
279
  While CI runs in the background, briefly inform the user:
188
280
  ```
@@ -205,7 +297,8 @@ Task(
205
297
  )
206
298
  ```
207
299
 
208
- Substitute `{PR_NUMBER}`, `{BRANCH}`, `{FAILING_CHECKS}` into the prompt.
300
+ Substitute `{PR_NUMBER}`, `{BRANCH}`, `{FAILING_CHECKS}`, `{PLATFORM}`,
301
+ `{AZDO_MODE}`, `{AZDO_ORG}`, `{AZDO_ORG_URL}`, `{AZDO_PROJECT}`, `{PAT}` into the prompt.
209
302
 
210
303
  If fixed, return to Stage 3 (run CI watch again).
211
304
  If unable to fix after 2 attempts, report to user and stop.
@@ -225,7 +318,8 @@ Task(
225
318
  )
226
319
  ```
227
320
 
228
- Substitute `{PR_NUMBER}`, `{REMOTE}`, `{DEFAULT_BRANCH}`, merge/branch flags.
321
+ Substitute `{PR_NUMBER}`, `{REMOTE}`, `{DEFAULT_BRANCH}`, `{PLATFORM}`,
322
+ `{AZDO_MODE}`, `{AZDO_ORG_URL}`, `{AZDO_PROJECT}`, `{PAT}`, merge/branch flags.
229
323
 
230
324
  Parse LAND_REPORT.
231
325
 
@@ -4,6 +4,8 @@ Subagent prompts for each ship stage. The orchestrator reads these and
4
4
  substitutes template variables before launching each subagent.
5
5
 
6
6
  `{PLATFORM}` is either `github` or `azdo` (detected from remote URL).
7
+ `{AZDO_MODE}` is `cli` or `rest` (only relevant when PLATFORM == azdo).
8
+ `{AZDO_ORG}`, `{AZDO_PROJECT}`, `{AZDO_ORG_URL}` provide AzDO context.
7
9
 
8
10
  ---
9
11
 
@@ -69,7 +71,7 @@ FILES TO EXCLUDE: {FILES_TO_EXCLUDE}
69
71
  EOF
70
72
  )"
71
73
 
72
- **If PLATFORM == azdo:**
74
+ **If PLATFORM == azdo AND AZDO_MODE == cli:**
73
75
  az repos pr create \
74
76
  --title "<type>: <concise title>" \
75
77
  --description "$(cat <<'EOF'
@@ -85,20 +87,38 @@ FILES TO EXCLUDE: {FILES_TO_EXCLUDE}
85
87
  )" \
86
88
  --source-branch "$BRANCH" \
87
89
  --target-branch "{DEFAULT_BRANCH}" \
90
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
88
91
  --output json
89
92
 
93
+ **If PLATFORM == azdo AND AZDO_MODE == rest:**
94
+ REPO_NAME=$(basename -s .git "$(git remote get-url origin)")
95
+ AUTH="Authorization: Basic $(echo -n ":{PAT}" | base64)"
96
+ PR_JSON=$(curl -s -X POST \
97
+ -H "$AUTH" \
98
+ -H "Content-Type: application/json" \
99
+ "{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/git/repositories/$REPO_NAME/pullrequests?api-version=7.0" \
100
+ -d "{\"sourceRefName\": \"refs/heads/$BRANCH\", \"targetRefName\": \"refs/heads/{DEFAULT_BRANCH}\", \"title\": \"<type>: <concise title>\", \"description\": \"## Summary\\n<1-3 sentences>\\n\\n## Changes\\n<bullets>\\n\\n## Testing\\n- [ ] <steps>\"}")
101
+
90
102
  6. **Gather info**
91
103
 
92
104
  **If PLATFORM == github:**
93
105
  PR_URL=$(gh pr view --json url -q .url)
94
106
  PR_NUMBER=$(gh pr view --json number -q .number)
95
107
 
96
- **If PLATFORM == azdo:**
108
+ **If PLATFORM == azdo AND AZDO_MODE == cli:**
97
109
  # If az repos pr create returned JSON, parse it directly:
98
110
  PR_NUMBER=$(echo "$PR_JSON" | jq -r '.pullRequestId')
99
111
  # Otherwise list to find it:
100
- PR_NUMBER=$(az repos pr list --source-branch "$BRANCH" --status active --query '[0].pullRequestId' -o tsv)
101
- PR_URL=$(az repos pr show --id $PR_NUMBER --query 'repository.webUrl' -o tsv)/pullrequest/$PR_NUMBER
112
+ PR_NUMBER=$(az repos pr list --source-branch "$BRANCH" --status active \
113
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
114
+ --query '[0].pullRequestId' -o tsv)
115
+ PR_URL="{AZDO_ORG_URL}/{AZDO_PROJECT}/_git/$(basename -s .git "$(git remote get-url origin)")/pullrequest/$PR_NUMBER"
116
+
117
+ **If PLATFORM == azdo AND AZDO_MODE == rest:**
118
+ # Parse from the create response JSON:
119
+ PR_NUMBER=$(echo "$PR_JSON" | jq -r '.pullRequestId')
120
+ REPO_NAME=$(basename -s .git "$(git remote get-url origin)")
121
+ PR_URL="{AZDO_ORG_URL}/{AZDO_PROJECT}/_git/$REPO_NAME/pullrequest/$PR_NUMBER"
102
122
 
103
123
  COMMITS=$(git log {REMOTE}/{DEFAULT_BRANCH}..HEAD --oneline)
104
124
  DIFF_STAT=$(git diff {REMOTE}/{DEFAULT_BRANCH} --shortstat)
@@ -145,14 +165,16 @@ BRANCH: {BRANCH}
145
165
  2. **Report final status**
146
166
  gh pr checks {PR_NUMBER}
147
167
 
148
- **If PLATFORM == azdo:**
168
+ **If PLATFORM == azdo AND AZDO_MODE == cli:**
149
169
 
150
170
  1. **Wait for CI to start** (pipelines may take time to trigger after PR creation)
151
171
  Poll until at least one run appears (max 2 minutes, check every 15s):
152
172
  ```
153
173
  RUNS_FOUND=false
154
174
  for i in $(seq 1 8); do
155
- RUN_COUNT=$(az pipelines runs list --branch "$BRANCH" --top 5 --query "length(@)" -o tsv 2>/dev/null)
175
+ RUN_COUNT=$(az pipelines runs list --branch "$BRANCH" --top 5 \
176
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
177
+ --query "length(@)" -o tsv 2>/dev/null)
156
178
  if [ -n "$RUN_COUNT" ] && [ "$RUN_COUNT" != "0" ]; then
157
179
  RUNS_FOUND=true
158
180
  break
@@ -162,27 +184,101 @@ BRANCH: {BRANCH}
162
184
  ```
163
185
  If no runs appear after 2 minutes, also check PR policy evaluations:
164
186
  ```
165
- az repos pr policy list --id {PR_NUMBER} --query "[].{name:configuration.type.displayName, status:status}" -o table
187
+ az repos pr policy list --id {PR_NUMBER} \
188
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
189
+ --query "[].{name:configuration.type.displayName, status:status}" -o table
166
190
  ```
167
191
  If no policies exist either, report `no_checks`.
168
192
 
169
- 2. **Wait for runs to complete** (max 30 minutes, check every 30s)
193
+ 2. **Wait for runs to complete — with fail-fast** (max 30 minutes, check every 30s)
170
194
  ```
171
195
  for i in $(seq 1 60); do
172
- IN_PROGRESS=$(az pipelines runs list --branch "$BRANCH" --top 5 --query "[?status=='inProgress'] | length(@)" -o tsv)
196
+ # Check for failures FIRST stop immediately if any run has failed
197
+ FAILED=$(az pipelines runs list --branch "$BRANCH" --top 5 \
198
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
199
+ --query "[?result=='failed'] | length(@)" -o tsv 2>/dev/null)
200
+ if [ -n "$FAILED" ] && [ "$FAILED" != "0" ]; then
201
+ echo "CI failure detected — stopping wait"
202
+ break
203
+ fi
204
+
205
+ # Then check if anything is still running
206
+ IN_PROGRESS=$(az pipelines runs list --branch "$BRANCH" --top 5 \
207
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
208
+ --query "[?status=='inProgress'] | length(@)" -o tsv 2>/dev/null)
173
209
  if [ "$IN_PROGRESS" = "0" ] || [ -z "$IN_PROGRESS" ]; then break; fi
174
210
  sleep 30
175
211
  done
176
212
  ```
177
213
 
178
- 3. **Report final status**
179
- Check both pipeline runs and PR policy evaluations:
214
+ 3. **Determine final status**
215
+ Query both pipeline runs and PR policy evaluations:
216
+ ```
217
+ az pipelines runs list --branch "$BRANCH" --top 5 \
218
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
219
+ --query "[].{name:definition.name, status:status, result:result}" -o table
220
+ az repos pr policy list --id {PR_NUMBER} \
221
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
222
+ --query "[].{name:configuration.type.displayName, status:status}" -o table
223
+ ```
224
+
225
+ **Map results to CHECKS_REPORT status:**
226
+ - Any pipeline with `result == "failed"` → `some_failed`
227
+ - Any policy with `status == "rejected"` → `some_failed`
228
+ - All pipelines `result == "succeeded"` AND no rejected policies → `all_passed`
229
+ - Pipelines still `inProgress` after timeout → `pending`
230
+ - No pipelines and no policies found → `no_checks`
231
+
232
+ **IMPORTANT:** Do NOT report `all_passed` if any run has `result == "failed"`.
233
+ Do NOT continue waiting once a failure is detected — report `some_failed`
234
+ immediately with the failing check names.
235
+
236
+ **If PLATFORM == azdo AND AZDO_MODE == rest:**
237
+
238
+ 1. **Wait for CI to start** (max 2 minutes, check every 15s)
239
+ ```
240
+ REPO_NAME=$(basename -s .git "$(git remote get-url origin)")
241
+ AUTH="Authorization: Basic $(echo -n ":{PAT}" | base64)"
242
+ BUILDS_URL="{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/build/builds?branchName=refs/heads/$BRANCH&\$top=5&api-version=7.0"
243
+
244
+ RUNS_FOUND=false
245
+ for i in $(seq 1 8); do
246
+ RUN_COUNT=$(curl -s -H "$AUTH" "$BUILDS_URL" | jq '.value | length')
247
+ if [ -n "$RUN_COUNT" ] && [ "$RUN_COUNT" != "0" ]; then
248
+ RUNS_FOUND=true
249
+ break
250
+ fi
251
+ sleep 15
252
+ done
253
+ ```
254
+ If no runs appear, check PR policy evaluations via REST:
255
+ ```
256
+ EVALS=$(curl -s -H "$AUTH" \
257
+ "{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/policy/evaluations?artifactId=vstfs:///CodeReview/CodeReviewId/{AZDO_PROJECT}/{PR_NUMBER}&api-version=7.0")
258
+ EVAL_COUNT=$(echo "$EVALS" | jq '.value | length')
259
+ ```
260
+ If no evaluations exist either, report `no_checks`.
261
+
262
+ 2. **Wait for runs to complete — with fail-fast** (max 30 minutes, check every 30s)
180
263
  ```
181
- az pipelines runs list --branch "$BRANCH" --top 5 --query "[].{name:definition.name, status:status, result:result}" -o table
182
- az repos pr policy list --id {PR_NUMBER} --query "[].{name:configuration.type.displayName, status:status}" -o table
264
+ for i in $(seq 1 60); do
265
+ BUILDS_JSON=$(curl -s -H "$AUTH" "$BUILDS_URL")
266
+
267
+ # Check for failures FIRST
268
+ FAILED=$(echo "$BUILDS_JSON" | jq '[.value[] | select(.result=="failed")] | length')
269
+ if [ "$FAILED" != "0" ]; then
270
+ echo "CI failure detected — stopping wait"
271
+ break
272
+ fi
273
+
274
+ # Check if anything still running
275
+ IN_PROGRESS=$(echo "$BUILDS_JSON" | jq '[.value[] | select(.status=="inProgress")] | length')
276
+ if [ "$IN_PROGRESS" = "0" ]; then break; fi
277
+ sleep 30
278
+ done
183
279
  ```
184
- A policy status of `approved` or `queued` with no `rejected` means passed.
185
- A pipeline result of `succeeded` means passed; `failed` means failed.
280
+
281
+ 3. **Determine final status** same mapping rules as CLI mode above.
186
282
 
187
283
  ## Output Format
188
284
 
@@ -216,14 +312,29 @@ FAILING CHECKS: {FAILING_CHECKS}
216
312
  gh run list --branch {BRANCH} --status failure --json databaseId --jq '.[0].databaseId'
217
313
  gh run view <run-id> --log-failed
218
314
 
219
- **If PLATFORM == azdo:**
220
- RUN_ID=$(az pipelines runs list --branch {BRANCH} --top 1 --query "[?result=='failed'].id | [0]" -o tsv)
221
- az pipelines runs show --id $RUN_ID --output json
315
+ **If PLATFORM == azdo AND AZDO_MODE == cli:**
316
+ RUN_ID=$(az pipelines runs list --branch {BRANCH} --top 1 \
317
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
318
+ --query "[?result=='failed'].id | [0]" -o tsv)
319
+ az pipelines runs show --id $RUN_ID \
320
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" --output json
222
321
  # Get timeline for detailed step failures:
223
322
  az devops invoke --area build --resource timeline \
224
- --route-parameters project=$(az devops configure -l --query '[?name==`project`].value' -o tsv) buildId=$RUN_ID \
323
+ --route-parameters project="{AZDO_PROJECT}" buildId=$RUN_ID \
324
+ --org "{AZDO_ORG_URL}" \
225
325
  --api-version 7.0 --query "records[?result=='failed'].{name:name, log:log.url}" -o table
226
326
 
327
+ **If PLATFORM == azdo AND AZDO_MODE == rest:**
328
+ AUTH="Authorization: Basic $(echo -n ":{PAT}" | base64)"
329
+ # Get failed build ID
330
+ RUN_ID=$(curl -s -H "$AUTH" \
331
+ "{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/build/builds?branchName=refs/heads/{BRANCH}&resultFilter=failed&\$top=1&api-version=7.0" \
332
+ | jq -r '.value[0].id')
333
+ # Get timeline for step-level failures
334
+ curl -s -H "$AUTH" \
335
+ "{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/build/builds/$RUN_ID/timeline?api-version=7.0" \
336
+ | jq '.records[] | select(.result=="failed") | {name, log: .log.url}'
337
+
227
338
  2. **Analyze and fix**
228
339
  Read the relevant source files, understand the failures, fix the code.
229
340
 
@@ -267,33 +378,80 @@ BRANCH_FLAGS: {--delete-branch (default) | omit if --keep-branch}
267
378
  **If PLATFORM == github:**
268
379
  gh pr merge {PR_NUMBER} {MERGE_FLAGS} {BRANCH_FLAGS}
269
380
 
270
- **If PLATFORM == azdo:**
381
+ **If PLATFORM == azdo AND AZDO_MODE == cli:**
271
382
  # 1. Verify all policies pass before attempting merge
272
- REJECTED=$(az repos pr policy list --id {PR_NUMBER} --query "[?status=='rejected'] | length(@)" -o tsv 2>/dev/null)
383
+ REJECTED=$(az repos pr policy list --id {PR_NUMBER} \
384
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
385
+ --query "[?status=='rejected'] | length(@)" -o tsv 2>/dev/null)
273
386
  if [ -n "$REJECTED" ] && [ "$REJECTED" != "0" ]; then
274
387
  echo "ERROR: $REJECTED PR policies are rejected — cannot merge"
275
- az repos pr policy list --id {PR_NUMBER} --query "[?status=='rejected'].{name:configuration.type.displayName, status:status}" -o table
276
- # Report failure
388
+ az repos pr policy list --id {PR_NUMBER} \
389
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
390
+ --query "[?status=='rejected'].{name:configuration.type.displayName, status:status}" -o table
277
391
  exit 1
278
392
  fi
279
393
 
280
- # 2. Complete the PR with merge strategy
281
- MERGE_STRATEGY="squash" # default; use "noFastForward" if --no-squash
282
- DELETE_BRANCH="true" # default; use "false" if --keep-branch
394
+ # 2. Resolve merge strategy from flags
395
+ if echo "{MERGE_FLAGS}" | grep -q "\-\-merge"; then
396
+ SQUASH_FLAG="false"
397
+ else
398
+ SQUASH_FLAG="true"
399
+ fi
400
+ if echo "{BRANCH_FLAGS}" | grep -q "\-\-keep-branch"; then
401
+ DELETE_FLAG="false"
402
+ else
403
+ DELETE_FLAG="true"
404
+ fi
283
405
 
406
+ # 3. Complete the PR
284
407
  az repos pr update --id {PR_NUMBER} --status completed \
285
- --squash $( [ "$MERGE_STRATEGY" = "squash" ] && echo "true" || echo "false" ) \
286
- --delete-source-branch $DELETE_BRANCH
408
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
409
+ --squash "$SQUASH_FLAG" \
410
+ --delete-source-branch "$DELETE_FLAG"
411
+ MERGE_RC=$?
287
412
 
288
- # 3. If merge fails (e.g. policies still evaluating), wait and retry once
289
- if [ $? -ne 0 ]; then
413
+ # 4. If merge fails (e.g. policies still evaluating), wait and retry once
414
+ if [ $MERGE_RC -ne 0 ]; then
290
415
  echo "Merge failed, waiting 30s for policies to finalize..."
291
416
  sleep 30
292
417
  az repos pr update --id {PR_NUMBER} --status completed \
293
- --squash $( [ "$MERGE_STRATEGY" = "squash" ] && echo "true" || echo "false" ) \
294
- --delete-source-branch $DELETE_BRANCH
418
+ --org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
419
+ --squash "$SQUASH_FLAG" \
420
+ --delete-source-branch "$DELETE_FLAG"
295
421
  fi
296
422
 
423
+ **If PLATFORM == azdo AND AZDO_MODE == rest:**
424
+ AUTH="Authorization: Basic $(echo -n ":{PAT}" | base64)"
425
+ REPO_NAME=$(basename -s .git "$(git remote get-url origin)")
426
+ PR_API="{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/git/repositories/$REPO_NAME/pullrequests/{PR_NUMBER}"
427
+
428
+ # 1. Check for rejected policy evaluations
429
+ EVALS=$(curl -s -H "$AUTH" \
430
+ "{AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/policy/evaluations?artifactId=vstfs:///CodeReview/CodeReviewId/{AZDO_PROJECT}/{PR_NUMBER}&api-version=7.0")
431
+ REJECTED=$(echo "$EVALS" | jq '[.value[] | select(.status=="rejected")] | length')
432
+ if [ "$REJECTED" != "0" ]; then
433
+ echo "ERROR: PR has rejected policy evaluations — cannot merge"
434
+ echo "$EVALS" | jq '.value[] | select(.status=="rejected") | {name: .configuration.type.displayName, status}'
435
+ exit 1
436
+ fi
437
+
438
+ # 2. Resolve merge strategy from flags
439
+ if echo "{MERGE_FLAGS}" | grep -q "\-\-merge"; then
440
+ MERGE_STRATEGY="noFastForward"
441
+ else
442
+ MERGE_STRATEGY="squash"
443
+ fi
444
+ if echo "{BRANCH_FLAGS}" | grep -q "\-\-keep-branch"; then
445
+ DELETE_BRANCH="false"
446
+ else
447
+ DELETE_BRANCH="true"
448
+ fi
449
+
450
+ # 3. Complete the PR
451
+ curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
452
+ "$PR_API?api-version=7.0" \
453
+ -d "{\"status\": \"completed\", \"completionOptions\": {\"mergeStrategy\": \"$MERGE_STRATEGY\", \"deleteSourceBranch\": $DELETE_BRANCH}}"
454
+
297
455
  2. **Sync local repository**
298
456
  Invoke `/sync` to checkout {DEFAULT_BRANCH}, pull latest changes, restore
299
457
  any stashed work, and clean up merged branches. This replaces manual git
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: speccy
3
- description: Deep-dive interview skill for creating comprehensive specifications. Reviews existing code and docs, then interviews the user through multiple rounds of targeted questions covering technical implementation, UI/UX, concerns, and tradeoffs. Produces a structured spec via create-specification. Use when starting a new feature, system, or major change that needs a spec.
3
+ description: Deep-dive interview skill for creating comprehensive specifications. Reviews existing code and docs, then interviews the user through multiple rounds of targeted questions covering technical implementation, UI/UX, concerns, and tradeoffs. Produces a structured spec in specs/. Use when starting a new feature, system, or major change that needs a spec.
4
4
  argument-hint: Goal, feature, or high-level description to specify
5
- allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent, AskUserQuestion
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent, AskUserQuestion, Skill
6
6
  ---
7
7
 
8
8
  # Speccy - Interview-Driven Specification Builder
@@ -60,10 +60,11 @@ Status icons: ✅ done · ❌ failed · ⚠️ degraded · ⏳ working · ⏭️
60
60
  ---
61
61
 
62
62
  Interview the user through multiple rounds of targeted questions to build
63
- a comprehensive specification. Then hand off to the `create-specification`
64
- skill to produce the final structured spec document.
63
+ a comprehensive specification, then write it directly using the spec template
64
+ in `references/spec-template.md`.
65
65
 
66
66
  Interview prompts and question guidelines: `references/interview-guide.md`
67
+ Spec template and writing guidelines: `references/spec-template.md`
67
68
 
68
69
  ## Pre-flight
69
70
 
@@ -71,15 +72,12 @@ Before starting, check all dependencies in this table:
71
72
 
72
73
  | Dependency | Type | Check | Required | Resolution | Detail |
73
74
  |-----------|------|-------|----------|------------|--------|
74
- | create-specification | skill | `~/.claude/skills/create-specification/SKILL.md` or `~/.agents/skills/create-specification/SKILL.md` | yes | ask | This skill is needed to format the final spec. Install with: `npx skills add slamb2k/mad-skills --skill create-specification` |
75
75
  | prime | skill | `~/.claude/skills/prime/SKILL.md` or `~/.claude/plugins/marketplaces/slamb2k/skills/prime/SKILL.md` | no | fallback | Context loading; falls back to manual project scan |
76
76
 
77
77
  For each row, in order:
78
78
  1. Test file existence (check both paths for symlinked skills)
79
79
  2. If found: continue silently
80
80
  3. If missing: apply Resolution strategy
81
- - **ask**: Notify user the skill is missing, offer to install it. If they
82
- decline, halt execution — the final spec cannot be produced without it.
83
81
  4. After all checks: proceed to context gathering
84
82
 
85
83
  ---
@@ -168,23 +166,21 @@ Once the interview is complete and decisions are confirmed:
168
166
  mkdir -p specs
169
167
  ```
170
168
 
171
- 2. **Invoke the `create-specification` skill** using the Skill tool:
172
- ```
173
- Skill(skill: "create-specification")
174
- ```
169
+ 2. **Read the spec template** from `references/spec-template.md`
170
+
171
+ 3. **Generate the spec** by filling the template with:
172
+ - The original GOAL as the introduction and purpose
173
+ - All decisions from the interview rounds, mapped to the appropriate sections
174
+ - Code/architecture context discovered in Stage 1
175
+ - Acceptance criteria derived from requirements decisions
176
+ - Test strategy aligned with the project's existing patterns
177
+
178
+ 4. **Write the spec file** to `specs/{name}.md` where `{name}` is a
179
+ kebab-case slug derived from the GOAL (e.g., `specs/user-auth.md`,
180
+ `specs/payment-integration.md`). Use the Write tool directly.
175
181
 
176
- 3. **Provide the full context** to the skill as input:
177
- - The original GOAL
178
- - All decisions from the interview rounds
179
- - Any relevant code/architecture context discovered in Stage 1
180
- - The spec purpose should match the GOAL description
181
- - **Instruct the skill to save the spec to `specs/{name}.md`** where
182
- `{name}` is a kebab-case slug derived from the GOAL (e.g.,
183
- `specs/user-auth.md`, `specs/payment-integration.md`)
184
-
185
- 4. The `create-specification` skill will handle formatting and template
186
- structure. The `specs/` directory is the standard location — `/build`
187
- and `/prime` both scan it automatically.
182
+ 5. The `specs/` directory is the standard location `/build` and `/prime`
183
+ both scan it automatically.
188
184
 
189
185
  ---
190
186
 
@@ -0,0 +1,113 @@
1
+ # Specification Template
2
+
3
+ Use this template when generating the final specification document in Stage 3.
4
+ Fill every section with content from the interview decisions and project context.
5
+ Omit sections that are genuinely not applicable, but err on the side of inclusion.
6
+
7
+ ## Writing Guidelines
8
+
9
+ - Use precise, explicit, and unambiguous language
10
+ - Clearly distinguish between requirements, constraints, and recommendations
11
+ - Use structured formatting (headings, lists, tables) for easy parsing
12
+ - Avoid idioms, metaphors, or context-dependent references
13
+ - Define all acronyms and domain-specific terms
14
+ - Include examples and edge cases where applicable
15
+ - Ensure the document is self-contained and does not rely on external context
16
+
17
+ ## File Naming
18
+
19
+ Save to `specs/{slug}.md` where `{slug}` is a kebab-case name derived from
20
+ the GOAL. Examples: `specs/user-auth.md`, `specs/payment-integration.md`.
21
+
22
+ ## Template
23
+
24
+ ```md
25
+ ---
26
+ title: [Concise Title Describing the Specification's Focus]
27
+ version: 1.0
28
+ date_created: [YYYY-MM-DD]
29
+ last_updated: [YYYY-MM-DD]
30
+ tags: [e.g., infrastructure, process, design, app, schema, tool, data, architecture]
31
+ ---
32
+
33
+ # Introduction
34
+
35
+ [A short concise introduction to the specification and the goal it is intended to achieve.]
36
+
37
+ ## 1. Purpose & Scope
38
+
39
+ [Clear, concise description of the specification's purpose and scope. State the intended audience and any assumptions.]
40
+
41
+ ## 2. Definitions
42
+
43
+ [List and define all acronyms, abbreviations, and domain-specific terms used in this specification.]
44
+
45
+ ## 3. Requirements, Constraints & Guidelines
46
+
47
+ [Explicitly list all requirements, constraints, rules, and guidelines. Use bullet points or tables for clarity.]
48
+
49
+ - **REQ-001**: [Functional requirement]
50
+ - **SEC-001**: [Security requirement]
51
+ - **CON-001**: [Constraint]
52
+ - **GUD-001**: [Guideline]
53
+ - **PAT-001**: [Pattern to follow]
54
+
55
+ ## 4. Interfaces & Data Contracts
56
+
57
+ [Describe the interfaces, APIs, data contracts, or integration points. Use tables or code blocks for schemas and examples.]
58
+
59
+ ## 5. Acceptance Criteria
60
+
61
+ [Define clear, testable acceptance criteria for each requirement using Given-When-Then format where appropriate.]
62
+
63
+ - **AC-001**: Given [context], When [action], Then [expected outcome]
64
+ - **AC-002**: The system shall [specific behavior] when [condition]
65
+
66
+ ## 6. Test Automation Strategy
67
+
68
+ [Define the testing approach, frameworks, and automation requirements.]
69
+
70
+ - **Test Levels**: Unit, Integration, End-to-End
71
+ - **Frameworks**: [appropriate for the project's stack]
72
+ - **Test Data Management**: [approach for test data creation and cleanup]
73
+ - **CI/CD Integration**: [automated testing in pipelines]
74
+ - **Coverage Requirements**: [minimum code coverage thresholds]
75
+ - **Performance Testing**: [approach for load and performance testing]
76
+
77
+ ## 7. Rationale & Context
78
+
79
+ [Explain the reasoning behind the requirements, constraints, and guidelines. Provide context for design decisions.]
80
+
81
+ ## 8. Dependencies & External Integrations
82
+
83
+ [Define external systems, services, and architectural dependencies. Focus on what is needed rather than how it's implemented.]
84
+
85
+ ### External Systems
86
+ - **EXT-001**: [External system name] - [Purpose and integration type]
87
+
88
+ ### Third-Party Services
89
+ - **SVC-001**: [Service name] - [Required capabilities and SLA requirements]
90
+
91
+ ### Infrastructure Dependencies
92
+ - **INF-001**: [Infrastructure component] - [Requirements and constraints]
93
+
94
+ ### Data Dependencies
95
+ - **DAT-001**: [External data source] - [Format, frequency, and access requirements]
96
+
97
+ ### Technology Platform Dependencies
98
+ - **PLT-001**: [Platform/runtime requirement] - [Version constraints and rationale]
99
+
100
+ **Note**: Focus on architectural and business dependencies, not specific package implementations.
101
+
102
+ ## 9. Examples & Edge Cases
103
+
104
+ [Code snippets or data examples demonstrating correct application, including edge cases.]
105
+
106
+ ## 10. Validation Criteria
107
+
108
+ [List the criteria or tests that must be satisfied for compliance with this specification.]
109
+
110
+ ## 11. Related Specifications / Further Reading
111
+
112
+ [Links to related specs or relevant external documentation.]
113
+ ```