@slamb2k/mad-skills 2.0.25 → 2.0.27
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/brace/SKILL.md +115 -2
- package/skills/manifest.json +5 -5
- package/skills/ship/SKILL.md +123 -29
- package/skills/ship/references/stage-prompts.md +190 -32
- package/skills/speccy/SKILL.md +19 -23
- package/skills/speccy/references/spec-template.md +113 -0
package/package.json
CHANGED
package/skills/brace/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
---
|
package/skills/manifest.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-03-
|
|
2
|
+
"generated": "2026-03-12T10:15:46.290Z",
|
|
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":
|
|
9
|
+
"lines": 441,
|
|
10
10
|
"hasScripts": false,
|
|
11
11
|
"hasReferences": true,
|
|
12
12
|
"hasAssets": true,
|
|
@@ -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":
|
|
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
|
|
89
|
-
"lines":
|
|
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,
|
package/skills/ship/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
122
|
+
5. After all checks: summarize what's available and what's degraded
|
|
95
123
|
|
|
96
|
-
|
|
97
|
-
substituted into all stage prompts as `{REMOTE}` and `{DEFAULT_BRANCH}`.
|
|
98
|
-
|
|
99
|
-
### Platform Detection
|
|
124
|
+
### AzDO Tooling Detection
|
|
100
125
|
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
133
|
+
AZDO_MODE="rest"
|
|
111
134
|
fi
|
|
112
135
|
```
|
|
113
136
|
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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}`
|
|
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}`
|
|
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}`
|
|
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}`,
|
|
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
|
|
101
|
-
|
|
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
|
|
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}
|
|
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
|
-
|
|
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. **
|
|
179
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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
|
|
221
|
-
|
|
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
|
|
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}
|
|
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}
|
|
276
|
-
|
|
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.
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
--
|
|
286
|
-
--
|
|
408
|
+
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
409
|
+
--squash "$SQUASH_FLAG" \
|
|
410
|
+
--delete-source-branch "$DELETE_FLAG"
|
|
411
|
+
MERGE_RC=$?
|
|
287
412
|
|
|
288
|
-
#
|
|
289
|
-
if [
|
|
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
|
-
--
|
|
294
|
-
--
|
|
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
|
package/skills/speccy/SKILL.md
CHANGED
|
@@ -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
|
|
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
|
|
64
|
-
|
|
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. **
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
+
```
|