@slamb2k/mad-skills 2.0.37 → 2.0.38
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/README.md +4 -5
- package/package.json +1 -1
- package/skills/manifest.json +5 -5
- package/skills/ship/SKILL.md +37 -47
- package/skills/ship/references/stage-prompts.md +46 -252
- package/skills/ship/scripts/ci-watch.sh +204 -0
- package/skills/ship/scripts/merge.sh +146 -0
- package/skills/ship/tests/evals.json +3 -3
- package/skills/sync/SKILL.md +22 -102
- package/skills/sync/scripts/sync.sh +158 -0
- package/agents/ship-analyzer.md +0 -106
package/README.md
CHANGED
|
@@ -402,7 +402,7 @@ Three methods are available. The table below shows what each delivers:
|
|
|
402
402
|
| | Plugin | npx skills | npm package |
|
|
403
403
|
|---|---|---|---|
|
|
404
404
|
| Skills (slash commands) | ✅ all 10 | ✅ all 10 | — |
|
|
405
|
-
|
|
|
405
|
+
| Bundled scripts (sync, CI, merge) | ✅ | ✅ | — |
|
|
406
406
|
| Session hooks (session-guard) | ✅ | ❌ | — |
|
|
407
407
|
| Cross-agent (Cursor, Cline, etc.) | ❌ Claude Code only | ✅ | — |
|
|
408
408
|
| Selective skill install | ❌ | ✅ | — |
|
|
@@ -410,7 +410,7 @@ Three methods are available. The table below shows what each delivers:
|
|
|
410
410
|
|
|
411
411
|
### Plugin (recommended)
|
|
412
412
|
|
|
413
|
-
Installs skills
|
|
413
|
+
Installs skills and session hooks from the GitHub repo into
|
|
414
414
|
`~/.claude/plugins/`. Updates automatically. Claude Code only.
|
|
415
415
|
|
|
416
416
|
**Step 1 — Register the marketplace (one-time):**
|
|
@@ -448,9 +448,8 @@ npx skills add slamb2k/mad-skills -g -y # All skills, global
|
|
|
448
448
|
npx skills add slamb2k/mad-skills --skill ship -g -y # Specific skill
|
|
449
449
|
```
|
|
450
450
|
|
|
451
|
-
Installs skills into `~/.claude/skills/` (and `~/.agents/skills/` for other agents). **Does not install
|
|
451
|
+
Installs skills into `~/.claude/skills/` (and `~/.agents/skills/` for other agents). **Does not install hooks.** This means:
|
|
452
452
|
|
|
453
|
-
- `/build` falls back to `general-purpose` agent for the ship stage instead of the optimised `ship-analyzer` agent
|
|
454
453
|
- The session-guard hook (CLAUDE.md staleness detection, git validation) is not active
|
|
455
454
|
|
|
456
455
|
Use this method when you need cross-agent compatibility (Cursor, Cline, Amp, etc.) or want to install individual skills.
|
|
@@ -514,7 +513,7 @@ mad-skills/
|
|
|
514
513
|
│ ├── build-manifests.js # Generate skills/manifest.json
|
|
515
514
|
│ └── package-skills.js # Package .skill archives
|
|
516
515
|
├── hooks/ # Session hooks + plugin hook config
|
|
517
|
-
├── agents/ # Agent definitions (
|
|
516
|
+
├── agents/ # Agent definitions (reserved for future use)
|
|
518
517
|
├── tests/results/ # Eval output
|
|
519
518
|
├── archive/ # Legacy skills (v1.x)
|
|
520
519
|
├── .claude-plugin/ # Plugin metadata
|
package/package.json
CHANGED
package/skills/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-03-
|
|
2
|
+
"generated": "2026-03-17T03:05:22.402Z",
|
|
3
3
|
"count": 10,
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
@@ -76,8 +76,8 @@
|
|
|
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":
|
|
80
|
-
"hasScripts":
|
|
79
|
+
"lines": 418,
|
|
80
|
+
"hasScripts": true,
|
|
81
81
|
"hasReferences": true,
|
|
82
82
|
"hasAssets": false,
|
|
83
83
|
"hasTests": true
|
|
@@ -96,8 +96,8 @@
|
|
|
96
96
|
"name": "sync",
|
|
97
97
|
"directory": "sync",
|
|
98
98
|
"description": "Sync local repository with origin/main. Use before starting new work, after completing a PR, or when needing latest upstream changes. Safely stashes uncommitted changes, fetches and pulls origin/main, restores stash, and cleans up stale local branches (merged or with deleted remotes). Invoke when switching contexts or preparing for new feature work.",
|
|
99
|
-
"lines":
|
|
100
|
-
"hasScripts":
|
|
99
|
+
"lines": 164,
|
|
100
|
+
"hasScripts": true,
|
|
101
101
|
"hasReferences": false,
|
|
102
102
|
"hasAssets": false,
|
|
103
103
|
"hasTests": true
|
package/skills/ship/SKILL.md
CHANGED
|
@@ -59,9 +59,10 @@ Status icons: ✅ done · ❌ failed · ⚠️ degraded · ⏳ working · ⏭️
|
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
|
62
|
-
Ship changes through the complete PR lifecycle.
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
Ship changes through the complete PR lifecycle. Deterministic stages (sync, CI
|
|
63
|
+
polling, merge) run as bash scripts for speed and reliability. Only stages that
|
|
64
|
+
require reasoning (commit/PR authoring, CI fix analysis) use LLM subagents.
|
|
65
|
+
Stage prompts are in `references/stage-prompts.md`.
|
|
65
66
|
|
|
66
67
|
## Flags
|
|
67
68
|
|
|
@@ -112,7 +113,6 @@ Before starting, check all dependencies in this table. The table contains
|
|
|
112
113
|
| git | cli | `git --version` | yes | stop | Install from https://git-scm.com |
|
|
113
114
|
| gh | cli | `gh --version` | yes | url | https://cli.github.com |
|
|
114
115
|
| az devops | cli | `az devops -h 2>/dev/null` | no | fallback | Falls back to REST API with PAT; see AzDO tooling below |
|
|
115
|
-
| ship-analyzer | agent | `~/.claude/agents/ship-analyzer.md` or `~/.claude/plugins/marketplaces/slamb2k/agents/ship-analyzer.md` | no | fallback | Uses general-purpose agent |
|
|
116
116
|
|
|
117
117
|
**Platform-conditional rules:**
|
|
118
118
|
- **`gh`**: Only required when `PLATFORM == github`. Skip for AzDO repos.
|
|
@@ -129,6 +129,7 @@ For each applicable row, in order:
|
|
|
129
129
|
- **ask**: notify user, offer to run command in Detail, continue either way (or halt if required)
|
|
130
130
|
- **fallback**: notify user with Detail, continue with degraded behavior
|
|
131
131
|
5. After all checks: summarize what's available and what's degraded
|
|
132
|
+
6. Always show the commit/PR agent: `✅ commit agent general-purpose`
|
|
132
133
|
|
|
133
134
|
### AzDO Tooling Detection
|
|
134
135
|
|
|
@@ -226,38 +227,33 @@ substituted into all stage prompts as `{REMOTE}` and `{DEFAULT_BRANCH}`.
|
|
|
226
227
|
|
|
227
228
|
## Stage 1: Sync
|
|
228
229
|
|
|
229
|
-
|
|
230
|
+
Run the sync script directly (no LLM needed):
|
|
230
231
|
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
model: "haiku",
|
|
235
|
-
description: "Sync with default branch",
|
|
236
|
-
prompt: "Follow ~/.claude/skills/sync/SKILL.md or ~/.claude/plugins/marketplaces/slamb2k/skills/sync/SKILL.md subagent prompt. Return SYNC_REPORT."
|
|
237
|
-
)
|
|
232
|
+
```bash
|
|
233
|
+
SKILL_ROOT="<resolved plugin root>"
|
|
234
|
+
bash "$SKILL_ROOT/skills/sync/scripts/sync.sh" "{REMOTE}" "{DEFAULT_BRANCH}"
|
|
238
235
|
```
|
|
239
236
|
|
|
240
|
-
Parse SYNC_REPORT. Extract `remote` and `default_branch`.
|
|
237
|
+
Parse SYNC_REPORT from output markers. Extract `remote` and `default_branch`.
|
|
238
|
+
Abort if exit code is 1 (fatal).
|
|
241
239
|
|
|
242
240
|
---
|
|
243
241
|
|
|
244
242
|
## Stage 2: Commit, Push & Create PR
|
|
245
243
|
|
|
246
244
|
This stage needs to **read and understand code** to write good commit messages
|
|
247
|
-
and PR descriptions
|
|
245
|
+
and PR descriptions — it's one of the few stages that requires an LLM.
|
|
248
246
|
|
|
249
|
-
Launch **
|
|
247
|
+
Launch **general-purpose subagent** (reads diffs + source files):
|
|
250
248
|
|
|
251
249
|
```
|
|
252
250
|
Task(
|
|
253
|
-
subagent_type: "
|
|
251
|
+
subagent_type: "general-purpose",
|
|
254
252
|
description: "Analyze, commit, push, and create PR",
|
|
255
253
|
prompt: <read from references/stage-prompts.md#stage-2>
|
|
256
254
|
)
|
|
257
255
|
```
|
|
258
256
|
|
|
259
|
-
> **Fallback:** If `ship-analyzer` is not available, use `subagent_type: "general-purpose"`.
|
|
260
|
-
|
|
261
257
|
Substitute `{USER_INTENT}`, `{FILES_TO_INCLUDE}`, `{FILES_TO_EXCLUDE}`,
|
|
262
258
|
`{REMOTE}`, `{DEFAULT_BRANCH}`, `{PLATFORM}`, `{AZDO_MODE}`, `{AZDO_ORG}`,
|
|
263
259
|
`{AZDO_PROJECT}`, `{AZDO_ORG_URL}`, `{PAT}` into the prompt.
|
|
@@ -283,23 +279,19 @@ immediately. This stage loops: watch → detect failure → fix → push → wat
|
|
|
283
279
|
|
|
284
280
|
### Watch
|
|
285
281
|
|
|
286
|
-
|
|
282
|
+
Run the CI watch script directly (no LLM needed — just polling):
|
|
287
283
|
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
)
|
|
284
|
+
```bash
|
|
285
|
+
SKILL_ROOT="<resolved plugin root>"
|
|
286
|
+
bash "$SKILL_ROOT/skills/ship/scripts/ci-watch.sh" \
|
|
287
|
+
"{PLATFORM}" "{PR_NUMBER}" "{BRANCH}" \
|
|
288
|
+
--azdo-mode="{AZDO_MODE}" --azdo-org-url="{AZDO_ORG_URL}" \
|
|
289
|
+
--azdo-project="{AZDO_PROJECT}" --azdo-project-url-safe="{AZDO_PROJECT_URL_SAFE}"
|
|
295
290
|
```
|
|
296
291
|
|
|
297
|
-
Substitute `{PR_NUMBER}`, `{BRANCH}`, `{PLATFORM}`, `{AZDO_MODE}`,
|
|
298
|
-
`{AZDO_ORG}`, `{AZDO_ORG_URL}`, `{AZDO_PROJECT}`, `{PAT}` into the prompt.
|
|
299
|
-
|
|
300
292
|
Briefly inform the user: `⏳ Watching CI for PR #{pr_number}...`
|
|
301
293
|
|
|
302
|
-
Parse CHECKS_REPORT
|
|
294
|
+
Parse CHECKS_REPORT from output markers. Exit code 0=passed, 1=failed, 2=timeout.
|
|
303
295
|
|
|
304
296
|
### Fix (if needed)
|
|
305
297
|
|
|
@@ -347,29 +339,27 @@ defaults (override via `--no-squash` and `--keep-branch` flags only).
|
|
|
347
339
|
|
|
348
340
|
### 5a. Merge the PR
|
|
349
341
|
|
|
350
|
-
|
|
342
|
+
Run the merge script directly (no LLM needed):
|
|
351
343
|
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
344
|
+
```bash
|
|
345
|
+
SKILL_ROOT="<resolved plugin root>"
|
|
346
|
+
bash "$SKILL_ROOT/skills/ship/scripts/merge.sh" \
|
|
347
|
+
"{PLATFORM}" "{PR_NUMBER}" \
|
|
348
|
+
{MERGE_FLAGS} {BRANCH_FLAGS} \
|
|
349
|
+
--azdo-mode="{AZDO_MODE}" --azdo-org-url="{AZDO_ORG_URL}" \
|
|
350
|
+
--azdo-project="{AZDO_PROJECT}" --azdo-project-url-safe="{AZDO_PROJECT_URL_SAFE}"
|
|
359
351
|
```
|
|
360
352
|
|
|
361
|
-
|
|
362
|
-
`{AZDO_MODE}`, `{AZDO_ORG_URL}`, `{AZDO_PROJECT}`, `{PAT}`, merge/branch flags.
|
|
363
|
-
|
|
364
|
-
Parse LAND_REPORT.
|
|
353
|
+
Parse LAND_REPORT from output markers. Exit code 0=merged, 1=failed.
|
|
365
354
|
|
|
366
355
|
### 5b. Sync local repo
|
|
367
356
|
|
|
368
|
-
After the merge
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
357
|
+
After the merge script succeeds, run the sync script to checkout the default
|
|
358
|
+
branch, pull the merge commit, and **clean up stale branches**:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
bash "$SKILL_ROOT/skills/sync/scripts/sync.sh" "{REMOTE}" "{DEFAULT_BRANCH}"
|
|
362
|
+
```
|
|
373
363
|
|
|
374
364
|
---
|
|
375
365
|
|
|
@@ -12,7 +12,10 @@ Use `{AZDO_PROJECT}` for CLI `--project` flags and display. Use `{AZDO_PROJECT_U
|
|
|
12
12
|
|
|
13
13
|
## Stage 2: Commit, Push & Create PR
|
|
14
14
|
|
|
15
|
-
**Agent:**
|
|
15
|
+
**Agent:** general-purpose
|
|
16
|
+
|
|
17
|
+
This is the only shipping stage that requires an LLM — it reads and understands
|
|
18
|
+
code to produce meaningful commit messages and PR descriptions.
|
|
16
19
|
|
|
17
20
|
```
|
|
18
21
|
Ship the following changes. Analyze the diffs, create semantic commits,
|
|
@@ -31,6 +34,36 @@ FILES TO INCLUDE: {FILES_TO_INCLUDE}
|
|
|
31
34
|
|
|
32
35
|
FILES TO EXCLUDE: {FILES_TO_EXCLUDE}
|
|
33
36
|
|
|
37
|
+
## Principles
|
|
38
|
+
|
|
39
|
+
1. **Read before writing** — Always read the actual diff AND relevant source
|
|
40
|
+
files before composing commit messages. Never guess at intent from filenames.
|
|
41
|
+
2. **Semantic grouping** — Group related changes into logical commits. A
|
|
42
|
+
"logical group" shares a single purpose.
|
|
43
|
+
3. **Concise but complete** — Commit messages explain WHAT and WHY in 1-2
|
|
44
|
+
sentences. PR descriptions give the full picture.
|
|
45
|
+
4. **No attribution** — Never add Co-Authored-By or Generated-by lines.
|
|
46
|
+
5. **Prioritize impact** — When changes span many files, read the most
|
|
47
|
+
impactful diffs first (source > tests > config).
|
|
48
|
+
|
|
49
|
+
## Commit message format
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
<type>(<scope>): <imperative description>
|
|
53
|
+
|
|
54
|
+
<optional body: what changed and why, wrapped at 72 chars>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Types: feat, fix, refactor, docs, chore, test, perf
|
|
58
|
+
|
|
59
|
+
Good examples:
|
|
60
|
+
- feat(auth): replace pairing gate with channel allowlist
|
|
61
|
+
- fix(memory): correct positional arg order in get_recent_commitments
|
|
62
|
+
|
|
63
|
+
Bad examples:
|
|
64
|
+
- update files (too vague)
|
|
65
|
+
- feat: changes to auth system (no scope, vague)
|
|
66
|
+
|
|
34
67
|
## Steps
|
|
35
68
|
|
|
36
69
|
1. **Analyze changes**
|
|
@@ -52,9 +85,9 @@ FILES TO EXCLUDE: {FILES_TO_EXCLUDE}
|
|
|
52
85
|
)"
|
|
53
86
|
|
|
54
87
|
Rules:
|
|
55
|
-
- Never add Co-Authored-By or attribution lines
|
|
56
88
|
- Use HEREDOC for commit messages (ensures proper formatting)
|
|
57
|
-
-
|
|
89
|
+
- Use `git add -p` when only some hunks in a file belong in a given commit
|
|
90
|
+
- Always verify the push succeeded before creating the PR
|
|
58
91
|
|
|
59
92
|
4. **Push**
|
|
60
93
|
git push -u {REMOTE} $BRANCH
|
|
@@ -149,152 +182,13 @@ SHIP_REPORT:
|
|
|
149
182
|
|
|
150
183
|
## Stage 3: Wait for CI
|
|
151
184
|
|
|
152
|
-
**
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
Monitor PR/pipeline status checks until complete. Fail fast — stop polling the
|
|
156
|
-
moment any check fails so fixes can start immediately.
|
|
157
|
-
|
|
158
|
-
Limit CHECKS_REPORT to 10 lines maximum.
|
|
159
|
-
|
|
160
|
-
PLATFORM: {PLATFORM}
|
|
161
|
-
PR_NUMBER: {PR_NUMBER}
|
|
162
|
-
BRANCH: {BRANCH}
|
|
163
|
-
|
|
164
|
-
## Steps
|
|
165
|
-
|
|
166
|
-
**If PLATFORM == github:**
|
|
167
|
-
|
|
168
|
-
1. **Wait for checks — fail fast on first failure**
|
|
169
|
-
gh pr checks {PR_NUMBER} --watch --fail-fast
|
|
170
|
-
|
|
171
|
-
2. **Report final status**
|
|
172
|
-
gh pr checks {PR_NUMBER}
|
|
185
|
+
**Replaced by script:** `scripts/ci-watch.sh`
|
|
173
186
|
|
|
174
|
-
|
|
187
|
+
The orchestrator runs this script directly via Bash — no LLM needed.
|
|
188
|
+
See `skills/ship/scripts/ci-watch.sh` for the full implementation.
|
|
175
189
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
RUNS_FOUND=false
|
|
180
|
-
for i in $(seq 1 8); do
|
|
181
|
-
RUN_COUNT=$(az pipelines runs list --branch "$BRANCH" --top 5 \
|
|
182
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
183
|
-
--query "length(@)" -o tsv 2>/dev/null)
|
|
184
|
-
if [ -n "$RUN_COUNT" ] && [ "$RUN_COUNT" != "0" ]; then
|
|
185
|
-
RUNS_FOUND=true
|
|
186
|
-
break
|
|
187
|
-
fi
|
|
188
|
-
sleep 15
|
|
189
|
-
done
|
|
190
|
-
```
|
|
191
|
-
If no runs appear after 2 minutes, also check PR policy evaluations:
|
|
192
|
-
```
|
|
193
|
-
az repos pr policy list --id {PR_NUMBER} \
|
|
194
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
195
|
-
--query "[].{name:configuration.type.displayName, status:status}" -o table
|
|
196
|
-
```
|
|
197
|
-
If no policies exist either, report `no_checks`.
|
|
198
|
-
|
|
199
|
-
2. **Wait for runs to complete — with fail-fast** (max 30 minutes, check every 15s)
|
|
200
|
-
```
|
|
201
|
-
for i in $(seq 1 120); do
|
|
202
|
-
# Check for failures FIRST — stop immediately if any run has failed
|
|
203
|
-
FAILED=$(az pipelines runs list --branch "$BRANCH" --top 5 \
|
|
204
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
205
|
-
--query "[?result=='failed'] | length(@)" -o tsv 2>/dev/null)
|
|
206
|
-
if [ -n "$FAILED" ] && [ "$FAILED" != "0" ]; then
|
|
207
|
-
echo "CI failure detected — stopping wait"
|
|
208
|
-
break
|
|
209
|
-
fi
|
|
210
|
-
|
|
211
|
-
# Then check if anything is still running
|
|
212
|
-
IN_PROGRESS=$(az pipelines runs list --branch "$BRANCH" --top 5 \
|
|
213
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
214
|
-
--query "[?status=='inProgress'] | length(@)" -o tsv 2>/dev/null)
|
|
215
|
-
if [ "$IN_PROGRESS" = "0" ] || [ -z "$IN_PROGRESS" ]; then break; fi
|
|
216
|
-
sleep 15
|
|
217
|
-
done
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
3. **Determine final status**
|
|
221
|
-
Query both pipeline runs and PR policy evaluations:
|
|
222
|
-
```
|
|
223
|
-
az pipelines runs list --branch "$BRANCH" --top 5 \
|
|
224
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
225
|
-
--query "[].{name:definition.name, status:status, result:result}" -o table
|
|
226
|
-
az repos pr policy list --id {PR_NUMBER} \
|
|
227
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
228
|
-
--query "[].{name:configuration.type.displayName, status:status}" -o table
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
**Map results to CHECKS_REPORT status:**
|
|
232
|
-
- Any pipeline with `result == "failed"` → `some_failed`
|
|
233
|
-
- Any policy with `status == "rejected"` → `some_failed`
|
|
234
|
-
- All pipelines `result == "succeeded"` AND no rejected policies → `all_passed`
|
|
235
|
-
- Pipelines still `inProgress` after timeout → `pending`
|
|
236
|
-
- No pipelines and no policies found → `no_checks`
|
|
237
|
-
|
|
238
|
-
**IMPORTANT:** Do NOT report `all_passed` if any run has `result == "failed"`.
|
|
239
|
-
Do NOT continue waiting once a failure is detected — report `some_failed`
|
|
240
|
-
immediately with the failing check names.
|
|
241
|
-
|
|
242
|
-
**If PLATFORM == azdo AND AZDO_MODE == rest:**
|
|
243
|
-
|
|
244
|
-
1. **Wait for CI to start** (max 2 minutes, check every 15s)
|
|
245
|
-
```
|
|
246
|
-
REPO_NAME=$(basename -s .git "$(git remote get-url origin)")
|
|
247
|
-
AUTH="Authorization: Basic $(echo -n ":{PAT}" | base64)"
|
|
248
|
-
BUILDS_URL="{AZDO_ORG_URL}/{AZDO_PROJECT_URL_SAFE}/_apis/build/builds?branchName=refs/heads/$BRANCH&\$top=5&api-version=7.0"
|
|
249
|
-
|
|
250
|
-
RUNS_FOUND=false
|
|
251
|
-
for i in $(seq 1 8); do
|
|
252
|
-
RUN_COUNT=$(curl -s -H "$AUTH" "$BUILDS_URL" | jq '.value | length')
|
|
253
|
-
if [ -n "$RUN_COUNT" ] && [ "$RUN_COUNT" != "0" ]; then
|
|
254
|
-
RUNS_FOUND=true
|
|
255
|
-
break
|
|
256
|
-
fi
|
|
257
|
-
sleep 15
|
|
258
|
-
done
|
|
259
|
-
```
|
|
260
|
-
If no runs appear, check PR policy evaluations via REST:
|
|
261
|
-
```
|
|
262
|
-
EVALS=$(curl -s -H "$AUTH" \
|
|
263
|
-
"{AZDO_ORG_URL}/{AZDO_PROJECT_URL_SAFE}/_apis/policy/evaluations?artifactId=vstfs:///CodeReview/CodeReviewId/{AZDO_PROJECT_URL_SAFE}/{PR_NUMBER}&api-version=7.0")
|
|
264
|
-
EVAL_COUNT=$(echo "$EVALS" | jq '.value | length')
|
|
265
|
-
```
|
|
266
|
-
If no evaluations exist either, report `no_checks`.
|
|
267
|
-
|
|
268
|
-
2. **Wait for runs to complete — with fail-fast** (max 30 minutes, check every 15s)
|
|
269
|
-
```
|
|
270
|
-
for i in $(seq 1 120); do
|
|
271
|
-
BUILDS_JSON=$(curl -s -H "$AUTH" "$BUILDS_URL")
|
|
272
|
-
|
|
273
|
-
# Check for failures FIRST
|
|
274
|
-
FAILED=$(echo "$BUILDS_JSON" | jq '[.value[] | select(.result=="failed")] | length')
|
|
275
|
-
if [ "$FAILED" != "0" ]; then
|
|
276
|
-
echo "CI failure detected — stopping wait"
|
|
277
|
-
break
|
|
278
|
-
fi
|
|
279
|
-
|
|
280
|
-
# Check if anything still running
|
|
281
|
-
IN_PROGRESS=$(echo "$BUILDS_JSON" | jq '[.value[] | select(.status=="inProgress")] | length')
|
|
282
|
-
if [ "$IN_PROGRESS" = "0" ]; then break; fi
|
|
283
|
-
sleep 15
|
|
284
|
-
done
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
3. **Determine final status** — same mapping rules as CLI mode above.
|
|
288
|
-
|
|
289
|
-
## Output Format
|
|
290
|
-
|
|
291
|
-
CHECKS_REPORT:
|
|
292
|
-
- status: all_passed|some_failed|pending|no_checks
|
|
293
|
-
- checks:
|
|
294
|
-
- name: {check name}
|
|
295
|
-
status: success|failure|pending
|
|
296
|
-
- failing_checks: {list of failed check names, or "none"}
|
|
297
|
-
```
|
|
190
|
+
Output is a structured `CHECKS_REPORT` between `CHECKS_REPORT_BEGIN`/`END` markers.
|
|
191
|
+
Exit codes: 0=all_passed, 1=some_failed, 2=timeout, 3=tool error.
|
|
298
192
|
|
|
299
193
|
---
|
|
300
194
|
|
|
@@ -377,110 +271,10 @@ FIX_REPORT:
|
|
|
377
271
|
|
|
378
272
|
## Stage 5: Merge & Final Sync
|
|
379
273
|
|
|
380
|
-
**
|
|
274
|
+
**Replaced by script:** `scripts/merge.sh`
|
|
381
275
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
via /sync after this subagent returns.
|
|
385
|
-
Do NOT ask the user for confirmation — proceed immediately.
|
|
386
|
-
|
|
387
|
-
Limit LAND_REPORT to 10 lines maximum.
|
|
388
|
-
|
|
389
|
-
PLATFORM: {PLATFORM}
|
|
390
|
-
PR_NUMBER: {PR_NUMBER}
|
|
391
|
-
MERGE_FLAGS: {--squash (default) | --merge if --no-squash}
|
|
392
|
-
BRANCH_FLAGS: {--delete-branch (default) | omit if --keep-branch}
|
|
393
|
-
|
|
394
|
-
## Steps
|
|
395
|
-
|
|
396
|
-
1. **Merge**
|
|
397
|
-
|
|
398
|
-
**If PLATFORM == github:**
|
|
399
|
-
gh pr merge {PR_NUMBER} {MERGE_FLAGS} {BRANCH_FLAGS}
|
|
400
|
-
|
|
401
|
-
**If PLATFORM == azdo AND AZDO_MODE == cli:**
|
|
402
|
-
# 1. Verify all policies pass before attempting merge
|
|
403
|
-
REJECTED=$(az repos pr policy list --id {PR_NUMBER} \
|
|
404
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
405
|
-
--query "[?status=='rejected'] | length(@)" -o tsv 2>/dev/null)
|
|
406
|
-
if [ -n "$REJECTED" ] && [ "$REJECTED" != "0" ]; then
|
|
407
|
-
echo "ERROR: $REJECTED PR policies are rejected — cannot merge"
|
|
408
|
-
az repos pr policy list --id {PR_NUMBER} \
|
|
409
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
410
|
-
--query "[?status=='rejected'].{name:configuration.type.displayName, status:status}" -o table
|
|
411
|
-
exit 1
|
|
412
|
-
fi
|
|
413
|
-
|
|
414
|
-
# 2. Resolve merge strategy from flags
|
|
415
|
-
if echo "{MERGE_FLAGS}" | grep -q "\-\-merge"; then
|
|
416
|
-
SQUASH_FLAG="false"
|
|
417
|
-
else
|
|
418
|
-
SQUASH_FLAG="true"
|
|
419
|
-
fi
|
|
420
|
-
if echo "{BRANCH_FLAGS}" | grep -q "\-\-keep-branch"; then
|
|
421
|
-
DELETE_FLAG="false"
|
|
422
|
-
else
|
|
423
|
-
DELETE_FLAG="true"
|
|
424
|
-
fi
|
|
425
|
-
|
|
426
|
-
# 3. Complete the PR
|
|
427
|
-
az repos pr update --id {PR_NUMBER} --status completed \
|
|
428
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
429
|
-
--squash "$SQUASH_FLAG" \
|
|
430
|
-
--delete-source-branch "$DELETE_FLAG"
|
|
431
|
-
MERGE_RC=$?
|
|
432
|
-
|
|
433
|
-
# 4. If merge fails (e.g. policies still evaluating), wait and retry once
|
|
434
|
-
if [ $MERGE_RC -ne 0 ]; then
|
|
435
|
-
echo "Merge failed, waiting 30s for policies to finalize..."
|
|
436
|
-
sleep 30
|
|
437
|
-
az repos pr update --id {PR_NUMBER} --status completed \
|
|
438
|
-
--org "{AZDO_ORG_URL}" --project "{AZDO_PROJECT}" \
|
|
439
|
-
--squash "$SQUASH_FLAG" \
|
|
440
|
-
--delete-source-branch "$DELETE_FLAG"
|
|
441
|
-
fi
|
|
276
|
+
The orchestrator runs this script directly via Bash — no LLM needed.
|
|
277
|
+
See `skills/ship/scripts/merge.sh` for the full implementation.
|
|
442
278
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
REPO_NAME=$(basename -s .git "$(git remote get-url origin)")
|
|
446
|
-
PR_API="{AZDO_ORG_URL}/{AZDO_PROJECT_URL_SAFE}/_apis/git/repositories/$REPO_NAME/pullrequests/{PR_NUMBER}"
|
|
447
|
-
|
|
448
|
-
# 1. Check for rejected policy evaluations
|
|
449
|
-
EVALS=$(curl -s -H "$AUTH" \
|
|
450
|
-
"{AZDO_ORG_URL}/{AZDO_PROJECT_URL_SAFE}/_apis/policy/evaluations?artifactId=vstfs:///CodeReview/CodeReviewId/{AZDO_PROJECT_URL_SAFE}/{PR_NUMBER}&api-version=7.0")
|
|
451
|
-
REJECTED=$(echo "$EVALS" | jq '[.value[] | select(.status=="rejected")] | length')
|
|
452
|
-
if [ "$REJECTED" != "0" ]; then
|
|
453
|
-
echo "ERROR: PR has rejected policy evaluations — cannot merge"
|
|
454
|
-
echo "$EVALS" | jq '.value[] | select(.status=="rejected") | {name: .configuration.type.displayName, status}'
|
|
455
|
-
exit 1
|
|
456
|
-
fi
|
|
457
|
-
|
|
458
|
-
# 2. Resolve merge strategy from flags
|
|
459
|
-
if echo "{MERGE_FLAGS}" | grep -q "\-\-merge"; then
|
|
460
|
-
MERGE_STRATEGY="noFastForward"
|
|
461
|
-
else
|
|
462
|
-
MERGE_STRATEGY="squash"
|
|
463
|
-
fi
|
|
464
|
-
if echo "{BRANCH_FLAGS}" | grep -q "\-\-keep-branch"; then
|
|
465
|
-
DELETE_BRANCH="false"
|
|
466
|
-
else
|
|
467
|
-
DELETE_BRANCH="true"
|
|
468
|
-
fi
|
|
469
|
-
|
|
470
|
-
# 3. Complete the PR
|
|
471
|
-
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
|
|
472
|
-
"$PR_API?api-version=7.0" \
|
|
473
|
-
-d "{\"status\": \"completed\", \"completionOptions\": {\"mergeStrategy\": \"$MERGE_STRATEGY\", \"deleteSourceBranch\": $DELETE_BRANCH}}"
|
|
474
|
-
|
|
475
|
-
2. **Report**
|
|
476
|
-
MERGE_COMMIT=$(git rev-parse --short HEAD)
|
|
477
|
-
|
|
478
|
-
## Output Format
|
|
479
|
-
|
|
480
|
-
LAND_REPORT:
|
|
481
|
-
- status: success|failed
|
|
482
|
-
- merge_commit: {hash}
|
|
483
|
-
- merge_type: squash|merge
|
|
484
|
-
- branch_deleted: true|false
|
|
485
|
-
- errors: {any errors}
|
|
486
|
-
```
|
|
279
|
+
Output is a structured `LAND_REPORT` between `LAND_REPORT_BEGIN`/`END` markers.
|
|
280
|
+
Exit codes: 0=merged, 1=failed, 2=failed after retry.
|