@slamb2k/mad-skills 2.0.47 → 2.0.49
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/hooks/lib/config.cjs +0 -3
- package/hooks/lib/plugin-health.cjs +0 -31
- package/package.json +1 -1
- package/skills/brace/SKILL.md +11 -14
- package/skills/brace/assets/global-preferences-template.md +5 -2
- package/skills/brace/references/claude-md-template.md +4 -3
- package/skills/brace/references/phase-prompts.md +24 -37
- package/skills/brace/references/plugin-tuning-steps.md +0 -22
- package/skills/brace/references/report-template.md +1 -1
- package/skills/brace/references/scaffold-manifest.md +1 -1
- package/skills/dock/references/pipeline-templates.md +21 -5
- package/skills/launch/SKILL.md +355 -0
- package/skills/manifest.json +13 -3
- package/skills/ship/scripts/merge.sh +21 -19
package/hooks/lib/config.cjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const { join } = require('path');
|
|
4
4
|
const { homedir } = require('os');
|
|
5
|
-
const { readdirSync } = require('fs');
|
|
6
5
|
const config = require('./config.cjs');
|
|
7
6
|
const state = require('./state.cjs');
|
|
8
7
|
const { readJson } = require('./utils.cjs');
|
|
@@ -11,7 +10,6 @@ const { readJson } = require('./utils.cjs');
|
|
|
11
10
|
* Plugin Health — detect performance anti-patterns in companion plugins.
|
|
12
11
|
*
|
|
13
12
|
* Checks:
|
|
14
|
-
* - Hookify enabled with no rules (pure overhead)
|
|
15
13
|
* - claude-mem missing SKIP_TOOLS for read-only tools
|
|
16
14
|
* - claude-mem context injection too high (when OMC also active)
|
|
17
15
|
*
|
|
@@ -27,38 +25,9 @@ function checkPluginHealth(projectDir, output) {
|
|
|
27
25
|
|
|
28
26
|
const plugins = settings.enabledPlugins || {};
|
|
29
27
|
|
|
30
|
-
checkHookify(projectDir, plugins, output);
|
|
31
28
|
checkClaudeMem(plugins, output);
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
// ─── hookify ──────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
function checkHookify(projectDir, plugins, output) {
|
|
37
|
-
const hookifyKey = Object.keys(plugins).find(k => k.includes('hookify'));
|
|
38
|
-
if (!hookifyKey || plugins[hookifyKey] !== true) return; // Not enabled
|
|
39
|
-
|
|
40
|
-
// Count rule files in project .claude/ directory
|
|
41
|
-
let ruleCount = 0;
|
|
42
|
-
try {
|
|
43
|
-
const projectClaudeDir = join(projectDir, '.claude');
|
|
44
|
-
const files = readdirSync(projectClaudeDir);
|
|
45
|
-
ruleCount = files.filter(f => config.pluginHealth.hookify.rulePattern.test(f)).length;
|
|
46
|
-
} catch { /* directory doesn't exist — zero rules */ }
|
|
47
|
-
|
|
48
|
-
// Also check global .claude/ directory
|
|
49
|
-
try {
|
|
50
|
-
const globalClaudeDir = join(homedir(), '.claude');
|
|
51
|
-
const files = readdirSync(globalClaudeDir);
|
|
52
|
-
ruleCount += files.filter(f => config.pluginHealth.hookify.rulePattern.test(f)).length;
|
|
53
|
-
} catch { /* noop */ }
|
|
54
|
-
|
|
55
|
-
if (ruleCount === 0) {
|
|
56
|
-
output.signals.push(
|
|
57
|
-
'\u26A0 Hookify enabled but no rules configured \u2014 fires on every tool call for nothing. Run /brace or disable in settings',
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
31
|
// ─── claude-mem ───────────────────────────────────────────────────────
|
|
63
32
|
|
|
64
33
|
function checkClaudeMem(plugins, output) {
|
package/package.json
CHANGED
package/skills/brace/SKILL.md
CHANGED
|
@@ -102,7 +102,7 @@ For each row, in order:
|
|
|
102
102
|
**Plugin detection:** For plugin dependencies (Type = plugin), check
|
|
103
103
|
`~/.claude/settings.json` → `enabledPlugins` for a key containing the plugin
|
|
104
104
|
name set to `true`. Store results as `PLUGIN_STATE` (`claude_mem_installed`,
|
|
105
|
-
`omc_installed
|
|
105
|
+
`omc_installed`) for use in Phase 4 and Phase 7.
|
|
106
106
|
|
|
107
107
|
1. Capture **FLAGS** from the user's request
|
|
108
108
|
|
|
@@ -170,16 +170,15 @@ Store result as `upgrade_legacy: true|false` in USER_CONFIG.
|
|
|
170
170
|
|
|
171
171
|
4. Ask installation level via AskUserQuestion:
|
|
172
172
|
|
|
173
|
-
Question: "
|
|
173
|
+
Question: "Install universal guidance (preferences, principles) at user level?"
|
|
174
174
|
Options:
|
|
175
|
-
- "
|
|
176
|
-
- "
|
|
177
|
-
- "Project level only" → self-contained, portable
|
|
175
|
+
- "Yes, install globally (Recommended)" — applies to all projects via `~/.claude/CLAUDE.md`
|
|
176
|
+
- "No, project level only" — self-contained in this project's CLAUDE.md
|
|
178
177
|
|
|
179
178
|
5. Store as USER_CONFIG:
|
|
180
179
|
- project_name: from directory name or user override
|
|
181
180
|
- description: from user input
|
|
182
|
-
- install_level: "
|
|
181
|
+
- install_level: "global" | "project"
|
|
183
182
|
|
|
184
183
|
**If cancelled, stop here.**
|
|
185
184
|
|
|
@@ -220,7 +219,7 @@ Will upgrade: {list of items with status "upgrade"}
|
|
|
220
219
|
Will remove: {list of items with status "remove" — legacy cleanup}
|
|
221
220
|
Will clean: {list of items with status "cleanup" — remove legacy references}
|
|
222
221
|
Will skip: {count} existing items
|
|
223
|
-
|
|
222
|
+
Universal guidance: {install_level description}
|
|
224
223
|
|
|
225
224
|
Proceed?
|
|
226
225
|
```
|
|
@@ -247,7 +246,7 @@ Before sending the prompt, substitute these variables:
|
|
|
247
246
|
- `{ACTION_PLAN}` — the action plan from Phase 3
|
|
248
247
|
- `{PROJECT_NAME}` — from USER_CONFIG
|
|
249
248
|
- `{PROJECT_DESCRIPTION}` — from USER_CONFIG
|
|
250
|
-
- `{INSTALL_LEVEL}` — from USER_CONFIG ("
|
|
249
|
+
- `{INSTALL_LEVEL}` — from USER_CONFIG ("global" or "project")
|
|
251
250
|
- `{CLAUDE_MD_TEMPLATE}` — read from `references/claude-md-template.md`
|
|
252
251
|
(the section between BEGIN TEMPLATE and END TEMPLATE)
|
|
253
252
|
- `{GITIGNORE_CONTENT}` — read from `assets/gitignore-template`
|
|
@@ -334,10 +333,10 @@ This phase modifies user-level settings files (`~/.claude/settings.json`,
|
|
|
334
333
|
If `--force` is set, apply all recommendations without prompting.
|
|
335
334
|
|
|
336
335
|
**Plugin presence guards:** Only audit plugins detected as installed during
|
|
337
|
-
pre-flight (`PLUGIN_STATE`). Skip
|
|
338
|
-
if
|
|
339
|
-
|
|
340
|
-
|
|
336
|
+
pre-flight (`PLUGIN_STATE`). Skip M1/M2/M3 if claude-mem is absent. Skip M2
|
|
337
|
+
if OMC is absent (M2 requires both). The `{PLUGIN_ROLE_SEPARATION}` content
|
|
338
|
+
in CLAUDE.md is only injected when both claude-mem and OMC are confirmed
|
|
339
|
+
enabled.
|
|
341
340
|
|
|
342
341
|
If no companion plugins are installed at all, output:
|
|
343
342
|
```
|
|
@@ -383,8 +382,6 @@ If "Let me choose", present individual findings as multi-select.
|
|
|
383
382
|
|
|
384
383
|
| Code | Check | Condition | Severity |
|
|
385
384
|
|------|-------|-----------|----------|
|
|
386
|
-
| H1 | Hookify: no rules | enabled + zero `hookify.*.local.md` files | high |
|
|
387
|
-
| H2 | Hookify: python3 broken | enabled + `python3 --version` fails | high |
|
|
388
385
|
| M1 | claude-mem: read-only tools | SKIP_TOOLS missing Read/Glob/Grep/ToolSearch/Agent/WebSearch/WebFetch | medium |
|
|
389
386
|
| M2 | claude-mem: high context | observations > 10 or sessions > 3, AND OMC also enabled | medium |
|
|
390
387
|
| M3 | claude-mem: provider=claude | provider is "claude" (SDK spawn known-broken) | low |
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# Global Preferences & Universal Principles
|
|
2
2
|
|
|
3
3
|
Template appended to `~/.claude/CLAUDE.md` by brace when the user
|
|
4
|
-
selects "global"
|
|
5
|
-
this
|
|
4
|
+
selects "global" install level. When the user selects "project" level,
|
|
5
|
+
this file is not used — instead, the universal principles are written
|
|
6
|
+
to the project CLAUDE.md (with a redundancy check against global).
|
|
7
|
+
The Phase 4 agent inserts this content before the "## Current Skills"
|
|
8
|
+
section.
|
|
6
9
|
|
|
7
10
|
---
|
|
8
11
|
|
|
@@ -4,9 +4,10 @@ Template for the generated project CLAUDE.md. The Phase 4 agent substitutes
|
|
|
4
4
|
`{VARIABLE}` placeholders and writes to the project root.
|
|
5
5
|
|
|
6
6
|
`{UNIVERSAL_PRINCIPLES}` is populated with the Question & Assumption
|
|
7
|
-
Accountability
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Accountability section when install_level is "project" AND those sections
|
|
8
|
+
are not already present in `~/.claude/CLAUDE.md`. Left empty when
|
|
9
|
+
install_level is "global" (principles are in the global config instead)
|
|
10
|
+
or when the sections would be redundant with existing global content.
|
|
10
11
|
|
|
11
12
|
---
|
|
12
13
|
|
|
@@ -146,21 +146,31 @@ skills-based workflow while preserving all other content.
|
|
|
146
146
|
This project uses claude-mem for persistent cross-session memory.
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
### Global preferences
|
|
149
|
+
### Global preferences and universal principles
|
|
150
150
|
|
|
151
|
-
If INSTALL_LEVEL is "global"
|
|
151
|
+
**If INSTALL_LEVEL is "global":**
|
|
152
152
|
- Read ~/.claude/CLAUDE.md
|
|
153
153
|
- If it does NOT contain "## Global Preferences", insert the Global
|
|
154
154
|
Preferences Content (below) immediately before "## Current Skills"
|
|
155
155
|
- If it already contains "## Global Preferences", skip (idempotent)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
If INSTALL_LEVEL is "project"
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
- Substitute {UNIVERSAL_PRINCIPLES} in the project CLAUDE.md template with
|
|
157
|
+
an empty string (principles are in the global config instead).
|
|
158
|
+
|
|
159
|
+
**If INSTALL_LEVEL is "project":**
|
|
160
|
+
- Do NOT modify ~/.claude/CLAUDE.md.
|
|
161
|
+
- Before writing universal principles into the project CLAUDE.md, check
|
|
162
|
+
~/.claude/CLAUDE.md for existing equivalent sections (redundancy guard):
|
|
163
|
+
- If global contains "## Global Preferences" → SKIP that section in
|
|
164
|
+
project CLAUDE.md. Add to SCAFFOLD_REPORT.skipped_redundant.
|
|
165
|
+
- If global contains "## Universal Operating Principles" → SKIP that
|
|
166
|
+
section in project CLAUDE.md. Add to SCAFFOLD_REPORT.skipped_redundant.
|
|
167
|
+
- For any sections NOT found in global, substitute {UNIVERSAL_PRINCIPLES}
|
|
168
|
+
in the project CLAUDE.md template with only the non-redundant sections
|
|
169
|
+
from the Universal Principles Content below.
|
|
170
|
+
- If ALL sections are redundant, substitute {UNIVERSAL_PRINCIPLES} with
|
|
171
|
+
an empty string.
|
|
172
|
+
- Report each skipped section to the user:
|
|
173
|
+
"⏭️ Skipped {section} in project CLAUDE.md — already present in ~/.claude/CLAUDE.md"
|
|
164
174
|
|
|
165
175
|
### CLAUDE.md Template
|
|
166
176
|
|
|
@@ -206,6 +216,7 @@ SCAFFOLD_REPORT:
|
|
|
206
216
|
cleaned: [list of files cleaned of legacy references]
|
|
207
217
|
preserved_content: [any key decisions extracted from memory/MEMORY.md, or empty]
|
|
208
218
|
skipped: [list of items skipped]
|
|
219
|
+
skipped_redundant: [sections skipped in project CLAUDE.md because already in global, or empty]
|
|
209
220
|
global_updated: true|false|skipped
|
|
210
221
|
errors: [any errors encountered]
|
|
211
222
|
```
|
|
@@ -286,39 +297,19 @@ Limit PLUGIN_REPORT to 30 lines maximum.
|
|
|
286
297
|
node -e "
|
|
287
298
|
const s = JSON.parse(require('fs').readFileSync('$HOME/.claude/settings.json','utf8'));
|
|
288
299
|
const p = s.enabledPlugins || {};
|
|
289
|
-
const hookify = Object.keys(p).find(k => k.includes('hookify'));
|
|
290
300
|
const mem = Object.keys(p).find(k => k.includes('claude-mem'));
|
|
291
301
|
const omc = Object.keys(p).find(k => k.includes('oh-my-claudecode'));
|
|
292
|
-
console.log('hookify_installed:' + !!hookify);
|
|
293
|
-
console.log('hookify_enabled:' + (hookify && p[hookify] === true));
|
|
294
302
|
console.log('claude_mem_installed:' + !!mem);
|
|
295
303
|
console.log('claude_mem_enabled:' + (mem && p[mem] === true));
|
|
296
304
|
console.log('omc_installed:' + !!omc);
|
|
297
305
|
console.log('omc_enabled:' + (omc && p[omc] === true));
|
|
298
306
|
"
|
|
299
|
-
Record:
|
|
307
|
+
Record: claude_mem_installed/enabled, omc_installed/enabled
|
|
300
308
|
|
|
301
309
|
If a plugin is not installed (not in enabledPlugins at all), skip its
|
|
302
310
|
entire audit section below and report all its fields as "N/A".
|
|
303
311
|
|
|
304
|
-
2. **
|
|
305
|
-
a. Check python3 availability:
|
|
306
|
-
python3 --version >/dev/null 2>&1 && echo "python3_available:true" || echo "python3_available:false"
|
|
307
|
-
Record: python3_available
|
|
308
|
-
|
|
309
|
-
b. Count hookify rule files:
|
|
310
|
-
RULE_COUNT=0
|
|
311
|
-
for f in "$HOME/.claude"/hookify.*.local.md .claude/hookify.*.local.md; do
|
|
312
|
-
[ -f "$f" ] && RULE_COUNT=$((RULE_COUNT + 1))
|
|
313
|
-
done
|
|
314
|
-
echo "hookify_rule_count:$RULE_COUNT"
|
|
315
|
-
Record: hookify_rule_count
|
|
316
|
-
|
|
317
|
-
c. Determine findings:
|
|
318
|
-
- If python3_available == false → finding H2
|
|
319
|
-
- If hookify_rule_count == 0 → finding H1
|
|
320
|
-
|
|
321
|
-
3. **claude-mem audit** (only if claude_mem_enabled == true)
|
|
312
|
+
2. **claude-mem audit** (only if claude_mem_enabled == true)
|
|
322
313
|
MEM_SETTINGS="$HOME/.claude-mem/settings.json"
|
|
323
314
|
if [ -f "$MEM_SETTINGS" ]; then
|
|
324
315
|
node -e "
|
|
@@ -349,10 +340,6 @@ Limit PLUGIN_REPORT to 30 lines maximum.
|
|
|
349
340
|
|
|
350
341
|
PLUGIN_REPORT:
|
|
351
342
|
settings_file_found: true|false
|
|
352
|
-
hookify_installed: true|false
|
|
353
|
-
hookify_enabled: true|false
|
|
354
|
-
hookify_python3_available: true|false|N/A
|
|
355
|
-
hookify_rule_count: {number}|N/A
|
|
356
343
|
claude_mem_installed: true|false
|
|
357
344
|
claude_mem_enabled: true|false
|
|
358
345
|
claude_mem_skip_tools: {current value}|N/A
|
|
@@ -363,5 +350,5 @@ PLUGIN_REPORT:
|
|
|
363
350
|
claude_mem_has_openrouter_key: true|false|N/A
|
|
364
351
|
omc_installed: true|false
|
|
365
352
|
omc_enabled: true|false
|
|
366
|
-
findings: {comma-separated list of
|
|
353
|
+
findings: {comma-separated list of M1,M2,M3 or "none"}
|
|
367
354
|
```
|
|
@@ -6,28 +6,6 @@ checks current value before modifying.
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## H1/H2: Disable Hookify
|
|
10
|
-
|
|
11
|
-
Target: `~/.claude/settings.json`
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
node -e "
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const p = require('os').homedir() + '/.claude/settings.json';
|
|
17
|
-
const s = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
18
|
-
const key = Object.keys(s.enabledPlugins || {}).find(k => k.includes('hookify'));
|
|
19
|
-
if (key && s.enabledPlugins[key] !== false) {
|
|
20
|
-
s.enabledPlugins[key] = false;
|
|
21
|
-
fs.writeFileSync(p, JSON.stringify(s, null, 2) + '\n');
|
|
22
|
-
console.log('Disabled hookify');
|
|
23
|
-
} else {
|
|
24
|
-
console.log('Hookify already disabled — skipped');
|
|
25
|
-
}
|
|
26
|
-
"
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
9
|
## M1: Add Read-Only Tools to SKIP_TOOLS
|
|
32
10
|
|
|
33
11
|
Target: `~/.claude-mem/settings.json`
|
|
@@ -19,6 +19,7 @@ Present this summary after verification completes.
|
|
|
19
19
|
│
|
|
20
20
|
│ 📄 Files
|
|
21
21
|
│ {✅|⏭️} CLAUDE.md
|
|
22
|
+
│ {✅|⏭️} ~/.claude/CLAUDE.md {updated / already present / skipped (project-only)}
|
|
22
23
|
│ {✅|⏭️} .gitignore
|
|
23
24
|
│
|
|
24
25
|
│ 🗑️ Removed (only if legacy items were cleaned up)
|
|
@@ -27,7 +28,6 @@ Present this summary after verification completes.
|
|
|
27
28
|
│ {✅} memory/ Legacy memory directory
|
|
28
29
|
│
|
|
29
30
|
│ 🔌 Plugin Tuning (only if Phase 7 ran)
|
|
30
|
-
│ {✅|⏭️} Hookify: {disabled / already disabled / not installed / skipped / N/A}
|
|
31
31
|
│ {✅|⏭️} claude-mem SKIP_TOOLS: {optimised / already optimal / not installed / skipped / N/A}
|
|
32
32
|
│ {✅|⏭️} claude-mem context: {reduced / already optimal / not installed / skipped / N/A}
|
|
33
33
|
│ {✅|⏭️} claude-mem provider: {switched / already optimal / not installed / skipped / N/A}
|
|
@@ -9,7 +9,7 @@ creation checklist. Phase 1 uses it to detect existing structure.
|
|
|
9
9
|
|------|--------|-------------|
|
|
10
10
|
| `CLAUDE.md` | references/claude-md-template.md | Project operating document |
|
|
11
11
|
| `.gitignore` | assets/gitignore-template | Standard ignores |
|
|
12
|
-
| `~/.claude/CLAUDE.md` | assets/global-preferences-template.md | Global preferences (
|
|
12
|
+
| `~/.claude/CLAUDE.md` | assets/global-preferences-template.md | Global preferences (when install_level is "global") |
|
|
13
13
|
|
|
14
14
|
## Project Directories
|
|
15
15
|
|
|
@@ -115,9 +115,18 @@ jobs:
|
|
|
115
115
|
|
|
116
116
|
- name: Smoke test
|
|
117
117
|
run: |
|
|
118
|
-
#
|
|
119
|
-
|
|
120
|
-
curl
|
|
118
|
+
# Poll the health endpoint until ready (bounded deadline so a
|
|
119
|
+
# genuinely broken deploy still fails fast). Do not use a blind
|
|
120
|
+
# `sleep N && curl` — it flakes on slow cold starts and wastes
|
|
121
|
+
# time on fast ones.
|
|
122
|
+
deadline=$((SECONDS + 120))
|
|
123
|
+
until curl -fs "$DEV_URL/healthz" >/dev/null; do
|
|
124
|
+
if [ $SECONDS -ge $deadline ]; then
|
|
125
|
+
echo "Health check failed to pass within 120s" >&2
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
sleep 2
|
|
129
|
+
done
|
|
121
130
|
|
|
122
131
|
# ── Promote to Staging (on release tag) ───────────────────────
|
|
123
132
|
promote-staging:
|
|
@@ -173,8 +182,15 @@ jobs:
|
|
|
173
182
|
|
|
174
183
|
- name: Post-deploy smoke test
|
|
175
184
|
run: |
|
|
176
|
-
sleep
|
|
177
|
-
|
|
185
|
+
# Poll until healthy, bounded deadline. Never `sleep N && curl`.
|
|
186
|
+
deadline=$((SECONDS + 120))
|
|
187
|
+
until curl -fs "$PROD_URL/healthz" >/dev/null; do
|
|
188
|
+
if [ $SECONDS -ge $deadline ]; then
|
|
189
|
+
echo "Production health check failed to pass within 120s" >&2
|
|
190
|
+
exit 1
|
|
191
|
+
fi
|
|
192
|
+
sleep 2
|
|
193
|
+
done
|
|
178
194
|
```
|
|
179
195
|
|
|
180
196
|
### Reusable deploy step patterns
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: launch
|
|
3
|
+
description: "Run the full OMC idea-to-merged-PR pipeline — cancel + deep-interview + ralplan + autopilot + mad-skills:ship — in a single invocation. Explicit-only; this skill never auto-activates. Only run when the user literally types /launch. Do not invoke on phrases like \"launch this\", \"ship it\", \"full pipeline\", or similar — none of those should trigger this skill."
|
|
4
|
+
argument-hint: "<rough idea for the feature>"
|
|
5
|
+
allowed-tools: Bash, Read, Skill
|
|
6
|
+
disable-model-invocation: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Launch — Idea → Merged PR, end-to-end
|
|
10
|
+
|
|
11
|
+
When this skill is invoked, IMMEDIATELY output the banner below before doing anything else.
|
|
12
|
+
Pick ONE tagline at random — vary your choice each time.
|
|
13
|
+
CRITICAL: Reproduce the banner EXACTLY character-for-character. The first line of the art has 4 leading characters (one invisible braille-blank + 3 spaces) — you MUST preserve them.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
{tagline}
|
|
17
|
+
|
|
18
|
+
⠀ ██╗██╗ █████╗ ██╗ ██╗███╗ ██╗ ██████╗██╗ ██╗
|
|
19
|
+
██╔╝██║ ██╔══██╗██║ ██║████╗ ██║██╔════╝██║ ██║
|
|
20
|
+
██╔╝ ██║ ███████║██║ ██║██╔██╗ ██║██║ ███████║
|
|
21
|
+
██╔╝ ██║ ██╔══██║██║ ██║██║╚██╗██║██║ ██╔══██║
|
|
22
|
+
██╔╝ ███████╗██║ ██║╚██████╔╝██║ ╚████║╚██████╗██║ ██║
|
|
23
|
+
╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝╚═╝ ╚═╝
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Taglines:
|
|
27
|
+
- 🚀 From idea to orbit!
|
|
28
|
+
- 🛫 Cleared for takeoff!
|
|
29
|
+
- 🎯 Zero-touch launch inbound!
|
|
30
|
+
- 🎆 3, 2, 1… liftoff!
|
|
31
|
+
- 🛰️ Launch sequence initiated!
|
|
32
|
+
- 🏁 One command, full launch!
|
|
33
|
+
- 🤖 Full auto, full send!
|
|
34
|
+
- ⚙️ The whole launchpad!
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Output Formatting
|
|
39
|
+
|
|
40
|
+
After the banner, display parsed input:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
┌─ Input ────────────────────────────────────────
|
|
44
|
+
│ {Field}: {value}
|
|
45
|
+
│ Flags: {parsed flags or "none"}
|
|
46
|
+
└────────────────────────────────────────────────
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Pre-flight results:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
── Pre-flight ───────────────────────────────────
|
|
53
|
+
✅ {dep} {version or "found"}
|
|
54
|
+
⚠️ {dep} not found → {fallback detail}
|
|
55
|
+
❌ {dep} missing → stopping
|
|
56
|
+
──────────────────────────────────────────────────
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Stage headers: `━━ {N} · {Name} ━━━━━━━━━━━━━━━━━━━━━━━━━`
|
|
60
|
+
|
|
61
|
+
Status icons: ✅ done · ❌ failed · ⚠️ degraded · ⏳ working · ⏭️ skipped
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Purpose
|
|
66
|
+
|
|
67
|
+
`/launch` is a thin orchestrator over five existing skills. Its job is to run them in order and keep going between them — nothing more. Each inner skill owns its own stage-level concerns (state, hooks, reviewers, regression). Treat this wrapper as dumb: if any inner stage fails, stop and report — do not attempt retry logic here.
|
|
68
|
+
|
|
69
|
+
The collaboration window is **Stage 2 (deep-interview)**. Everything after is autonomous; any mid-pipeline pauses come from the inner skills surfacing `AskUserQuestion` prompts (e.g. an autopilot Phase 4 reviewer needs a call), not from this wrapper.
|
|
70
|
+
|
|
71
|
+
## Flags
|
|
72
|
+
|
|
73
|
+
Parse optional flags from the request:
|
|
74
|
+
|
|
75
|
+
- `--skip-cancel`: skip the Stage 1 stale-state cleanup (use when you *know* the session is clean)
|
|
76
|
+
- `--skip-interview`: skip Stage 2 if a usable spec already exists at `.omc/specs/deep-interview-*.md`
|
|
77
|
+
- `--pr-only`: stop after autopilot (Stage 4); don't run ship
|
|
78
|
+
- `--no-ship`: alias for `--pr-only`
|
|
79
|
+
- `--critic=architect|critic|codex`: forwarded to ralplan for the consensus reviewer
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Pre-flight
|
|
84
|
+
|
|
85
|
+
Before Stage 1, check all dependencies in this table:
|
|
86
|
+
|
|
87
|
+
| Dependency | Type | Check | Required | Resolution | Detail |
|
|
88
|
+
|---|---|---|---|---|---|
|
|
89
|
+
| git | cli | `git --version` | yes | stop | Install from https://git-scm.com |
|
|
90
|
+
| oh-my-claudecode | plugin | `ls "$HOME/.claude/plugins/cache/omc/oh-my-claudecode"/*/skills/cancel/SKILL.md 2>/dev/null` | yes | stop | OMC plugin not installed. Install via `/plugin add oh-my-claudecode` or follow the OMC installation docs. Without it, deep-interview / ralplan / autopilot / cancel are unavailable. |
|
|
91
|
+
| oh-my-claudecode:cancel | skill | `ls "$HOME/.claude/plugins/cache/omc/oh-my-claudecode"/*/skills/cancel/SKILL.md 2>/dev/null` | yes | stop | OMC cancel skill missing — reinstall OMC |
|
|
92
|
+
| oh-my-claudecode:deep-interview | skill | `ls "$HOME/.claude/plugins/cache/omc/oh-my-claudecode"/*/skills/deep-interview/SKILL.md 2>/dev/null` | yes | stop | OMC deep-interview skill missing — reinstall OMC |
|
|
93
|
+
| oh-my-claudecode:ralplan | skill | `ls "$HOME/.claude/plugins/cache/omc/oh-my-claudecode"/*/skills/ralplan/SKILL.md 2>/dev/null` | yes | stop | OMC ralplan skill missing — reinstall OMC |
|
|
94
|
+
| oh-my-claudecode:autopilot | skill | `ls "$HOME/.claude/plugins/cache/omc/oh-my-claudecode"/*/skills/autopilot/SKILL.md 2>/dev/null` | yes | stop | OMC autopilot skill missing — reinstall OMC |
|
|
95
|
+
| mad-skills:ship | skill | `ls "$HOME/.claude/plugins/marketplaces/slamb2k/skills/ship/SKILL.md" 2>/dev/null` | yes | stop | mad-skills:ship not installed. Skip with `--pr-only` to stop after autopilot. Install with: `npx skills add slamb2k/mad-skills --skill ship` |
|
|
96
|
+
|
|
97
|
+
For each row, in order:
|
|
98
|
+
|
|
99
|
+
1. Skip rows that don't apply (e.g. the `mad-skills:ship` row when `--pr-only` is set — the skill isn't required in that path)
|
|
100
|
+
2. Run the Check command
|
|
101
|
+
3. If found: continue silently
|
|
102
|
+
4. If missing: apply Resolution strategy
|
|
103
|
+
- **stop**: notify user with Detail, halt execution
|
|
104
|
+
5. After all checks: summarize what's available and what's degraded
|
|
105
|
+
|
|
106
|
+
The most important check is the **oh-my-claudecode plugin** — without it, four of the five stages are impossible and the skill must abort. Do not try to proceed with only some OMC skills installed; if the cache directory exists but individual skills are missing, the OMC installation is corrupt and needs reinstalling.
|
|
107
|
+
|
|
108
|
+
**Additional state check (soft warning, not a blocker):** after the dependency table, run `git status --porcelain` once. If there is any output, warn the user that the working tree has uncommitted changes. `deep-interview` and `ralplan` don't touch source, but `autopilot` will. Let the user decide whether to stash / commit / proceed.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Plan Resolution
|
|
113
|
+
|
|
114
|
+
Capture the user's argument as the **IDEA** (the rough feature description). This is what deep-interview will use as its starting input.
|
|
115
|
+
|
|
116
|
+
Display in the Input box as:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
│ Idea: {first 80 chars of idea}
|
|
120
|
+
│ Flags: {parsed flags or "none"}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Stage 1 · Cancel stale state
|
|
126
|
+
|
|
127
|
+
Unless `--skip-cancel` was set, invoke the OMC cancel skill to wipe any orphaned state:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Skill("oh-my-claudecode:cancel", "--force")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Why force:** `/launch` is meant to be a clean-slate starting point. If previous OMC sessions left orphaned `awaiting_confirmation` entries (this is a known rough edge — e.g. the word "autopilot" in a prompt can trigger autopilot state without user consent), those will pollute later stages. `--force` wipes everything so the downstream stages start from zero.
|
|
134
|
+
|
|
135
|
+
After the cancel skill returns, confirm no modes are active before proceeding. Emit a short one-line status:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
✅ state cleared — no active OMC modes
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If cancel reports lingering state it couldn't clear, stop and report. Don't press on.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Stage 2 · Deep-interview (requirements Q&A)
|
|
146
|
+
|
|
147
|
+
Unless `--skip-interview` was set AND a pre-existing spec is found, invoke:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Skill("oh-my-claudecode:deep-interview", "{IDEA}")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Let deep-interview run to completion. It will:
|
|
154
|
+
|
|
155
|
+
- Conduct Socratic Q&A via `AskUserQuestion` — **these pauses surface to the user**, not the wrapper
|
|
156
|
+
- Continue until its ambiguity gate passes (≤ 20%)
|
|
157
|
+
- Write the final spec to `.omc/specs/deep-interview-{slug}.md`
|
|
158
|
+
|
|
159
|
+
**If `--skip-interview` was set:**
|
|
160
|
+
|
|
161
|
+
- Look for any file matching `.omc/specs/deep-interview-*.md`
|
|
162
|
+
- If found, use the most recently modified one and note its path in the stage output
|
|
163
|
+
- If not found, warn the user and fall back to running deep-interview anyway
|
|
164
|
+
|
|
165
|
+
When the stage completes, emit:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
✅ spec written to .omc/specs/deep-interview-{slug}.md
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Stage 3 · Ralplan (plan + consensus review)
|
|
174
|
+
|
|
175
|
+
Invoke:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
Skill("oh-my-claudecode:ralplan", "--direct{critic-flag-if-any}")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`--direct` tells ralplan to skip its own interview and consume the deep-interview spec directly. If the user passed `--critic=architect|critic|codex`, forward that flag to ralplan.
|
|
182
|
+
|
|
183
|
+
Ralplan will run Planner → Architect → Critic in sequence, looping until Critic approves. When it completes, the consensus plan lives at `.omc/plans/ralplan-{slug}.md`.
|
|
184
|
+
|
|
185
|
+
Emit:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
✅ consensus plan at .omc/plans/ralplan-{slug}.md
|
|
189
|
+
✅ Critic verdict: APPROVED
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
If Critic keeps rejecting past its internal loop budget, ralplan will stop and report. In that case, stop this wrapper too — don't press into autopilot with a rejected plan.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Stage 4 · Autopilot (execution + QA + validation)
|
|
197
|
+
|
|
198
|
+
Invoke:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
Skill("oh-my-claudecode:autopilot")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
No arguments. Autopilot will detect the consensus plan at `.omc/plans/ralplan-{slug}.md` and **skip its own Phase 0 + Phase 1** (expansion + planning), going straight to Phase 2 (Execution). It runs:
|
|
205
|
+
|
|
206
|
+
- Phase 2: parallel executor agents implement the plan
|
|
207
|
+
- Phase 3: QA cycling (up to 5 iterations) — build, lint, test, fix
|
|
208
|
+
- Phase 4: multi-perspective validation (Architect + Security-reviewer + Code-reviewer in parallel; all must approve)
|
|
209
|
+
- Phase 5: state cleanup
|
|
210
|
+
|
|
211
|
+
**Critical:** autopilot's output is *verified local code*, not a merged PR. Autopilot's Phase 5 is only state-file cleanup — it does not push, open a PR, watch CI, or merge. Ship is a separate stage.
|
|
212
|
+
|
|
213
|
+
Emit:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
✅ all phases complete
|
|
217
|
+
✅ tests pass, build green, validators approved
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
If autopilot's Phase 3 QA cycle repeats the same error 3 times, or Phase 4 validation fails 3 rounds, autopilot stops and reports. In that case stop this wrapper too — a human needs to review.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Stage 5 · Ship (push → PR → merge → sync)
|
|
225
|
+
|
|
226
|
+
**Skip if `--pr-only` or `--no-ship` was set.**
|
|
227
|
+
|
|
228
|
+
Invoke:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
Skill("mad-skills:ship", "{one-line summary of approach from ralplan} — Files: {files from autopilot IMPL_REPORT}")
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`mad-skills:ship` handles the PR lifecycle:
|
|
235
|
+
|
|
236
|
+
- Pushes the branch (creates one if needed)
|
|
237
|
+
- Creates PR via `gh pr create` (or AzDO equivalent)
|
|
238
|
+
- Watches CI — fixes failures in a separate sub-agent up to 2 attempts
|
|
239
|
+
- Squash-merges on green
|
|
240
|
+
- Syncs local back to default branch
|
|
241
|
+
|
|
242
|
+
Emit:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
✅ PR #{number} merged ({merge_commit})
|
|
246
|
+
✅ local synced to {default_branch}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
If ship's CI watch exhausts its fix attempts, ship stops and displays its own failure banner. In that case, stop — do not attempt additional retry here.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Waiting patterns
|
|
254
|
+
|
|
255
|
+
Whenever this skill (or its inner invocations) needs to wait on a file or condition, use a polling loop with an explicit deadline — **never** `sleep N && <cmd>`:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
deadline=$((SECONDS + 600))
|
|
259
|
+
until [ -s "$path" ] || [ $SECONDS -ge $deadline ]; do sleep 2; done
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
This keeps waits bounded and cache-friendly, and lets the loop exit early when the expected file appears.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Final Report
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
┌─ Launch · Report ──────────────────────────────
|
|
270
|
+
│
|
|
271
|
+
│ ✅ Launch complete
|
|
272
|
+
│
|
|
273
|
+
│ 💡 Idea: {first line of idea}
|
|
274
|
+
│ 📄 Spec: .omc/specs/deep-interview-{slug}.md
|
|
275
|
+
│ 🗺️ Plan: .omc/plans/ralplan-{slug}.md
|
|
276
|
+
│
|
|
277
|
+
│ 📝 Stages
|
|
278
|
+
│ 1. State cleared ✅
|
|
279
|
+
│ 2. Requirements interview ✅ ({Q&A rounds} rounds)
|
|
280
|
+
│ 3. Plan + Critic consensus ✅ ({critic-approval-rounds} rounds)
|
|
281
|
+
│ 4. Execution + QA + review ✅ ({QA cycles} QA cycles, {validator} approved)
|
|
282
|
+
│ 5. PR + merge ✅ ({pr_url})
|
|
283
|
+
│
|
|
284
|
+
│ 📊 Code
|
|
285
|
+
│ Files changed: {count}
|
|
286
|
+
│ Tests: {passed}/{total}
|
|
287
|
+
│
|
|
288
|
+
│ 🔗 Links
|
|
289
|
+
│ PR: {pr_url}
|
|
290
|
+
│ Merge: {merge_commit}
|
|
291
|
+
│
|
|
292
|
+
│ ⚡ Next
|
|
293
|
+
│ {anything surfaced by debrief from autopilot or ship}
|
|
294
|
+
│
|
|
295
|
+
└─────────────────────────────────────────────────
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
If the run stopped short (`--pr-only`, `--no-ship`, or a stage failure), adjust the stages list accordingly — mark later stages with ⏭️ (skipped) or ❌ (failed) and surface what's left to do manually in the `⚡ Next` section.
|
|
299
|
+
|
|
300
|
+
### Pipeline Summary (always emit)
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
┌─ Pipeline Summary ─────────────────────────────
|
|
304
|
+
│
|
|
305
|
+
│ {icon} Cancel {"no-op" or "cleared N sessions"}
|
|
306
|
+
│ {icon} Deep-interview {".omc/specs/... or reused spec"}
|
|
307
|
+
│ {icon} Ralplan {critic verdict}
|
|
308
|
+
│ {icon} Autopilot {"green / N files changed"}
|
|
309
|
+
│ {icon} Ship {pr_url → merge_commit}
|
|
310
|
+
│
|
|
311
|
+
└─────────────────────────────────────────────────
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Failure Handling
|
|
317
|
+
|
|
318
|
+
`/launch` is deliberately dumb about retries. Each inner skill already has its own retry/verification logic — trying to add more at this layer just fights with them. If any stage stops with a failure:
|
|
319
|
+
|
|
320
|
+
1. Display the failure banner below (not the success report)
|
|
321
|
+
2. List which stages completed, which failed, and which were skipped
|
|
322
|
+
3. Stop. Do not advance to later stages. Do not invoke `/sync` or any cleanup.
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
┌─ Launch · FAILED ───────────────────────────────
|
|
326
|
+
│
|
|
327
|
+
│ ❌ Pipeline stopped at Stage {N}: {stage name}
|
|
328
|
+
│
|
|
329
|
+
│ Reason: {specific failure reason from the inner skill}
|
|
330
|
+
│ Stages completed: {list}
|
|
331
|
+
│ Stages skipped: {list}
|
|
332
|
+
│
|
|
333
|
+
│ ⚠️ Work may exist locally but is NOT merged.
|
|
334
|
+
│ Review the inner skill's output for next steps.
|
|
335
|
+
│
|
|
336
|
+
└──────────────────────────────────────────────────
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Rules on failure:
|
|
340
|
+
|
|
341
|
+
- Do not emit a "Pipeline Summary" with a ✅
|
|
342
|
+
- Do not suggest a cancel/sync/retry here — the inner skill that failed owns that
|
|
343
|
+
- Do not invoke any recovery skill automatically
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Trigger Behaviour
|
|
348
|
+
|
|
349
|
+
This skill has `disable-model-invocation: true`. That means:
|
|
350
|
+
|
|
351
|
+
- It will **never** auto-activate on keyword hits, even if the user says things like *"launch this feature"*, *"ship it end to end"*, *"run the full pipeline"*, or mentions any of the inner skill names.
|
|
352
|
+
- It runs **only** when the user literally types `/launch` as a slash command.
|
|
353
|
+
- Another skill can still invoke it explicitly via `Skill("launch", "...")` if desired.
|
|
354
|
+
|
|
355
|
+
This is deliberate: the inner skills (especially autopilot) have aggressive keyword triggers of their own, and a wrapper that also auto-triggered would compound the false-positive risk. Keep this file's `disable-model-invocation` flag set.
|
package/skills/manifest.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-04-
|
|
3
|
-
"count":
|
|
2
|
+
"generated": "2026-04-20T02:26:55.864Z",
|
|
3
|
+
"count": 11,
|
|
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": 424,
|
|
10
10
|
"hasScripts": false,
|
|
11
11
|
"hasReferences": true,
|
|
12
12
|
"hasAssets": true,
|
|
@@ -52,6 +52,16 @@
|
|
|
52
52
|
"hasAssets": false,
|
|
53
53
|
"hasTests": true
|
|
54
54
|
},
|
|
55
|
+
{
|
|
56
|
+
"name": "launch",
|
|
57
|
+
"directory": "launch",
|
|
58
|
+
"description": "\"Run the full OMC idea-to-merged-PR pipeline — cancel + deep-interview + ralplan + autopilot + mad-skills:ship — in a single invocation. Explicit-only; this skill never auto-activates. Only run when the user literally types /launch. Do not invoke on phrases like \\\"launch this\\\", \\\"ship it\\\", \\\"full pipeline\\\", or similar — none of those should trigger this skill.\"",
|
|
59
|
+
"lines": 356,
|
|
60
|
+
"hasScripts": false,
|
|
61
|
+
"hasReferences": false,
|
|
62
|
+
"hasAssets": false,
|
|
63
|
+
"hasTests": false
|
|
64
|
+
},
|
|
55
65
|
{
|
|
56
66
|
"name": "prime",
|
|
57
67
|
"directory": "prime",
|
|
@@ -94,30 +94,32 @@ if [ "$AZDO_MODE" = "cli" ]; then
|
|
|
94
94
|
sleep 15
|
|
95
95
|
done
|
|
96
96
|
|
|
97
|
-
# Complete the PR
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
BRANCH_DELETED=$DELETE_BRANCH
|
|
105
|
-
else
|
|
106
|
-
# Retry once after 30s (policies may still be evaluating)
|
|
107
|
-
sleep 30
|
|
97
|
+
# Complete the PR — retry on a bounded deadline; policies may still be
|
|
98
|
+
# evaluating after the initial poll finished. First attempt runs
|
|
99
|
+
# immediately, then we poll every 5s up to a 30s total budget.
|
|
100
|
+
# Avoid `sleep 30 && retry` (blind wait) — this loop exits early on success.
|
|
101
|
+
MERGE_DEADLINE=$((SECONDS + 30))
|
|
102
|
+
MERGE_OK=false
|
|
103
|
+
while :; do
|
|
108
104
|
if az repos pr update --id "$PR_NUMBER" --status completed \
|
|
109
105
|
--org "$AZDO_ORG_URL" \
|
|
110
106
|
--squash "$SQUASH_FLAG" \
|
|
111
107
|
--delete-source-branch "$DELETE_FLAG" 2>/dev/null; then
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
BRANCH_DELETED=$DELETE_BRANCH
|
|
115
|
-
else
|
|
116
|
-
STATUS="failed"
|
|
117
|
-
ERRORS="Merge failed after retry"
|
|
118
|
-
MERGE_COMMIT=""; BRANCH_DELETED=false
|
|
119
|
-
emit_report; exit 2
|
|
108
|
+
MERGE_OK=true
|
|
109
|
+
break
|
|
120
110
|
fi
|
|
111
|
+
[ $SECONDS -ge $MERGE_DEADLINE ] && break
|
|
112
|
+
sleep 5
|
|
113
|
+
done
|
|
114
|
+
if $MERGE_OK; then
|
|
115
|
+
STATUS="success"
|
|
116
|
+
MERGE_COMMIT=$(git rev-parse --short HEAD 2>/dev/null)
|
|
117
|
+
BRANCH_DELETED=$DELETE_BRANCH
|
|
118
|
+
else
|
|
119
|
+
STATUS="failed"
|
|
120
|
+
ERRORS="Merge failed after retry"
|
|
121
|
+
MERGE_COMMIT=""; BRANCH_DELETED=false
|
|
122
|
+
emit_report; exit 2
|
|
121
123
|
fi
|
|
122
124
|
emit_report
|
|
123
125
|
exit 0
|