@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.
@@ -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.37",
4
+ "version": "2.0.38",
5
5
  "author": {
6
6
  "name": "slamb2k",
7
7
  "url": "https://github.com/slamb2k"
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
- | Agents (e.g. ship-analyzer) | ✅ | | — |
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, agents, and session hooks from the GitHub repo into
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 agents or hooks.** This means:
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 (ship-analyzer)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slamb2k/mad-skills",
3
- "version": "2.0.37",
3
+ "version": "2.0.38",
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-17T02:08:37.271Z",
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": 428,
80
- "hasScripts": false,
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": 244,
100
- "hasScripts": false,
99
+ "lines": 164,
100
+ "hasScripts": true,
101
101
  "hasReferences": false,
102
102
  "hasAssets": false,
103
103
  "hasTests": true
@@ -59,9 +59,10 @@ Status icons: ✅ done · ❌ failed · ⚠️ degraded · ⏳ working · ⏭️
59
59
 
60
60
  ---
61
61
 
62
- Ship changes through the complete PR lifecycle. Every stage runs in a subagent
63
- to isolate context from the primary conversation. Prompts for each stage are
64
- in `references/stage-prompts.md`.
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
- Launch **Bash subagent** (haiku simple git commands):
230
+ Run the sync script directly (no LLM needed):
230
231
 
231
- ```
232
- Task(
233
- subagent_type: "Bash",
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`. Abort if sync failed.
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. Use a code-aware subagent.
245
+ and PR descriptions it's one of the few stages that requires an LLM.
248
246
 
249
- Launch **ship-analyzer subagent** (reads diffs + source files):
247
+ Launch **general-purpose subagent** (reads diffs + source files):
250
248
 
251
249
  ```
252
250
  Task(
253
- subagent_type: "ship-analyzer",
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
- Launch **Bash subagent** (haiku — polling):
282
+ Run the CI watch script directly (no LLM needed just polling):
287
283
 
288
- ```
289
- Task(
290
- subagent_type: "Bash",
291
- model: "haiku",
292
- description: "Monitor CI checks",
293
- prompt: <read from references/stage-prompts.md#stage-3>
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 when the subagent returns.
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
- Launch **Bash subagent** (haiku simple git + platform CLI commands):
342
+ Run the merge script directly (no LLM needed):
351
343
 
352
- ```
353
- Task(
354
- subagent_type: "Bash",
355
- model: "haiku",
356
- description: "Merge PR",
357
- prompt: <read from references/stage-prompts.md#stage-5>
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
- Substitute `{PR_NUMBER}`, `{REMOTE}`, `{DEFAULT_BRANCH}`, `{PLATFORM}`,
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 subagent succeeds, invoke `/sync` from the orchestrator to
369
- checkout the default branch, pull the merge commit, and **clean up stale
370
- branches** (prune gone remotes, delete merged branches). This replaces any
371
- manual `git checkout`/`git pull` — `/sync` handles everything including stash
372
- restore and branch cleanup.
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:** ship-analyzer (fallback: general-purpose)
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
- - Types: feat, fix, refactor, docs, chore, test, perf
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
- **Agent:** Bash (haiku, background)
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
- **If PLATFORM == azdo AND AZDO_MODE == cli:**
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
- 1. **Wait for CI to start** (pipelines may take time to trigger after PR creation)
177
- Poll until at least one run appears (max 2 minutes, check every 15s):
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
- **Agent:** Bash (haiku)
274
+ **Replaced by script:** `scripts/merge.sh`
381
275
 
382
- ```
383
- Merge the pull request. Do NOT sync locally — the orchestrator handles that
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
- **If PLATFORM == azdo AND AZDO_MODE == rest:**
444
- AUTH="Authorization: Basic $(echo -n ":{PAT}" | base64)"
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.