Skip to content

Commit 7d8f430

Browse files
feat(mdd): add global ops scope, collision guard, and ops list command
- /mdd ops now asks global vs project scope as first question - Global saves to ~/.claude/ops/ — reusable across all projects - Note shown: global ops cannot access project-local .env vars - Collision guard: if a global op exists with the same slug, creating a project op with that name is a hard stop (no silent shadowing) - runop and update-op check project-local first, then global; announce which scope the runbook was found in - /mdd ops list shows unified view of all global + project runbooks with last-run status, grouped by scope Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ea90329 commit 7d8f430

1 file changed

Lines changed: 72 additions & 12 deletions

File tree

.claude/commands/mdd.md

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,15 +1872,35 @@ Report:
18721872

18731873
## OPS DOCUMENT MODE — `/mdd ops <description>`
18741874

1875-
Triggered when arguments start with `ops`. Creates a new ops runbook in `.mdd/ops/`.
1875+
Triggered when arguments start with `ops`. If arguments are exactly `ops list` → jump to **Ops List Mode** (Phase OL) instead.
18761876

1877-
### Phase OP1 — Derive slug and check for existing doc
1877+
### Phase OP1 — Scope, slug, and collision check
18781878

1879-
1. Strip `ops` from the start of arguments. Use the remainder as the description.
1880-
2. Derive a slug: lowercase, hyphens, drop filler words (e.g., "deploy swarmk to dokploy" → `swarmk-dokploy`, "api docker hub" → `api-docker-hub`).
1881-
3. Check `.mdd/ops/<slug>.md`:
1882-
- **Does not exist** → proceed to Phase OP2 (create)
1883-
- **Exists** → tell the user: *"Runbook `<slug>` already exists. Use `/mdd update-op <slug>` to edit it or `/mdd runop <slug>` to execute it."* Stop.
1879+
**Step 1 — Ask scope first (before anything else):**
1880+
1881+
```
1882+
Where should this runbook live?
1883+
(a) Project — .mdd/ops/<slug>.md (this project only)
1884+
(b) Global — ~/.claude/ops/<slug>.md (reusable across all projects)
1885+
1886+
Note: Global ops cannot access project-local .env variables or
1887+
project-specific paths. Use ~/.env globals only.
1888+
```
1889+
1890+
**Step 2 — Derive slug:**
1891+
Strip `ops` from the start of arguments. Use the remainder as the description.
1892+
Derive a slug: lowercase, hyphens, drop filler words (e.g., "deploy swarmk to dokploy" → `swarmk-dokploy`, "update cloudflare dns" → `cloudflare-dns`).
1893+
1894+
**Step 3 — Collision check:**
1895+
- If scope is **project**: check whether `~/.claude/ops/<slug>.md` exists.
1896+
- If global op exists with that slug → **hard stop**:
1897+
*"A global runbook named `<slug>` already exists (`~/.claude/ops/<slug>.md`). Project ops cannot share a name with a global op — use a different name, or run `/mdd runop <slug>` to execute the global one."*
1898+
- If scope is **global**: check whether `~/.claude/ops/` exists, create it if not (`mkdir -p ~/.claude/ops`).
1899+
1900+
**Step 4 — Check target location:**
1901+
- Target path: `.mdd/ops/<slug>.md` (project) or `~/.claude/ops/<slug>.md` (global)
1902+
- **Does not exist** → proceed to Phase OP2 (create)
1903+
- **Exists** → tell the user: *"Runbook `<slug>` already exists. Use `/mdd update-op <slug>` to edit it or `/mdd runop <slug>` to execute it."* Stop.
18841904

18851905
### Phase OP2 — Ask questions
18861906

@@ -1995,8 +2015,10 @@ Triggered when arguments start with `runop`. Executes an existing ops runbook wi
19952015
### Phase RO1 — Load runbook
19962016

19972017
1. Parse `<slug>` from arguments — hard stop *"Slug required. Usage: /mdd runop <slug>"* if missing.
1998-
2. Read `.mdd/ops/<slug>.md` — hard stop if not found:
1999-
*"No runbook found for `<slug>`. Run `/mdd ops <description>` to create one."*
2018+
2. Locate the runbook (project-local first, then global):
2019+
- Check `.mdd/ops/<slug>.md` → found: announce *"Running project runbook: `<slug>`"*
2020+
- Check `~/.claude/ops/<slug>.md` → found: announce *"Running global runbook: `<slug>`"*
2021+
- Neither found → hard stop: *"No runbook found for `<slug>` (checked project and global). Run `/mdd ops <description>` to create one, or `/mdd ops list` to see all available runbooks."*
20002022
3. Parse all frontmatter fields: regions (sorted by `deploy_order`), services, `deployment_strategy`.
20012023

