@mutmutco/codex-plugin 3.131.0
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/.codex-plugin/plugin.json +30 -0
- package/bin/mmi-cli +6 -0
- package/bin/mmi-cli.cmd +3 -0
- package/bin/mmi-hook +2 -0
- package/bin/mmi-hook-console.cmd +10 -0
- package/bin/mmi-hook.exe +0 -0
- package/hooks/codex-hooks.json +41 -0
- package/package.json +21 -0
- package/scripts/command-ladder-core.mjs +334 -0
- package/scripts/command-ladder-gate.mjs +126 -0
- package/scripts/deny-gate-crash.mjs +179 -0
- package/scripts/edit-tool-paths.mjs +113 -0
- package/scripts/env-write-lint.mjs +137 -0
- package/scripts/hook-io.mjs +22 -0
- package/scripts/hook-policy.mjs +73 -0
- package/scripts/hook-run.mjs +437 -0
- package/scripts/hook-trace.mjs +151 -0
- package/scripts/pretooluse-shell-gates.mjs +424 -0
- package/scripts/secret-echo-lint.mjs +177 -0
- package/scripts/secret-redact.mjs +552 -0
- package/scripts/throttle-core.mjs +324 -0
- package/scripts/validate-hook.mjs +156 -0
- package/scripts/vault-edit-gate.mjs +94 -0
- package/skills/bootstrap/SKILL.md +550 -0
- package/skills/bootstrap/seeds/Dockerfile.template +30 -0
- package/skills/bootstrap/seeds/README.template.md +37 -0
- package/skills/bootstrap/seeds/architecture.template.md +34 -0
- package/skills/bootstrap/seeds/decisions-readme.template.md +45 -0
- package/skills/bootstrap/seeds/docker-compose.template.yml +26 -0
- package/skills/bootstrap/seeds/gate.template.yml +85 -0
- package/skills/bootstrap/seeds/google-login.template.md +33 -0
- package/skills/bootstrap/seeds/manifest.json +26 -0
- package/skills/bootstrap/seeds/mmi-product-required-checks.template.json +23 -0
- package/skills/browser-automation/SKILL.md +95 -0
- package/skills/epic/SKILL.md +104 -0
- package/skills/hotfix/SKILL.md +165 -0
- package/skills/mmi/SKILL.md +404 -0
- package/skills/mmi-doctor/SKILL.md +63 -0
- package/skills/onboard/SKILL.md +85 -0
- package/skills/rcand/SKILL.md +208 -0
- package/skills/release/SKILL.md +599 -0
- package/skills/resume/SKILL.md +90 -0
- package/skills/secrets/SKILL.md +159 -0
- package/skills/stage/SKILL.md +153 -0
- package/skills/worktree/SKILL.md +151 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: resume
|
|
3
|
+
description: Open a session — self-check, repo status, and the next actionable board item in one pass.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**Host-native invocation:** Claude `/mmi:resume` · Codex `$mmi:resume` · jervcode/Kimi `/skill:resume` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
|
|
7
|
+
|
|
8
|
+
# /resume — start of session
|
|
9
|
+
|
|
10
|
+
One flow to re-anchor at the top of a session: is the toolchain sound, what does this repo/worktree look
|
|
11
|
+
like right now, and what is the single next thing worth doing. Read-only — it renders the picture and gets
|
|
12
|
+
out of the way. `/mmi` is the board-only hello-to-work; `/resume` is the wider session open that folds in
|
|
13
|
+
the toolchain self-check and the current-branch snapshot.
|
|
14
|
+
|
|
15
|
+
## Step 0 — toolchain self-check
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mmi-cli doctor --self
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`doctor --self` verifies the two things that silently break a session: CLI/plugin version parity against
|
|
22
|
+
the published version, and whether the active `gh` token can resolve this repo. It is fast, but it reads
|
|
23
|
+
npm and GitHub — it is not offline-safe, and it checks neither PATH nor hook wiring (#3485). On a **hard**
|
|
24
|
+
gap it points at the heal path — relay it and run it before working:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
mmi-cli plugin heal # only on a hard version/plugin gap; then restart Claude Code / reopen the workspace
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A clean self-check is silent-enough — move straight on. Do not block the snapshot on a soft/advisory note.
|
|
31
|
+
|
|
32
|
+
## Step 1 — repo snapshot
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
mmi-cli status
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`status` is the unified current-state read for **this** checkout: branch, linked worktrees, your open PRs,
|
|
39
|
+
your claimed board items, and whether a local stage is running. It is the "where am I" line — render it,
|
|
40
|
+
don't re-derive it from raw `git`/`gh`. Keep the case-preserving `repo` value it reports; that is the
|
|
41
|
+
workspace identity the next step must use (for example `mutmutco/MMC-ZuberShade`).
|
|
42
|
+
|
|
43
|
+
## Step 2 — the next move
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
mmi-cli oracle next --repo <repo reported by status>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Never the retired flat `mmi-cli next` — Wave 3 (#4316) removed that alias; a `flat 'next' alias was
|
|
50
|
+
removed` exit means follow the printed `oracle next` path (and `mmi-cli plugin heal` when
|
|
51
|
+
`doctor --self` says the plugin is behind). Pass the repository from `status` explicitly; never
|
|
52
|
+
reconstruct it by changing case or punctuation. `next` recommends the single most actionable
|
|
53
|
+
claimable board item (unblocked, priority-ranked) and prints the exact
|
|
54
|
+
`mmi-cli oracle board claim <n>` to take it. Offer it; do not claim on the user's behalf without a go.
|
|
55
|
+
For the full board partition (yours / claimable / taken) run `/mmi`.
|
|
56
|
+
|
|
57
|
+
## Step 3 — render, then stop
|
|
58
|
+
|
|
59
|
+
Lead with one line: toolchain OK (or the heal to run), the branch + what's in flight, and the one next
|
|
60
|
+
item with its claim command. Then stop. The user drives — claim, continue, or file when they say so.
|
|
61
|
+
|
|
62
|
+
## Current-state doors (Hub#4120)
|
|
63
|
+
|
|
64
|
+
When answering “what is true now”, **ask the CLI**, not committed markdown inventories or weekly
|
|
65
|
+
janitor prose. Catalog: `docs/Architecture/compute-at-read.md` (in MMI-Hub).
|
|
66
|
+
|
|
67
|
+
| Ask | Verb |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| Schedules / harbour lanes | `mmi-cli harbour org schedules` / `--json` |
|
|
70
|
+
| Boxes / IPs | `mmi-cli devops runtime box list` |
|
|
71
|
+
| Registry / projects | `mmi-cli oracle org project list\|get` |
|
|
72
|
+
| Board | `mmi-cli oracle board read` / `mmi-cli oracle next` |
|
|
73
|
+
| Docs index freshness | `mmi-cli oracle docs index --check` |
|
|
74
|
+
| Path / symbol / meaning pointers | `mmi-cli oracle repo-index search <q>` (Hub cloud; `--semantic` optional) |
|
|
75
|
+
|
|
76
|
+
## Notes
|
|
77
|
+
|
|
78
|
+
- `/resume` is read-only session orientation. It never claims, moves, or writes.
|
|
79
|
+
- Board status moves are automatic (claim, PR open, merge). Never suggest a manual status move.
|
|
80
|
+
- Slower, board-partition view → `/mmi`. Readiness-gate view for a new repo/first session → `/onboard`.
|
|
81
|
+
|
|
82
|
+
## Retro — one check before you finish
|
|
83
|
+
Before your final report, answer one question honestly: did **this skill's own instructions** misfire
|
|
84
|
+
this run — ambiguous wording, a misleading message, or an environment failure it should have warned
|
|
85
|
+
about? (Process only — never the user's code or task; e.g. a self-check that reported a false gap, or a
|
|
86
|
+
snapshot that named the wrong branch.) If yes, file **one** lesson and move on; a clean run is silent
|
|
87
|
+
(hard cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR — never
|
|
88
|
+
edit the skill live; the retro is advisory, so if the call fails, note it and continue:
|
|
89
|
+
`mmi-cli learning skill-lesson --skill resume --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
|
|
90
|
+
(never the retired flat `mmi-cli skill-lesson`).
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: secrets
|
|
3
|
+
description: Manage the full own-project vault and granted org-infra secret names.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**Host-native invocation:** Claude `/mmi:secrets` · Codex `$mmi:secrets` · jervcode/Kimi `/skill:secrets` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
|
|
7
|
+
|
|
8
|
+
# /secrets — two-tier project secrets
|
|
9
|
+
|
|
10
|
+
**Authority:** a project-admin self-serves their own product repo's full vault tree (`mmi-cli oracle org access
|
|
11
|
+
role` → `project-admin` + `train`): stageless, `dev`, `rc`, and `main`. Do not redirect them to master for
|
|
12
|
+
an own-project coordinate. Only org-infra grants and cross-project administration stay master-only.
|
|
13
|
+
|
|
14
|
+
Secrets in the org split by **blast radius + who manages them** (not by storage — both tiers are SSM
|
|
15
|
+
SecureString + KMS, and **a value is never echoed to chat or logs**):
|
|
16
|
+
|
|
17
|
+
- **YOUR REPO** — `/mmi-future/<slug>/<KEY>` (the stageless canonical) plus
|
|
18
|
+
`/mmi-future/<slug>/{dev,rc,main}/*` (staged per-stage overrides) for a repo you are project-admin of.
|
|
19
|
+
You **self-serve your own repo's full tree** — the canonical root, dev, rc, AND main prod secrets — via
|
|
20
|
+
`/secrets`, on your **GitHub role alone**, no AWS, no waiting on the master (#2032).
|
|
21
|
+
- **ORG-INFRA** — the shared/infra namespaces (`/mmi-future/{shared,cloudflare,docs,mmi-hub}/*`), at
|
|
22
|
+
**every** tier (their `dev/` holds Hetzner/AWS/CF tokens + Hub tooling). **Crown jewels, master-only.**
|
|
23
|
+
A project-admin reaches one of these only via a master **grant**; the master is unrestricted (master ⊇
|
|
24
|
+
project-admin).
|
|
25
|
+
|
|
26
|
+
**Grant scope (#3652).** A grant is `rw` by default — it opens both keyless `use` and `set`/`rm` on that
|
|
27
|
+
key. `mmi-cli vault secrets grant <repo> <login> <key> --read` grants **consume-only** reach: keyless `use`
|
|
28
|
+
works, rotate and remove still refuse. Only a `--read` grant may name a **wildcard** key — `*` for the
|
|
29
|
+
whole namespace, `<provider>/*` for one provider group — so "may read every crown jewel" is one grant
|
|
30
|
+
instead of forty. A wildcard never confers write; asking for one as `rw` is refused, not narrowed.
|
|
31
|
+
`mmi-cli oracle org access capabilities` prints `grant (read)` or `grant (rw)` next to each name.
|
|
32
|
+
|
|
33
|
+
A bare `<KEY>` is the **stageless canonical** at the slug root — one value every stage shares (#2244)
|
|
34
|
+
and the path deploys resolve (#2523). A staged `dev/<KEY>` is a **per-stage override** and WINS for that
|
|
35
|
+
stage at deploy (#2522); `set`/`use` warn (names only) when an override shadows the canonical. The
|
|
36
|
+
`dev`/non-`dev` `tier` field in older JSON is compatibility metadata, not an authority boundary: an
|
|
37
|
+
own-project `main/SECRET_KEY_BASE` or `rc/DB_URL` remains project-admin self-service.
|
|
38
|
+
|
|
39
|
+
**Declare-first (#2528):** the registry catalog is the SSOT; SSM is a projection of it. A `set` to a
|
|
40
|
+
coordinate not declared in the catalog is **rejected** — the error names the attempted path and the fix.
|
|
41
|
+
Declare the key first via `mmi-cli oracle org project set <owner/repo> --secrets-file <catalog.json>` (`stages: []`
|
|
42
|
+
= the shared stageless canonical, `stages: ["dev"]` = a per-stage override), then retry. See what is
|
|
43
|
+
declared with `mmi-cli vault secrets catalog` or `secrets list`; ask for a key you lack via `mmi-cli vault secrets
|
|
44
|
+
request <KEY>`. There is no master bypass. `rm` of an undeclared path stays allowed, so cleanup keeps
|
|
45
|
+
working. Google OAuth is one stageless pair per repo: bare `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET`
|
|
46
|
+
at the slug root (the staged `{dev,rc,main}/GOOGLE_*` shape is retired; `org oauth set-creds` writes the
|
|
47
|
+
bare keys).
|
|
48
|
+
|
|
49
|
+
All ops run through `mmi-cli vault secrets …`, which calls the org backend with the caller's `gh` token; the
|
|
50
|
+
backend re-verifies **project-admin-of-this-repo** and the **vault namespace** server-side and does the scoped
|
|
51
|
+
SSM op. This skill never touches AWS.
|
|
52
|
+
|
|
53
|
+
## Step 0 — orient
|
|
54
|
+
|
|
55
|
+
Read the current repo + slug; the tier of a key follows from its name (above). `secrets list` shows what
|
|
56
|
+
exists and which ones **you** can manage (a `*`), never values.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
mmi-cli vault secrets list # names + tier + a * on the ones you can write. NEVER values.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`secrets: no registry META` or `Hub API unreachable` → the repo is not registered with the Hub, GitHub auth
|
|
63
|
+
is missing, or the Hub API is unavailable. Run `mmi-cli oracle org project get <owner/repo>` to distinguish those cases;
|
|
64
|
+
a master-admin backfills the registry before secrets can be resolved.
|
|
65
|
+
|
|
66
|
+
## Step 1 — the verb
|
|
67
|
+
|
|
68
|
+
Default to the **current repo**; pass `--repo owner/Name` to target another (you must be its project-admin).
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# CONSUME a secret keyless — injected into the command's env, NEVER printed (raw `secrets get` was
|
|
72
|
+
# removed, the floor wins, #2844):
|
|
73
|
+
mmi-cli vault secrets use SCRAPER_API_KEY -- <cmd>
|
|
74
|
+
|
|
75
|
+
# Write / rotate — the VALUE is read from stdin, NEVER an argument (so it can't leak into shell history
|
|
76
|
+
# or process args). Pipe it in:
|
|
77
|
+
printf %s "$THE_VALUE" | mmi-cli vault secrets set SCRAPER_API_KEY
|
|
78
|
+
|
|
79
|
+
# Validate a known provider key without printing its value:
|
|
80
|
+
mmi-cli vault secrets verify RECALL_API_KEY
|
|
81
|
+
|
|
82
|
+
# Remove:
|
|
83
|
+
mmi-cli vault secrets rm SCRAPER_API_KEY
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Never** pass a value as an argument (`secrets set KEY thevalue` is wrong — there is no value arg). The
|
|
87
|
+
confirmation prints the **name and tier only**, never the value.
|
|
88
|
+
|
|
89
|
+
## Rotation checklist
|
|
90
|
+
|
|
91
|
+
Before rotating, enumerate every copy of the key so no tier stays stale:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
mmi-cli vault secrets list --repo owner/Repo
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Check the bare canonical plus any `dev/`, `rc/`, `main/` overrides the list shows. Rotate the provider-side
|
|
98
|
+
key first, then write every declared coordinate that still needs that key. For keys with a provider probe
|
|
99
|
+
(`RECALL_API_KEY` today), `secrets set` validates the new value before printing success; `secrets verify
|
|
100
|
+
<KEY>` repeats the same probe later without printing the value. If the verifier fails, treat the rotation as
|
|
101
|
+
incomplete even if the vault write itself succeeded.
|
|
102
|
+
|
|
103
|
+
## Step 2 — org-infra elevation (master-only)
|
|
104
|
+
|
|
105
|
+
Your own repo's full tree (dev/rc/main) is already self-serve (#2032). The **org-infra** namespaces
|
|
106
|
+
(`shared`/`cloudflare`/`docs`/`mmi-hub`, every tier) stay master-only. A project-admin who needs one of
|
|
107
|
+
those keys asks the master, who grants a **scoped, auditable** standing access to that one key (or revokes
|
|
108
|
+
it). These verbs are **master-only** — the backend 403s anyone else.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# MASTER: let @oguz-mut consume one _org provider key from their MM-Chat project
|
|
112
|
+
mmi-cli vault secrets grant mutmutco/MM-Chat oguz-mut google/SOME_SHARED_KEY
|
|
113
|
+
# MASTER: withdraw it
|
|
114
|
+
mmi-cli vault secrets revoke mutmutco/MM-Chat oguz-mut google/SOME_SHARED_KEY
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The master can also operate the org-infra key directly while guiding (master ⊇ project-admin). Org-infra
|
|
118
|
+
access by a project-admin **always originates from the master** — own-project access does not.
|
|
119
|
+
|
|
120
|
+
## Coordinate — where does a new project secret belong?
|
|
121
|
+
|
|
122
|
+
- Shared across stages → bare `<KEY>` at the project slug root (the usual canonical value).
|
|
123
|
+
- Genuinely stage-distinct → `<stage>/<KEY>` override for `dev`, `rc`, or `main`.
|
|
124
|
+
- The project-admin owns both shapes across their own project. A path under `_org`/shared infrastructure is
|
|
125
|
+
not a project coordinate and stays master-managed.
|
|
126
|
+
|
|
127
|
+
## Tier-to-tier copy (provider keys, #1433)
|
|
128
|
+
|
|
129
|
+
**Encryption / stage-distinct keys** (`*_ENC_KEY`, `SECRET_KEY_BASE`, etc.) must be **generated per stage** —
|
|
130
|
+
never copied. **Provider sandbox keys** (e.g. Recall/Gemini for Katip dev/rc) **may** be shared across dev/rc
|
|
131
|
+
when they point at the same sandbox project; prod should stay distinct when the provider contract requires it.
|
|
132
|
+
|
|
133
|
+
Sanctioned copy within the own-project vault (org-infra/cross-project sources remain master-gated):
|
|
134
|
+
```bash
|
|
135
|
+
mmi-cli vault secrets copy --from rc --to dev --keys RECALL_API_KEY,GEMINI_API_KEY
|
|
136
|
+
mmi-cli vault secrets copy --from rc --to dev --keys RECALL_API_KEY --dry-run # plan only
|
|
137
|
+
```
|
|
138
|
+
Prefer this over manual copy piping — audit-logged, blocklist enforced.
|
|
139
|
+
|
|
140
|
+
## Notes
|
|
141
|
+
|
|
142
|
+
- **Never echo a value** — not in chat, a commit, a log, or an issue. No command emits a value to stdout;
|
|
143
|
+
consume one keyless with `secrets use <KEY> -- <cmd>` (#2844). Treat every secret as write-once.
|
|
144
|
+
- Authority is **GitHub**: project-admin = repo `write` plus the registry `projectAdmins` entry, master =
|
|
145
|
+
org owner. The backend checks both with the org App token, so the decision is server-side (your token
|
|
146
|
+
scope can't widen it).
|
|
147
|
+
- Runtime consumes declared keys through its scoped broker token (and central deploy injects the declared
|
|
148
|
+
stage set without a repo `.env`); CI reads keylessly through its OIDC role. Neither uses raw `/secrets`
|
|
149
|
+
output. `secrets use <KEY>` is the safe local command-consumption path.
|
|
150
|
+
- Every self-service write is **attributable** — the backend logs actor + repo + KEY name (never the value).
|
|
151
|
+
|
|
152
|
+
## Retro — one check before you finish
|
|
153
|
+
Before your final report, answer one question honestly: did **this skill's own instructions** misfire
|
|
154
|
+
this run — ambiguous wording, a misleading message, or an environment failure it should have warned
|
|
155
|
+
about? (Process only — never the user's code or task; e.g. a canonical-name or tier mix-up, or a step
|
|
156
|
+
that risked echoing a value.) If yes, file **one** lesson and move on; a clean run is silent (hard cap:
|
|
157
|
+
one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR — never edit the
|
|
158
|
+
skill live; the retro is advisory, so if the call fails, note it and continue:
|
|
159
|
+
`mmi-cli learning skill-lesson --skill secrets --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stage
|
|
3
|
+
description: Run a local test stage, or a personal cloud dev stage with --live.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**Host-native invocation:** Claude `/mmi:stage` · Codex `$mmi:stage` · jervcode/Kimi `/skill:stage` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
|
|
7
|
+
|
|
8
|
+
# /stage — local test environment
|
|
9
|
+
|
|
10
|
+
A throwaway **local** environment to exercise the current branch — a dev server / local stack, plus
|
|
11
|
+
Playwright or other tools where the project uses them. It is **off the promotion train**: any developer,
|
|
12
|
+
no version effect, no deploy. It lives in a **gitignored** workspace, **force-kills the previous stage**
|
|
13
|
+
before starting, and **auto-decommissions** when done.
|
|
14
|
+
|
|
15
|
+
Use `/stage` as the normal path for local previews, smoke tests, and Playwright targets. If the user
|
|
16
|
+
already asked to run or preview the app, that is enough authorization to start it; when intent is unclear,
|
|
17
|
+
ask before creating or destroying a stage. Manual dev-server commands are allowed when the user explicitly
|
|
18
|
+
wants a bypass or when a bounded diagnostic needs one, but say why. If the stage path itself fails, diagnose
|
|
19
|
+
from `mmi-cli stage --json`, `tmp/stage/state.json`, process/port/container evidence, and file Hub/org-default
|
|
20
|
+
friction instead of silently working around it.
|
|
21
|
+
|
|
22
|
+
The stage recipe is the **registry-derived default** for plumbing-free product repos: a `tenant-container`
|
|
23
|
+
repo that ships `docker-compose.yml` and has a Hub registry `portRange` needs **no committed config**.
|
|
24
|
+
`mmi-cli stage` derives a default compose stage — run `docker compose build --no-cache`, run `docker compose
|
|
25
|
+
up -d`, choose a free port from the registry range, and report the local URL (e.g. `http://127.0.0.1:3700/`).
|
|
26
|
+
The commands are shell-aware and `--apply` runs them cross-shell. Board, deploy, secret, and project registry
|
|
27
|
+
facts all live in the Hub registry — no repo-local control-plane file.
|
|
28
|
+
|
|
29
|
+
**Secrets are vault-native — no `.env` on disk (#2655).** The org forbids *using* `.env` files, not just
|
|
30
|
+
committing them. A repo **without** `.env.example` stages vault-native: `mmi-cli stage --apply` resolves the
|
|
31
|
+
declared dev runtime secrets (registry `requiredRuntimeSecrets[dev]`) from the vault and injects them straight
|
|
32
|
+
into the compose **process environment** — the compose file reads them via `environment:` passthrough, and no
|
|
33
|
+
secret ever touches disk. A repo that still ships `.env.example` keeps the legacy `.env` bootstrap for
|
|
34
|
+
back-compat until its compose migrates to passthrough; new repos should ship **no** `.env.example`.
|
|
35
|
+
|
|
36
|
+
## Step 0 — inspect the plan
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
mmi-cli stage --json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The JSON reports `source` (`derived` / `local` / `none`) and, when derived, the local `url`. `source: none`
|
|
43
|
+
means neither a usable local recipe nor a derivable default exists — the message names the missing fact
|
|
44
|
+
(deployModel, `docker-compose.yml`, or registry `portRange`; `.env.example` is **not** required — #2655).
|
|
45
|
+
That gap does not mean the repo's Hub registry/org project setup is missing.
|
|
46
|
+
|
|
47
|
+
## Step 1 — run the stage
|
|
48
|
+
|
|
49
|
+
A stage is **per worktree**. The CLI force-stops only the previous stage **in this worktree**, runs
|
|
50
|
+
`stage.build`, starts `stage.up`, records `tmp/stage/state.json`, picks a free port from the registry
|
|
51
|
+
range (skipping ports reserved by sibling worktrees), and polls `stage.healthUrl` when configured.
|
|
52
|
+
Use `--port` to pin a port when needed:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
mmi-cli stage run --apply
|
|
56
|
+
mmi-cli stage run --apply --port 5180
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use a larger bound when the repo's local stack is known to be slow:
|
|
60
|
+
```bash
|
|
61
|
+
mmi-cli stage run --apply --timeout-ms 120000
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
All stage artifacts (build output, screenshots, Playwright traces, local DB files) stay under `tmp/stage/`
|
|
65
|
+
— never tracked.
|
|
66
|
+
|
|
67
|
+
A local stage is bound to the worktree that started it. Concurrent worktrees on one machine each keep
|
|
68
|
+
their own stage on separate ports — you do not need to stop one before starting another. For `jervaise`, continuity
|
|
69
|
+
keys on branch; use a distinct North Star slug per parallel grind or feature. Stage JSON/state records
|
|
70
|
+
the starting `cwd` plus git branch/commit when available.
|
|
71
|
+
|
|
72
|
+
### Step 1a — post-smoke panel (when criteria exist)
|
|
73
|
+
|
|
74
|
+
When grinding or the user supplied **acceptance criteria** (from the issue body at Gate 1 — not
|
|
75
|
+
ad-hoc chat text), run a **Budget-routing panel** on observable stage signals before teardown:
|
|
76
|
+
|
|
77
|
+
1. **Panel** (parallel):
|
|
78
|
+
- **requirements-match** (budget tier) — does the staged URL/behavior meet the criteria?
|
|
79
|
+
- **runtime-health** (budget tier) — console errors, failed `healthUrl`, broken UI signals
|
|
80
|
+
*(smoke observability — not the grind `correctness` hard lens)*
|
|
81
|
+
- **tests-actually-test** (budget tier) — if Playwright ran, did it exercise the changed path?
|
|
82
|
+
2. **Confirm** — get an independent second model to verify the staged URL or behavior meets the acceptance criteria.
|
|
83
|
+
3. Feed the result back to the active loop or report to the human. A blocker
|
|
84
|
+
means the stage failed smoke — do not claim the grind criterion met.
|
|
85
|
+
This panel is **not** a security clearance — grind Phase 2 still runs the `security` hard lens.
|
|
86
|
+
|
|
87
|
+
Skip when `/stage` is ad-hoc preview with no criteria.
|
|
88
|
+
|
|
89
|
+
## Step 2 — stop when done
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
mmi-cli stage stop --apply
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Stop the stage when the work is done, before switching context, or before replacing it with another stage.
|
|
96
|
+
If the user clearly wants the preview to stay up, leave it running and report that. The next `/stage` also
|
|
97
|
+
stops the previous recorded stage before starting, so a stale server does not linger between runs. For the
|
|
98
|
+
registry-derived Docker Compose default, stop also runs the recorded compose teardown (`docker compose down`)
|
|
99
|
+
from the original stage working directory.
|
|
100
|
+
|
|
101
|
+
## /stage --live — personal cloud dev stage
|
|
102
|
+
|
|
103
|
+
dev.x stages are **not standing environments**: without `--live` the dev stage is dark. `--live`
|
|
104
|
+
deploys the **current branch** to the project's dev runtime and serves it **only to your public IP** —
|
|
105
|
+
gated at the **Cloudflare edge** (#1761). The CLI detects your IP, dispatches the central
|
|
106
|
+
`tenant-deploy.yml` (stage=dev), then `tenant-control.yml` `cf-gate-allow` (an ephemeral Cloudflare WAF
|
|
107
|
+
rule scoped to your dev host + IP). The box no longer IP-gates dev. No SSH from your machine; everything
|
|
108
|
+
moves through the central workflows.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
mmi-cli stage --live # dry-run plan
|
|
112
|
+
mmi-cli stage --live --apply # deploy + gate your IP at the Cloudflare edge
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Tear it down when done — the runtime stops and the Cloudflare edge gate is removed:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
mmi-cli stage --live --down --apply
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Personal dev stages only — rc/live environments still move exclusively through the promotion train.
|
|
122
|
+
|
|
123
|
+
## Step 3 — report
|
|
124
|
+
|
|
125
|
+
The stage URL, what's running (server + tools), the workspace path (`tmp/stage/`), and the teardown command.
|
|
126
|
+
|
|
127
|
+
## Notes
|
|
128
|
+
|
|
129
|
+
- `/stage` is local — no AWS, no deploy, no board or version effect. The one cloud exception is
|
|
130
|
+
`--live`: an on-demand **dev** stage of your branch, gated to your IP at the Cloudflare edge (above).
|
|
131
|
+
- `/stage-live` is not an org command: remote rc/live environments move only through `/rcand`, `/release`, and `/hotfix`.
|
|
132
|
+
- Everything is gitignored; `/stage` never produces a tracked change.
|
|
133
|
+
- **Playwright MCP output goes to `tmp/`**, never the repo root: if you drive the Playwright MCP server,
|
|
134
|
+
pass `--output-dir tmp/playwright-mcp` (or point its output there). `.playwright-mcp/` is kept gitignored
|
|
135
|
+
by the org `.gitignore` managed block (`mmi-cli doctor`) as a safety net, so a stray default capture is
|
|
136
|
+
never tracked.
|
|
137
|
+
- `tools[]` is declarative for now; stage hardening starts/stops the main configured process. Repos with
|
|
138
|
+
extra local services should encode them behind `stage.up` until a tool runner is added.
|
|
139
|
+
- **Stale Docker bundle:** the registry-derived Docker Compose default builds with `docker compose build
|
|
140
|
+
--no-cache` before `up`. If the browser still serves an old bundle, run `mmi-cli stage stop --apply`, rerun
|
|
141
|
+
`/stage`, then inspect `tmp/stage/state.json` identity, container image labels, and compose build inputs.
|
|
142
|
+
- **Stale `.env`:** when `.env` already exists from a prior `/stage` run, `stage run` does **not** refresh it
|
|
143
|
+
from an updated `.env.example` — it warns on stderr and keeps the stale file. Delete `.env` (or merge in the
|
|
144
|
+
new keys by hand) when `.env.example` changes, then re-run `/stage`.
|
|
145
|
+
|
|
146
|
+
## Retro — one check before you finish
|
|
147
|
+
Before your final report, answer one question honestly: did **this skill's own instructions** misfire
|
|
148
|
+
this run — ambiguous wording, a misleading message, or an environment failure it should have warned
|
|
149
|
+
about? (Process only — never the user's code or task; e.g. a teardown that left a port bound, or a
|
|
150
|
+
Playwright output path aimed at the repo root.) If yes, file **one** lesson and move on; a clean run is
|
|
151
|
+
silent (hard cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR —
|
|
152
|
+
never edit the skill live; the retro is advisory, so if the call fails, note it and continue:
|
|
153
|
+
`mmi-cli learning skill-lesson --skill stage --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worktree
|
|
3
|
+
description: Orchestrate a worktree from create to landed PR, tied to board status.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**Host-native invocation:** Claude `/mmi:worktree` · Codex `$mmi:worktree` · jervcode/Kimi `/skill:worktree` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
|
|
7
|
+
|
|
8
|
+
# /worktree — create → work → land
|
|
9
|
+
|
|
10
|
+
Drive an item through an isolated worktree: cut it from latest `development`, claim the board item, do the
|
|
11
|
+
work, open and land the PR, and clean up — with the board moving automatically at each boundary. One
|
|
12
|
+
worktree per session; everything under `../mmi-worktrees/` is ephemeral and sweepable.
|
|
13
|
+
|
|
14
|
+
## Step 1 — create + provision
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
mmi-cli worktree create <owner/repo#N> --claim --from origin/development
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`worktree create` cuts the branch from `origin/development` (fetched first) and provisions it: installs
|
|
21
|
+
deps (`npm ci`) and copies local-only config (`.claude/settings.local.json`) a fresh checkout lacks. Use
|
|
22
|
+
`--from <ref>` for a non-default base, `--path <path>` to override the location. An existing worktree that
|
|
23
|
+
lost its deps re-provisions with `mmi-cli worktree setup [path]` (the SessionStart hook fires this
|
|
24
|
+
automatically).
|
|
25
|
+
|
|
26
|
+
The issue-ref form derives `<issue-number>-<short-slug>`, assigns the item, and moves it to In Progress.
|
|
27
|
+
|
|
28
|
+
`worktree create` also leases the tree to the **creating session** (#4328). A later `jerv-cli lane submit
|
|
29
|
+
--dir` (or any governed seat) refuses an already-held host lease and will not commandeer it. Before handing
|
|
30
|
+
the tree to a governed seat, release that lease first — safe once the session's own edits in that tree are
|
|
31
|
+
committed and pushed: `jerv-cli lease close --ref <worktree path>` (closes every lease on that ref; no id
|
|
32
|
+
lookup needed). To inspect what's held before closing, `jerv-cli lease list` shows id, ref, and owner.
|
|
33
|
+
|
|
34
|
+
## Step 2 — claim + work
|
|
35
|
+
|
|
36
|
+
Work in the provisioned worktree. Exercise the change with `/stage` (a local stage is bound to the worktree that
|
|
37
|
+
started it — stop it before switching worktrees). Sequential related items in one session reuse the active
|
|
38
|
+
worktree; do not churn one worktree per issue unless a true parallel or PR boundary needs it.
|
|
39
|
+
|
|
40
|
+
## Step 3 — open the PR
|
|
41
|
+
|
|
42
|
+
Push the branch and open the PR against `development`. The board moves to In Review automatically on PR
|
|
43
|
+
open — never move it by hand.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git push origin <branch>:<branch> # explicit feature refspec — see below
|
|
47
|
+
mmi-cli devops pr create --title "<title>" --body-file .jerv/PR_BODY.md --base development
|
|
48
|
+
mmi-cli devops pr checks-wait <PR-number> # wait for required CI to go green
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Write the PR body under `.jerv/` (#4405), never at the worktree root. `.jerv/` is the agent-artifact
|
|
52
|
+
prefix `worktree land` treats as removable; a stray untracked `PR_BODY.md` anywhere else classifies the
|
|
53
|
+
tree as `untracked-files` and Step 4 then skips cleanup entirely.
|
|
54
|
+
|
|
55
|
+
The push must be the explicit `<branch>:<branch>` refspec (the branch Step 1 derived, e.g.
|
|
56
|
+
`git push origin 3795-my-slice:3795-my-slice`). The #1660 protected-push gate denies the
|
|
57
|
+
`HEAD`/remote-only form (`git push -u origin HEAD`) — its target cannot be proven safe. And never
|
|
58
|
+
share the push with staging or commit verbs in one compound command: the deny applies to the whole
|
|
59
|
+
command, so a refused `git add … && git commit … && git push …` chain discards the add and commit
|
|
60
|
+
with it. Stage, commit, and push as separate commands.
|
|
61
|
+
|
|
62
|
+
If the branch adds, removes, or renames any `docs/` file, `pr create` refuses with a stale
|
|
63
|
+
`docs/index.md` (#4092) — run `mmi-cli oracle docs index --write`, commit `docs/index.md`, and push again
|
|
64
|
+
before retrying.
|
|
65
|
+
|
|
66
|
+
## Step 4 — land + clean up
|
|
67
|
+
|
|
68
|
+
Under standing go (green CI, CI-gated PR) land to `development`. **Invoke `mmi-cli devops pr land` from the
|
|
69
|
+
primary checkout** (or any cwd that is not the PR worktree) (#4549). Landing while cwd is still inside
|
|
70
|
+
the worktree that cleanup removes can merge successfully and still exit 1 with
|
|
71
|
+
`cleanupError: … Unable to read current working directory` — post-merge `gh`/`git` follow-ups then have
|
|
72
|
+
no readable cwd even when removal itself chdir'd away (#4140).
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
cd <primary-checkout> # e.g. the main MMI-Hub clone — not the slice worktree
|
|
76
|
+
mmi-cli devops pr land <PR-number>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`pr land` waits for checks, squash-merges, and does the full cleanup at the branch boundary: removes the
|
|
80
|
+
worktree, deletes the merged branch (local + origin), and prunes tracking refs. A worktree the IDE has
|
|
81
|
+
locked is queued and retried by `mmi-cli worktree gc sweep-deferred`. Self-authored merges need an explicit
|
|
82
|
+
per-session merge grant — ask early if you don't have one.
|
|
83
|
+
|
|
84
|
+
**`not-attempted (untracked-files)` (#3500 / #4405):** the usual cause of a merged PR that left its
|
|
85
|
+
worktree behind. Any untracked path outside `.jerv/` — a hand-written `PR_BODY.md`, notes, a scratch
|
|
86
|
+
script — makes removal refuse, because an unadded file can be real work. Delete or move the file, then
|
|
87
|
+
re-run cleanup **from inside the worktree** (`worktree land` has no `--path`; it acts on the tree you
|
|
88
|
+
are standing in):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
mmi-cli worktree land --apply
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Cwd-safe removal (#4140):** `worktree land --apply` (what `pr land` runs) releases the process cwd to
|
|
95
|
+
the primary checkout before deleting the tree (#1444/#2747) so Windows `rmdir` is not `EBUSY`. That does
|
|
96
|
+
**not** replace the agent rule above: still start `pr land` from the primary checkout (#4549). Prefer
|
|
97
|
+
primary cwd also when running `worktree land` manually outside `pr land`. If removal still fails (IDE
|
|
98
|
+
lock, antivirus), the deferred sweep retries from a safe cwd.
|
|
99
|
+
|
|
100
|
+
**No main lineage on a squashable development PR (#4365 / #4371):** never `git merge -s ours` (or otherwise
|
|
101
|
+
merge) a main-parented port commit into a development PR that will squash. Squash folds second-parent
|
|
102
|
+
trailers — including foreign `Closes #N` — into the development squash body, closes the wrong issues, and
|
|
103
|
+
trips the closing-keyword land guard. Keep the cherry-pick / main-clean source on a separate branch or ref;
|
|
104
|
+
land only development-tree changes on the development PR. To record a pushed main-clean SHA for a later
|
|
105
|
+
`/hotfix --from` without polluting squash parents, prefer a marker-only commit on the development branch
|
|
106
|
+
that references that SHA in the message — do not pull the main commit into the squashable parent list.
|
|
107
|
+
This does not weaken the `#3167` merge floor on `/hotfix` and `/release` (never squash a tagged commit;
|
|
108
|
+
alignment / roll-forward stays a true merge).
|
|
109
|
+
|
|
110
|
+
**Stay-open phrasing (JC#495 / #3718):** GitHub's closing-keyword parser is negation-blind. `Does not
|
|
111
|
+
close #N` still contains `close #N` and **closes the issue** on merge/squash (measured: Jerv-JervCode
|
|
112
|
+
PR #493 closed #487). When an issue must stay open, never put `close`/`closes`/`fix`/`resolve` + `#N`
|
|
113
|
+
in the PR body, commit message, or merge message — use `Part of #N`, `Refs #N`, or `leaves #N open` only.
|
|
114
|
+
`mmi-cli devops pr create` rewrites common negated phrases; `pr land` / `pr merge` still refuse any remaining
|
|
115
|
+
negation-blind close. `--force` only when those targets should actually close.
|
|
116
|
+
|
|
117
|
+
If a PR already inherited closed-issue `Closes` keywords from that anti-pattern and `pr land` / `pr merge`
|
|
118
|
+
refuses, land with `--force` only when those inherited targets are already closed — prefer preventing the
|
|
119
|
+
pollution above over relying on `--force`.
|
|
120
|
+
|
|
121
|
+
## Step 5 — record + next
|
|
122
|
+
|
|
123
|
+
Record durable decisions on the issue or PR, then check the board again:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
mmi-cli oracle wave status # remaining worktrees, open PRs, local stages at a glance
|
|
127
|
+
mmi-cli oracle next # the next actionable item
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Notes
|
|
131
|
+
|
|
132
|
+
- Cut worktrees only at `../mmi-worktrees/<RepoName>/<branch>` (#3471) — generic helpers that force `.claude/worktrees/` or
|
|
133
|
+
`.worktrees/` are not the MMI path.
|
|
134
|
+
- Multiple independent items → one worktree each, run in parallel, one PR per item (`/mmi` Leverage).
|
|
135
|
+
- Never land to a protected/release branch here — that is `/rcand` and `/release`.
|
|
136
|
+
- **Keep Cursor on the primary checkout (#4489 / #4901).** Edit the worktree without
|
|
137
|
+
`move_agent_to_root` into the slice — moving the workspace root pins the session so land/gc
|
|
138
|
+
refuse forever. If Cursor’s agent workspace is still rooted at the slice, `worktree land` /
|
|
139
|
+
`worktree gc --apply` / doctor gc **refuse** to delete that directory (and queue deferred
|
|
140
|
+
removal). On a Cursor agent host, land/gc fail closed when the active root cannot be resolved:
|
|
141
|
+
open the primary first, or set `MMI_ACTIVE_WORKSPACE_ROOT` to the primary path. After Cursor is
|
|
142
|
+
on the primary, `mmi-cli worktree gc sweep-deferred` finishes cleanup.
|
|
143
|
+
|
|
144
|
+
## Retro — one check before you finish
|
|
145
|
+
Before your final report, answer one question honestly: did **this skill's own instructions** misfire
|
|
146
|
+
this run — ambiguous wording, a misleading message, or an environment failure it should have warned
|
|
147
|
+
about? (Process only — never the user's code or task; e.g. a create that branched from a stale base, or a
|
|
148
|
+
land that left a worktree behind.) If yes, file **one** lesson and move on; a clean run is silent (hard
|
|
149
|
+
cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR — never edit
|
|
150
|
+
the skill live; the retro is advisory, so if the call fails, note it and continue:
|
|
151
|
+
`mmi-cli learning skill-lesson --skill worktree --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
|