@slamb2k/mad-skills 2.0.39 → 2.0.41
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 +15 -0
- package/skills/brace/references/claude-md-template.md +14 -0
- package/skills/build/SKILL.md +33 -0
- package/skills/manifest.json +6 -6
- package/skills/rig/SKILL.md +20 -0
- package/skills/ship/SKILL.md +79 -2
- package/skills/ship/references/stage-prompts.md +6 -0
- package/skills/ship/scripts/ci-watch.sh +26 -6
- package/skills/ship/scripts/merge.sh +57 -20
- package/skills/speccy/SKILL.md +19 -0
package/package.json
CHANGED
package/skills/brace/SKILL.md
CHANGED
|
@@ -249,6 +249,21 @@ Before sending the prompt, substitute these variables:
|
|
|
249
249
|
|
|
250
250
|
Parse SCAFFOLD_REPORT. If status is "failed", report to user and stop.
|
|
251
251
|
|
|
252
|
+
### Branch Discipline Injection
|
|
253
|
+
|
|
254
|
+
When updating an existing project CLAUDE.md (not creating from template):
|
|
255
|
+
|
|
256
|
+
1. Check if `## Branch Discipline` already exists:
|
|
257
|
+
```bash
|
|
258
|
+
grep -q "## Branch Discipline" CLAUDE.md
|
|
259
|
+
```
|
|
260
|
+
2. If NOT found, inject the Branch Discipline section before `## Guardrails`:
|
|
261
|
+
- Read the file content
|
|
262
|
+
- Find the line containing `## Guardrails`
|
|
263
|
+
- Insert the Branch Discipline section (from the template) immediately before it
|
|
264
|
+
- If no `## Guardrails` section exists, append the section at the end of the file
|
|
265
|
+
3. If already present, skip (idempotent)
|
|
266
|
+
|
|
252
267
|
---
|
|
253
268
|
|
|
254
269
|
## Phase 5: Verification & Report
|
|
@@ -51,6 +51,20 @@ handles curated facts.
|
|
|
51
51
|
|
|
52
52
|
{UNIVERSAL_PRINCIPLES}
|
|
53
53
|
|
|
54
|
+
## Branch Discipline
|
|
55
|
+
|
|
56
|
+
- **Always sync to main before starting new work** — run `/sync` or
|
|
57
|
+
`git checkout main && git pull` before creating a feature branch
|
|
58
|
+
- **Never branch from a feature branch** — always branch from an up-to-date `main`
|
|
59
|
+
- **One feature per branch** — don't stack unrelated changes on the same branch
|
|
60
|
+
- **After shipping a PR, sync immediately** — checkout main and pull before
|
|
61
|
+
starting the next task
|
|
62
|
+
- **If a PR is pending review**, switch to main before starting unrelated work —
|
|
63
|
+
don't build on top of an unmerged branch
|
|
64
|
+
|
|
65
|
+
These rules prevent divergent branches that require complex rebases with risk
|
|
66
|
+
of silent conflict resolution.
|
|
67
|
+
|
|
54
68
|
## Guardrails
|
|
55
69
|
|
|
56
70
|
- Verify tool output format before chaining into another tool
|
package/skills/build/SKILL.md
CHANGED
|
@@ -141,6 +141,39 @@ Before Stage 1, resolve the PLAN argument into content:
|
|
|
141
141
|
- File: `Plan: {file path} ({line count} lines)`
|
|
142
142
|
- Text: `Plan: inline ({word count} words)`
|
|
143
143
|
|
|
144
|
+
## Pre-Build Branch Check
|
|
145
|
+
|
|
146
|
+
Before starting Stage 1, verify the working tree is suitable for building:
|
|
147
|
+
|
|
148
|
+
1. **Detect current branch and default branch:**
|
|
149
|
+
```bash
|
|
150
|
+
CURRENT=$(git branch --show-current)
|
|
151
|
+
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
|
|
152
|
+
DEFAULT_BRANCH="${DEFAULT_BRANCH:-main}"
|
|
153
|
+
git fetch origin "$DEFAULT_BRANCH" --quiet 2>/dev/null
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
2. **If on a feature branch** (not `main`/`master`/default):
|
|
157
|
+
```bash
|
|
158
|
+
BEHIND=$(git rev-list --count HEAD..origin/"$DEFAULT_BRANCH" 2>/dev/null || echo 0)
|
|
159
|
+
```
|
|
160
|
+
If `BEHIND > 0`, warn the user via `AskUserQuestion`:
|
|
161
|
+
```
|
|
162
|
+
"You're on branch '{CURRENT}' which is {BEHIND} commits behind {DEFAULT_BRANCH}.
|
|
163
|
+
Starting a new feature here risks divergent branches and complex rebases."
|
|
164
|
+
```
|
|
165
|
+
Options:
|
|
166
|
+
- "Switch to main first (Recommended)" — run `/sync`, then create a new branch
|
|
167
|
+
- "Continue on this branch" — proceed (user accepts the risk)
|
|
168
|
+
- "Cancel" — stop
|
|
169
|
+
|
|
170
|
+
3. **If on the default branch** and not up to date:
|
|
171
|
+
```bash
|
|
172
|
+
LOCAL=$(git rev-parse "$DEFAULT_BRANCH")
|
|
173
|
+
REMOTE=$(git rev-parse "origin/$DEFAULT_BRANCH")
|
|
174
|
+
```
|
|
175
|
+
If `LOCAL != REMOTE`, run `/sync` automatically before proceeding.
|
|
176
|
+
|
|
144
177
|
---
|
|
145
178
|
|
|
146
179
|
## Stage 1: Explore
|
package/skills/manifest.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-03-
|
|
2
|
+
"generated": "2026-03-22T07:57:16.501Z",
|
|
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": 460,
|
|
10
10
|
"hasScripts": false,
|
|
11
11
|
"hasReferences": true,
|
|
12
12
|
"hasAssets": true,
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"name": "build",
|
|
17
17
|
"directory": "build",
|
|
18
18
|
"description": "Context-isolated feature development pipeline. Takes a detailed design/plan as argument and executes the full feature-dev lifecycle (explore, question, architect, implement, review, ship) inside subagents so the primary conversation stays compact. Use when you have a well-defined plan and want autonomous execution with minimal context window consumption.",
|
|
19
|
-
"lines":
|
|
19
|
+
"lines": 411,
|
|
20
20
|
"hasScripts": false,
|
|
21
21
|
"hasReferences": true,
|
|
22
22
|
"hasAssets": false,
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"name": "rig",
|
|
67
67
|
"directory": "rig",
|
|
68
68
|
"description": "'Idempotently bootstrap any repository with standard development tools, hooks, and workflows. Use when starting work on a new repo, onboarding to an existing project, or ensuring a repo has proper CI/CD setup. Configures: git hooks (lefthook), commit message templates, PR templates, and GitHub Actions for lint/format/type-check/build. Prompts for user confirmation before changes. Triggers: \"bootstrap repo\", \"setup hooks\", \"configure CI\", \"rig\", \"standardize repo\".'",
|
|
69
|
-
"lines":
|
|
69
|
+
"lines": 363,
|
|
70
70
|
"hasScripts": false,
|
|
71
71
|
"hasReferences": true,
|
|
72
72
|
"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": 495,
|
|
80
80
|
"hasScripts": true,
|
|
81
81
|
"hasReferences": true,
|
|
82
82
|
"hasAssets": false,
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"name": "speccy",
|
|
87
87
|
"directory": "speccy",
|
|
88
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":
|
|
89
|
+
"lines": 272,
|
|
90
90
|
"hasScripts": false,
|
|
91
91
|
"hasReferences": true,
|
|
92
92
|
"hasAssets": false,
|
package/skills/rig/SKILL.md
CHANGED
|
@@ -293,6 +293,26 @@ If "Let me choose", present individual options as multi-select.
|
|
|
293
293
|
For each approved item, follow the procedures in
|
|
294
294
|
`references/configuration-steps.md`.
|
|
295
295
|
|
|
296
|
+
### Branch Discipline in CLAUDE.md
|
|
297
|
+
|
|
298
|
+
If the project has an existing `CLAUDE.md`:
|
|
299
|
+
|
|
300
|
+
1. Check if `## Branch Discipline` already exists:
|
|
301
|
+
```bash
|
|
302
|
+
grep -q "## Branch Discipline" CLAUDE.md
|
|
303
|
+
```
|
|
304
|
+
2. If NOT found, inject the Branch Discipline section before `## Guardrails`:
|
|
305
|
+
- Read the file content
|
|
306
|
+
- Find the line containing `## Guardrails`
|
|
307
|
+
- Insert the Branch Discipline section immediately before it
|
|
308
|
+
- If no `## Guardrails` section exists, append at the end of the file
|
|
309
|
+
3. If already present, skip (idempotent)
|
|
310
|
+
|
|
311
|
+
The Branch Discipline content to inject is the `## Branch Discipline` section
|
|
312
|
+
from `skills/brace/references/claude-md-template.md`. Read that file to get
|
|
313
|
+
the exact content — this avoids duplication and ensures both /brace and /rig
|
|
314
|
+
inject identical text.
|
|
315
|
+
|
|
296
316
|
---
|
|
297
317
|
|
|
298
318
|
## Phase 5: Verification
|
package/skills/ship/SKILL.md
CHANGED
|
@@ -318,16 +318,51 @@ The fix subagent MUST commit and push before returning. Once it returns,
|
|
|
318
318
|
attempt = 0
|
|
319
319
|
while attempt < 2:
|
|
320
320
|
CHECKS = run_watch()
|
|
321
|
-
if CHECKS.status == "all_passed"
|
|
321
|
+
if CHECKS.status == "all_passed":
|
|
322
322
|
break → proceed immediately to Stage 5 (do NOT ask user to confirm merge)
|
|
323
|
+
if CHECKS.status == "no_checks":
|
|
324
|
+
→ prompt user via AskUserQuestion:
|
|
325
|
+
"No CI checks found for PR #{PR_NUMBER} after waiting 30 seconds.
|
|
326
|
+
The repository may not have CI configured, or checks may still be registering."
|
|
327
|
+
Options:
|
|
328
|
+
- "Merge without checks (Recommended)" → break to Stage 5
|
|
329
|
+
- "Wait another 30 seconds" → re-run the watch (do not increment attempt)
|
|
330
|
+
- "Cancel" → stop /ship and display failure banner
|
|
331
|
+
break (if user chose merge or after re-wait resolves)
|
|
323
332
|
attempt += 1
|
|
324
333
|
run_fix(CHECKS.failing_checks)
|
|
325
334
|
→ loop back to watch
|
|
326
335
|
|
|
327
336
|
if attempt == 2 and still failing:
|
|
328
|
-
|
|
337
|
+
→ display failure banner and stop (see Failure Handling below)
|
|
329
338
|
```
|
|
330
339
|
|
|
340
|
+
### Failure Handling
|
|
341
|
+
|
|
342
|
+
When /ship fails at ANY point (CI exhausts fix attempts, merge fails, post-merge
|
|
343
|
+
verification fails), display the failure banner and STOP:
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
┌─ Ship · FAILED ──────────────────────────────────
|
|
347
|
+
│
|
|
348
|
+
│ ❌ PR #{PR_NUMBER} was NOT merged
|
|
349
|
+
│
|
|
350
|
+
│ Reason: {specific failure reason}
|
|
351
|
+
│ Branch: {BRANCH} (still active)
|
|
352
|
+
│
|
|
353
|
+
│ ⚠️ You are still on branch '{BRANCH}'.
|
|
354
|
+
│ Run /sync to return to main before starting new work.
|
|
355
|
+
│
|
|
356
|
+
└───────────────────────────────────────────────────
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Critical rules on failure:**
|
|
360
|
+
- Do NOT proceed to "What's Next?"
|
|
361
|
+
- Do NOT suggest next tasks or follow-up work
|
|
362
|
+
- Do NOT invoke `/sync` or any other skill
|
|
363
|
+
- Do NOT use language like "will be auto-merged" or "PR is pending"
|
|
364
|
+
- The failure banner is the LAST output — nothing follows it
|
|
365
|
+
|
|
331
366
|
---
|
|
332
367
|
|
|
333
368
|
## Stage 5: Merge & Final Sync
|
|
@@ -352,6 +387,34 @@ bash "$SKILL_ROOT/skills/ship/scripts/merge.sh" \
|
|
|
352
387
|
|
|
353
388
|
Parse LAND_REPORT from output markers. Exit code 0=merged, 1=failed.
|
|
354
389
|
|
|
390
|
+
### 5a-verify. Verify merge completed
|
|
391
|
+
|
|
392
|
+
After merge.sh reports success, verify the PR actually merged:
|
|
393
|
+
|
|
394
|
+
**GitHub:**
|
|
395
|
+
```bash
|
|
396
|
+
PR_STATE=$(gh pr view "$PR_NUMBER" --json state --jq '.state')
|
|
397
|
+
```
|
|
398
|
+
Expected: `"MERGED"`
|
|
399
|
+
|
|
400
|
+
**AzDO CLI:**
|
|
401
|
+
```bash
|
|
402
|
+
PR_STATE=$(az repos pr show --id "$PR_NUMBER" --query status -o tsv)
|
|
403
|
+
```
|
|
404
|
+
Expected: `"completed"`
|
|
405
|
+
|
|
406
|
+
**AzDO REST:**
|
|
407
|
+
```bash
|
|
408
|
+
PR_RESP=$(curl -s -H "$AUTH" "${AZDO_ORG_URL}/${AZDO_PROJECT_URL_SAFE}/_apis/git/repositories/${REPO_ID}/pullRequests/${PR_NUMBER}?api-version=7.1")
|
|
409
|
+
PR_STATE=$(echo "$PR_RESP" | jq -r '.status')
|
|
410
|
+
```
|
|
411
|
+
Expected: `"completed"`
|
|
412
|
+
|
|
413
|
+
If the PR is NOT in the expected merged/completed state, treat as a failure —
|
|
414
|
+
display the failure banner (see Failure Handling) with reason "PR merge was
|
|
415
|
+
accepted but PR is still in '{PR_STATE}' state. The merge may be queued or
|
|
416
|
+
deferred." and stop.
|
|
417
|
+
|
|
355
418
|
### 5b. Sync local repo
|
|
356
419
|
|
|
357
420
|
After the merge script succeeds, run the sync script to checkout the default
|
|
@@ -361,10 +424,23 @@ branch, pull the merge commit, and **clean up stale branches**:
|
|
|
361
424
|
bash "$SKILL_ROOT/skills/sync/scripts/sync.sh" "{REMOTE}" "{DEFAULT_BRANCH}"
|
|
362
425
|
```
|
|
363
426
|
|
|
427
|
+
After sync completes, verify the working tree is on the default branch:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
CURRENT=$(git branch --show-current)
|
|
431
|
+
if [ "$CURRENT" != "{DEFAULT_BRANCH}" ]; then
|
|
432
|
+
git checkout {DEFAULT_BRANCH}
|
|
433
|
+
git pull {REMOTE} {DEFAULT_BRANCH}
|
|
434
|
+
fi
|
|
435
|
+
```
|
|
436
|
+
|
|
364
437
|
---
|
|
365
438
|
|
|
366
439
|
## What's Next?
|
|
367
440
|
|
|
441
|
+
**Only run this section if /ship succeeded (PR is merged).** If any failure
|
|
442
|
+
occurred, the failure banner was already displayed and nothing should follow it.
|
|
443
|
+
|
|
368
444
|
After a successful merge, determine what work comes next by checking these
|
|
369
445
|
sources (in priority order):
|
|
370
446
|
|
|
@@ -393,6 +469,7 @@ Compile all stage reports into a summary:
|
|
|
393
469
|
│ 🌿 Branch: {branch}
|
|
394
470
|
│ 🔗 PR: {pr_url}
|
|
395
471
|
│ 🔀 Merged: {merge_commit} ({merge_type})
|
|
472
|
+
│ 🌿 Now on: {DEFAULT_BRANCH} (up to date)
|
|
396
473
|
│
|
|
397
474
|
│ 📝 Commits
|
|
398
475
|
│ • {commit message 1}
|
|
@@ -258,6 +258,12 @@ FAILING CHECKS: {FAILING_CHECKS}
|
|
|
258
258
|
)"
|
|
259
259
|
git push
|
|
260
260
|
|
|
261
|
+
CRITICAL — after pushing, your job is DONE. Return immediately.
|
|
262
|
+
- Do NOT manually trigger, queue, or run any CI builds or pipelines
|
|
263
|
+
- Do NOT use `az pipelines run`, `gh workflow run`, or any build-triggering command
|
|
264
|
+
- The PR build policy will trigger automatically on push
|
|
265
|
+
- The orchestrator will poll for the new build via ci-watch.sh
|
|
266
|
+
|
|
261
267
|
## Output Format
|
|
262
268
|
|
|
263
269
|
FIX_REPORT:
|
|
@@ -22,12 +22,14 @@ for arg in "$@"; do
|
|
|
22
22
|
done
|
|
23
23
|
|
|
24
24
|
STATUS="" CHECKS="" FAILING=""
|
|
25
|
+
GRACE_POLLS=0
|
|
25
26
|
|
|
26
27
|
emit_report() {
|
|
27
28
|
echo "CHECKS_REPORT_BEGIN"
|
|
28
29
|
echo "status=$STATUS"
|
|
29
30
|
echo "checks=$CHECKS"
|
|
30
31
|
echo "failing_checks=${FAILING:-none}"
|
|
32
|
+
echo "grace_period_polls=$GRACE_POLLS"
|
|
31
33
|
echo "CHECKS_REPORT_END"
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -38,15 +40,29 @@ if [ "$PLATFORM" = "github" ]; then
|
|
|
38
40
|
emit_report; exit 3
|
|
39
41
|
fi
|
|
40
42
|
|
|
43
|
+
# Grace period: wait for checks to register (CI may not trigger immediately)
|
|
44
|
+
GRACE_POLLS=0
|
|
45
|
+
GRACE_JSON="[]"
|
|
46
|
+
for _ in $(seq 1 3); do
|
|
47
|
+
GRACE_POLLS=$((GRACE_POLLS + 1))
|
|
48
|
+
GRACE_JSON=$(gh pr checks "$PR_NUMBER" --json name,state 2>/dev/null || echo "[]")
|
|
49
|
+
if [ "$GRACE_JSON" != "[]" ]; then
|
|
50
|
+
break
|
|
51
|
+
fi
|
|
52
|
+
sleep 10
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
# If no checks found after grace period, report and exit
|
|
56
|
+
if [ "$GRACE_JSON" = "[]" ]; then
|
|
57
|
+
STATUS="no_checks"; CHECKS=""; FAILING="none"
|
|
58
|
+
emit_report; exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
41
61
|
# gh pr checks --watch blocks until done; --fail-fast stops on first failure
|
|
42
62
|
gh pr checks "$PR_NUMBER" --watch --fail-fast 2>/dev/null || true
|
|
43
63
|
|
|
44
64
|
# Parse final status
|
|
45
65
|
CHECKS_JSON=$(gh pr checks "$PR_NUMBER" --json name,state 2>/dev/null || echo "[]")
|
|
46
|
-
if [ "$CHECKS_JSON" = "[]" ]; then
|
|
47
|
-
STATUS="no_checks"; CHECKS=""; FAILING="none"
|
|
48
|
-
emit_report; exit 0
|
|
49
|
-
fi
|
|
50
66
|
|
|
51
67
|
FAIL_COUNT=$(echo "$CHECKS_JSON" | jq '[.[] | select(.state=="FAILURE")] | length')
|
|
52
68
|
CHECKS=$(echo "$CHECKS_JSON" | jq -r '.[] | "\(.name):\(.state | ascii_downcase)"' | paste -sd, -)
|
|
@@ -77,9 +93,11 @@ fi
|
|
|
77
93
|
|
|
78
94
|
# ── AzDO CLI mode ──────────────────────────────────────
|
|
79
95
|
if [ "$AZDO_MODE" = "cli" ]; then
|
|
80
|
-
#
|
|
96
|
+
# Grace period: wait for CI to start (max 2 min)
|
|
81
97
|
RUNS_FOUND=false
|
|
98
|
+
GRACE_POLLS=0
|
|
82
99
|
for _ in $(seq 1 8); do
|
|
100
|
+
GRACE_POLLS=$((GRACE_POLLS + 1))
|
|
83
101
|
RUN_COUNT=$(az pipelines runs list --branch "$BRANCH" --top 5 \
|
|
84
102
|
--org "$AZDO_ORG_URL" --project "$AZDO_PROJECT" \
|
|
85
103
|
--query "length(@)" -o tsv 2>/dev/null)
|
|
@@ -147,9 +165,11 @@ fi
|
|
|
147
165
|
if [ "$AZDO_MODE" = "rest" ]; then
|
|
148
166
|
BUILDS_URL="$AZDO_ORG_URL/$AZDO_PROJECT_URL_SAFE/_apis/build/builds?branchName=refs/heads/$BRANCH&\$top=5&api-version=7.0"
|
|
149
167
|
|
|
150
|
-
#
|
|
168
|
+
# Grace period: wait for CI to start (max 2 min)
|
|
151
169
|
RUNS_FOUND=false
|
|
170
|
+
GRACE_POLLS=0
|
|
152
171
|
for _ in $(seq 1 8); do
|
|
172
|
+
GRACE_POLLS=$((GRACE_POLLS + 1))
|
|
153
173
|
RUN_COUNT=$(curl -s -H "$AUTH" "$BUILDS_URL" | jq '.value | length')
|
|
154
174
|
if [ -n "$RUN_COUNT" ] && [ "$RUN_COUNT" != "0" ]; then
|
|
155
175
|
RUNS_FOUND=true; break
|
|
@@ -65,16 +65,34 @@ DELETE_FLAG=$( [ "$DELETE_BRANCH" = true ] && echo "true" || echo "false" )
|
|
|
65
65
|
|
|
66
66
|
# ── AzDO CLI mode ──────────────────────────────────────
|
|
67
67
|
if [ "$AZDO_MODE" = "cli" ]; then
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
--
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
# Wait for all policies to reach terminal state (approved/rejected/notApplicable)
|
|
69
|
+
POLICY_TIMEOUT=20 # 20 iterations × 15 seconds = 5 minutes
|
|
70
|
+
for POLICY_ITER in $(seq 1 $POLICY_TIMEOUT); do
|
|
71
|
+
POLICY_JSON=$(az repos pr policy list --id "$PR_NUMBER" --org "$AZDO_ORG_URL" --project "$AZDO_PROJECT" -o json 2>/dev/null || echo "[]")
|
|
72
|
+
|
|
73
|
+
REJECTED=$(echo "$POLICY_JSON" | jq '[.[] | select(.status=="rejected")] | length')
|
|
74
|
+
PENDING=$(echo "$POLICY_JSON" | jq '[.[] | select(.status=="running" or .status=="queued" or .status=="pending")] | length')
|
|
75
|
+
|
|
76
|
+
if [ "${REJECTED:-0}" -gt 0 ]; then
|
|
77
|
+
STATUS="failed"
|
|
78
|
+
ERRORS="policies rejected"
|
|
79
|
+
MERGE_COMMIT=""; BRANCH_DELETED=false
|
|
80
|
+
emit_report; exit 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [ "${PENDING:-0}" -eq 0 ]; then
|
|
84
|
+
break # All policies terminal
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if [ "$POLICY_ITER" -eq "$POLICY_TIMEOUT" ]; then
|
|
88
|
+
STATUS="failed"
|
|
89
|
+
ERRORS="policies not evaluated after 5 minutes"
|
|
90
|
+
MERGE_COMMIT=""; BRANCH_DELETED=false
|
|
91
|
+
emit_report; exit 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
sleep 15
|
|
95
|
+
done
|
|
78
96
|
|
|
79
97
|
# Complete the PR
|
|
80
98
|
if az repos pr update --id "$PR_NUMBER" --status completed \
|
|
@@ -111,16 +129,35 @@ if [ "$AZDO_MODE" = "rest" ]; then
|
|
|
111
129
|
REPO_NAME=$(basename -s .git "$(git remote get-url origin)")
|
|
112
130
|
PR_API="$AZDO_ORG_URL/$AZDO_PROJECT_URL_SAFE/_apis/git/repositories/$REPO_NAME/pullrequests/$PR_NUMBER"
|
|
113
131
|
|
|
114
|
-
#
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
# Wait for all policy evaluations to reach terminal state
|
|
133
|
+
POLICY_TIMEOUT=20
|
|
134
|
+
for POLICY_ITER in $(seq 1 $POLICY_TIMEOUT); do
|
|
135
|
+
EVALS=$(curl -s -H "$AUTH" \
|
|
136
|
+
"$AZDO_ORG_URL/$AZDO_PROJECT_URL_SAFE/_apis/policy/evaluations?artifactId=vstfs:///CodeReview/CodeReviewId/$AZDO_PROJECT_URL_SAFE/$PR_NUMBER&api-version=7.0" 2>/dev/null || echo '{"value":[]}')
|
|
137
|
+
|
|
138
|
+
REJECTED=$(echo "$EVALS" | jq '[.value[] | select(.status=="rejected")] | length')
|
|
139
|
+
PENDING=$(echo "$EVALS" | jq '[.value[] | select(.status=="running" or .status=="queued" or .status=="pending")] | length')
|
|
140
|
+
|
|
141
|
+
if [ "${REJECTED:-0}" -gt 0 ]; then
|
|
142
|
+
STATUS="failed"
|
|
143
|
+
ERRORS="PR has rejected policy evaluations"
|
|
144
|
+
MERGE_COMMIT=""; BRANCH_DELETED=false
|
|
145
|
+
emit_report; exit 1
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
if [ "${PENDING:-0}" -eq 0 ]; then
|
|
149
|
+
break
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
if [ "$POLICY_ITER" -eq "$POLICY_TIMEOUT" ]; then
|
|
153
|
+
STATUS="failed"
|
|
154
|
+
ERRORS="policies not evaluated after 5 minutes"
|
|
155
|
+
MERGE_COMMIT=""; BRANCH_DELETED=false
|
|
156
|
+
emit_report; exit 1
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
sleep 15
|
|
160
|
+
done
|
|
124
161
|
|
|
125
162
|
# Resolve merge strategy
|
|
126
163
|
MERGE_STRATEGY=$( [ "$SQUASH" = true ] && echo "squash" || echo "noFastForward" )
|
package/skills/speccy/SKILL.md
CHANGED
|
@@ -84,6 +84,25 @@ For each row, in order:
|
|
|
84
84
|
|
|
85
85
|
## Stage 1: Context Gathering
|
|
86
86
|
|
|
87
|
+
### Pre-Spec Branch Check
|
|
88
|
+
|
|
89
|
+
Before gathering context, check if the user is on a stale branch:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
CURRENT=$(git branch --show-current)
|
|
93
|
+
if [ "$CURRENT" != "main" ] && [ "$CURRENT" != "master" ]; then
|
|
94
|
+
git fetch origin main --quiet 2>/dev/null
|
|
95
|
+
BEHIND=$(git rev-list --count HEAD..origin/main 2>/dev/null || echo 0)
|
|
96
|
+
if [ "$BEHIND" -gt 5 ]; then
|
|
97
|
+
echo "⚠️ Branch '$CURRENT' is $BEHIND commits behind main."
|
|
98
|
+
echo " Consider running /sync before building from this spec."
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This is advisory only (specs don't modify code) — do not block, continue
|
|
104
|
+
regardless of the result.
|
|
105
|
+
|
|
87
106
|
Before asking any questions, build a thorough understanding of the project:
|
|
88
107
|
|
|
89
108
|
1. **Capture GOAL** — the user's argument describing what needs to be specified
|