20022024
### Phase RO2 — Pre-flight health check (all regions)
@@ -2114,7 +2136,10 @@ Triggered when arguments start with `update-op`. Updates an existing ops runbook
21142136
### Phase UO1 — Load
21152137

21162138
1. Parse `<slug>` — hard stop *"Slug required. Usage: /mdd update-op <slug>"* if missing.
2117-
2. Read `.mdd/ops/<slug>.md` — hard stop if not found: *"No runbook found for `<slug>`."*
2139+
2. Locate runbook (project-local first, then global):
2140+
- Check `.mdd/ops/<slug>.md` → found: load it, note scope = project
2141+
- Check `~/.claude/ops/<slug>.md` → found: load it, note scope = global
2142+
- Neither found → hard stop: *"No runbook found for `<slug>`. Run `/mdd ops list` to see all available runbooks."*
21182143

21192144
### Phase UO2 — Re-ask with current values pre-filled
21202145

@@ -2146,6 +2171,40 @@ Update frontmatter: `last_synced: <today>`, `status: draft` if previously `compl
21462171

21472172
---
21482173

2174+
## OPS LIST MODE — `/mdd ops list`
2175+
2176+
Triggered when arguments are exactly `ops list`. Lists all ops runbooks — global and project — in a single unified view.
2177+
2178+
### Phase OL — Scan and display
2179+
2180+
1. Glob `~/.claude/ops/*.md` — read each, extract `id`, `title`, `platform`, `status`, and the last `last_checked` value across all services.
2181+
2. Glob `.mdd/ops/*.md` (excluding `archive/`) — same fields.
2182+
3. Display unified list, grouped by scope:
2183+
2184+
```
2185+
📦 Ops Runbooks
2186+
2187+
Global (~/.claude/ops/)
2188+
cloudflare-dns DNS record updates via Cloudflare API last run: 2026-04-10
2189+
docker-hub-login Docker Hub authentication procedure last run: 2026-03-28
2190+
ssl-renewal Let's Encrypt cert renewal (Certbot) last run: never
2191+
2192+
Project (.mdd/ops/)
2193+
rulecatch-dokploy 10 services → eu-west (canary) + us-east last run: 2026-04-18 ✓ all healthy
2194+
swarmk-dokploy 7 services → eu-west (canary) + us-east last run: 2026-04-17 ⚠ api degraded
2195+
2196+
Run /mdd runop <slug> to execute any runbook.
2197+
```
2198+
2199+
If either directory is empty or missing, omit that section without error. If both are empty:
2200+
```
2201+
No ops runbooks found.
2202+
Project: /mdd ops <description> (saves to .mdd/ops/)
2203+
Global: /mdd ops <description> (choose "global" when prompted)
2204+
```
2205+
2206+
---
2207+
21492208
## COMMANDS MODE — `/mdd commands`
21502209

21512210
Triggered when arguments start with `commands`. Outputs a reference table of every available MDD mode.
@@ -2178,9 +2237,10 @@ Command | Description
21782237
/mdd plan-sync | Plan-Sync Mode — Reconcile manual edits to initiative/wave files
21792238
/mdd plan-remove-feature <wave> <feature> | Plan-Remove-Feature Mode — Remove a feature from a wave
21802239
/mdd plan-cancel-initiative <slug> | Plan-Cancel-Initiative Mode — Cancel an initiative and archive its waves
2181-
/mdd ops <description> | Ops Document Mode — Create a new deployment runbook in .mdd/ops/
2240+
/mdd ops <description> | Ops Document Mode — Create a runbook (asks: global ~/.claude/ops/ or project .mdd/ops/)
2241+
/mdd ops list | Ops List Mode — Show all runbooks (global and project) with last-run status
21822242
/mdd runop <slug> | Ops Execute Mode — Run a runbook: pre-flight health check, canary-gated deploy, post-flight verify
2183-
/mdd update-op <slug> | Ops Update Mode — Edit an existing deployment runbook
2243+
/mdd update-op <slug> | Ops Update Mode — Edit an existing runbook (checks project then global)
21842244
21852245
Run /mdd <feature description> to start building, /mdd ops <description> to create a deployment runbook, or /mdd audit to check existing code.
21862246
```

0 commit comments

Comments
 (0)