@mutmutco/cursor-plugin 4.2.7 → 4.3.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.
Files changed (35) hide show
  1. package/.cursor-plugin/plugin.json +2 -3
  2. package/package.json +1 -1
  3. package/scripts/edit-tool-paths.mjs +4 -4
  4. package/skills/bootstrap/SKILL.md +2 -2
  5. package/skills/bootstrap/seeds/README.template.md +2 -2
  6. package/skills/bootstrap/seeds/gate.template.yml +5 -5
  7. package/skills/bootstrap/seeds/manifest.json +1 -0
  8. package/skills/bootstrap/seeds/test-policy.template.json +4 -0
  9. package/skills/hotfix/SKILL.md +1 -1
  10. package/skills/rcand/SKILL.md +1 -1
  11. package/skills/release/SKILL.md +34 -9
  12. package/skills/secrets/SKILL.md +1 -1
  13. package/skills/stage/SKILL.md +1 -1
  14. package/bin/mmi-hook +0 -2
  15. package/bin/mmi-hook-console.cmd +0 -16
  16. package/bin/mmi-hook.exe +0 -0
  17. package/hooks/cursor-hooks.json +0 -26
  18. package/scripts/command-ladder-core.mjs +0 -339
  19. package/scripts/command-ladder-gate.mjs +0 -126
  20. package/scripts/deny-gate-crash.mjs +0 -179
  21. package/scripts/env-write-lint.mjs +0 -146
  22. package/scripts/hook-io.mjs +0 -22
  23. package/scripts/hook-policy.mjs +0 -78
  24. package/scripts/hook-run.mjs +0 -434
  25. package/scripts/hook-trace.mjs +0 -151
  26. package/scripts/pretooluse-shell-gates.mjs +0 -720
  27. package/scripts/secret-echo-lint.mjs +0 -177
  28. package/scripts/test-command-policy-core.mjs +0 -294
  29. package/scripts/throttle-core.mjs +0 -332
  30. package/scripts/vault-edit-gate.mjs +0 -94
  31. package/skills/browser-automation/SKILL.md +0 -122
  32. package/skills/mmi/SKILL.md +0 -544
  33. package/skills/mmi-doctor/SKILL.md +0 -66
  34. package/skills/mmi-resume/SKILL.md +0 -123
  35. package/skills/onboard/SKILL.md +0 -72
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mmi",
3
- "version": "4.2.7",
3
+ "version": "4.3.0",
4
4
  "mmiCompat": "4.x",
5
5
  "description": "MMI workflow skills and organisation gates for Cursor.",
6
6
  "author": {
@@ -16,6 +16,5 @@
16
16
  "workflow",
17
17
  "organisation"
18
18
  ],
19
- "skills": "./skills/",
20
- "hooks": "./hooks/cursor-hooks.json"
19
+ "skills": "./skills/"
21
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cursor-plugin",
3
- "version": "4.2.7",
3
+ "version": "4.3.0",
4
4
  "description": "MMI workflow skills and organisation gates for Cursor.",
5
5
  "author": {
6
6
  "name": "MMI Future",
@@ -4,9 +4,9 @@
4
4
  // whole patch in `tool_input.command` — there is no `file_path` at all. A gate that reads only
5
5
  // `file_path` therefore sees nothing to check and exits 0: on Codex the vault-edit gate FAILED OPEN and
6
6
  // `apply_patch` could create a `.env`, while the guide claimed the path was protected (cross-vendor
7
- // check, openai-sol). Matching `apply_patch` in a hook manifest does not rewrite the payload.
7
+ // check, openai-sol). Matching `apply_patch` in a host manifest does not rewrite the payload.
8
8
  //
9
- // One extractor, used by every edit-shaped hook, so a new host contract is taught in one place.
9
+ // One extractor, used by every edit-shaped integration, so a new host contract is taught in one place.
10
10
 
11
11
  /** Tool names that mean "this call writes files". `StrReplace` is Cursor's in-place edit tool: it
12
12
  * carries its target in `tool_input.path` and was outside this set, so even once the adapter matched
@@ -63,7 +63,7 @@ const SHELL_WRAPPERS = new Set(['bash', 'sh', 'zsh', 'dash', 'pwsh', 'powershell
63
63
  export function invokesApplyPatch(command) {
64
64
  if (typeof command !== 'string' || !command) return false;
65
65
  // Redirects may abut the command word — `apply_patch<<'EOF'` and `apply_patch<<<"$P"` are both valid
66
- // shell, and both hid the command name from a whitespace tokenizer (cross-vendor check, kimi-k3).
66
+ // shell, and both hid the command name from a whitespace tokenizer (cross-vendor check).
67
67
  // Separating the operators first is what makes the command word its own token in every form.
68
68
  const spaced = command.replace(/([<>]+)/g, ' $1 ');
69
69
  for (const segment of spaced.split(/\|\||&&|[;&|\n]|<<-?\s*['"]?\w+['"]?/)) {
@@ -85,7 +85,7 @@ export function invokesApplyPatch(command) {
85
85
  /**
86
86
  * Every file path a tool call writes to. Returns `[]` for non-edit tools and unreadable payloads —
87
87
  * callers treat an empty list as "nothing to check", exactly as they treated a missing `file_path`.
88
- * @param {{ tool_name?: unknown, tool_input?: Record<string, unknown> }} input raw hook payload
88
+ * @param {{ tool_name?: unknown, tool_input?: Record<string, unknown> }} input raw tool payload
89
89
  * @returns {string[]}
90
90
  */
91
91
  export function editedPaths(input) {
@@ -3,7 +3,7 @@ name: bootstrap
3
3
  description: Provision a repo into the org with board, registry, rules, and plugin setup.
4
4
  ---
5
5
 
6
- **Host-native invocation:** Claude `/mmi:bootstrap` · Codex `$mmi:bootstrap` · jervcode/Kimi `/skill:bootstrap` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
6
+ **Host-native invocation:** Claude `/mmi:bootstrap` · Codex `$mmi:bootstrap` · Cursor `/bootstrap` · jervcode `/bootstrap` · Hermes skill_view. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
7
 
8
8
  # /bootstrap — provision a repo into the org
9
9
 
@@ -506,7 +506,7 @@ mmi-cli devops bootstrap apply "$OWNER/$REPO" --class deployable \
506
506
  Tenant-container repos must carry a `docker-compose.yml` and Dockerfile that build from the shipped source
507
507
  archive; the train does not ship a prebuilt `dist/`. **Bootstrap seeds both files** for
508
508
  `deployModel: tenant-container` from `skills/bootstrap/seeds/` (rendered from
509
- `docs/Reference/tenant-runtime/docker-compose.yml` and `docs/Reference/tenant-runtime/Dockerfile`). The box
509
+ the bootstrap seed Dockerfile and compose templates). The box
510
510
  writes the release `.env` from the registry + vault at deploy time; the compose file carries `env_file: .env`
511
511
  and the app reads plain env vars — it must **not** self-load SSM and must **not** ship a committed `.env`.
512
512
 
@@ -1,7 +1,7 @@
1
1
  # {{REPO_NAME}}
2
2
 
3
3
  > One paragraph: what this repo **is** — the product/service and who it's for. (Write fresh — D35: do
4
- > not carry a legacy README over verbatim; the old docs are archived under `docs/Archive/`.)
4
+ > not carry a legacy README over verbatim; write the current product map.)
5
5
 
6
6
  ## What's here
7
7
 
@@ -11,7 +11,7 @@
11
11
 
12
12
  (Owner/operator — who runs this day to day.) Access follows the MMI Future three-level model: read for
13
13
  org members, developer as GitHub `write`, project-admin as `write` plus train-branch allowlist. Authority
14
- detail → [org-architecture §4](https://github.com/mutmutco/MMI-Hub/blob/development/docs/org-architecture.md);
14
+ detail → [Hub architecture](https://github.com/mutmutco/MMI-Hub/blob/main/architecture.md);
15
15
  access runbook → [repo-access](https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/repo-access.md).
16
16
 
17
17
  ## Agent context
@@ -28,7 +28,7 @@ name: gate
28
28
  # Do not add a windows-compat (or any other) job that runs-on GH-hosted Windows. Linux self-hosted
29
29
  # (mmi-live / mmi-heavy) is the only CI merge gate. Rare Windows proof stays on the owner's machine or
30
30
  # a future self-hosted Windows runner — never windows-latest.
31
- # (seed touch: keep kilo mirror + BOM digests in lockstep after classifier land.)
31
+ # (seed touch: keep BOM digests in lockstep after classifier land.)
32
32
  on:
33
33
  pull_request:
34
34
  push:
@@ -79,17 +79,17 @@ jobs:
79
79
  offenders=$(mktemp)
80
80
  trap 'rm -f "$offenders"' EXIT
81
81
  {
82
- git grep -Il $'\r' -- '*.sh' || true
83
- git grep -Il '^#!' | while IFS= read -r path; do
82
+ git grep --cached -Il $'\r' -- '*.sh' || true
83
+ git grep --cached -Il '^#!' | while IFS= read -r path; do
84
84
  case "$path" in
85
85
  *.sh) ;;
86
- *) git grep -Il $'\r' -- "$path" || true ;;
86
+ *) git grep --cached -Il $'\r' -- "$path" || true ;;
87
87
  esac
88
88
  done
89
89
  } | sort -u > "$offenders"
90
90
  if [[ -s "$offenders" ]]; then
91
91
  while IFS= read -r path; do
92
- echo "::error title=Line-ending guard::CR byte in tracked script: $path. Run git add --renormalize . with this repo's .gitattributes."
92
+ printf "::error title=Line-ending guard::CR byte in tracked script: %s. Run git add --renormalize . with this repo's .gitattributes.\n" "$path"
93
93
  done < "$offenders"
94
94
  exit 1
95
95
  fi
@@ -15,6 +15,7 @@
15
15
  { "target": "README.md", "source": "seed:README.template.md", "ownership": "repo", "classes": ["deployable", "content"], "managedBlock": { "source": "seed:readme-mmi-developer-environment.block.md", "begin": "<!-- mmi-developer-environment:start -->", "end": "<!-- mmi-developer-environment:end -->" } },
16
16
  { "target": "architecture.md", "source": "seed:architecture.template.md", "ownership": "repo", "classes": ["deployable", "content"] },
17
17
  { "target": "docs/decisions/README.md", "source": "seed:decisions-readme.template.md", "ownership": "repo", "classes": ["deployable", "content"] },
18
+ { "target": "test-policy.json", "source": "seed:test-policy.template.json", "ownership": "repo", "classes": ["deployable", "content"] },
18
19
  { "target": "docs/Guides/google-login.md", "source": "seed:google-login.template.md", "ownership": "repo", "classes": ["deployable"], "projectTypes": ["web-app"] },
19
20
  { "target": "docker-compose.yml", "source": "seed:docker-compose.template.yml", "ownership": "repo", "classes": ["deployable"], "deployModels": ["tenant-container"] },
20
21
  { "target": "Dockerfile", "source": "seed:Dockerfile.template", "ownership": "repo", "classes": ["deployable"], "deployModels": ["tenant-container"] }
@@ -0,0 +1,4 @@
1
+ {
2
+ "mandatory": [],
3
+ "protected": []
4
+ }
@@ -3,7 +3,7 @@ name: hotfix
3
3
  description: Promote an already-merged development fix to main and production through the gated Hub train.
4
4
  ---
5
5
 
6
- **Host-native invocation:** Claude `/mmi:hotfix` · Codex `$mmi:hotfix` · jervcode/Kimi `/skill:hotfix` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
6
+ **Host-native invocation:** Claude `/mmi:hotfix` · Codex `$mmi:hotfix` · Cursor `/hotfix` · jervcode `/hotfix` · Hermes skill_view. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
7
 
8
8
  # /hotfix — promote a development fix to main + prod
9
9
 
@@ -3,7 +3,7 @@ name: rcand
3
3
  description: Promote development to a release-candidate branch.
4
4
  ---
5
5
 
6
- **Host-native invocation:** Claude `/mmi:rcand` · Codex `$mmi:rcand` · jervcode/Kimi `/skill:rcand` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
6
+ **Host-native invocation:** Claude `/mmi:rcand` · Codex `$mmi:rcand` · Cursor `/rcand` · jervcode `/rcand` · Hermes skill_view. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
7
 
8
8
  # /rcand — promote development to release-candidate
9
9
 
@@ -3,7 +3,7 @@ name: release
3
3
  description: Ship rc or direct-track development to main and production.
4
4
  ---
5
5
 
6
- **Host-native invocation:** Claude `/mmi:release` · Codex `$mmi:release` · jervcode/Kimi `/skill:release` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
6
+ **Host-native invocation:** Claude `/mmi:release` · Codex `$mmi:release` · Cursor `/release` · jervcode `/release` · Hermes skill_view. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
7
 
8
8
  **Argument (#4929):** `/release minor`, `/release major`, or `/release patch` — a bare bump word, never a
9
9
  `--flag` or env var. Map it to `MMI_BUMP_INTENT` exported for the whole run (the train and
@@ -414,7 +414,7 @@ What the fold bumps, by repo:
414
414
  `scripts/check-hook-contract.mjs`, which executes host adapter commands as a native argv without a
415
415
  shell (#5605), so a checkout path that contains spaces is supported; the spaced-path argv regression
416
416
  is pinned in `infra/plugin-set.test.mjs`. Publication and staging both derive from `surfaces.json`;
417
- there is no second package list. Claude, Codex, Kimi, Cursor, and Kilo are active.
417
+ there is no second package list. Claude, Codex, Cursor, jervcode, and Hermes are active.
418
418
  - **App-style repos with a root `package.json`** (most products): the manifest + lockfile version via
419
419
  `npm version --no-git-tag-version`, kept in lockstep with the release tag.
420
420
  - **Repos with neither:** nothing to fold — the tag is the version.
@@ -603,8 +603,7 @@ Before running `--apply` for MMI-Hub, resolve the real tag first:
603
603
  `v2.43.0`) in summaries, Slack, chat, and the final report; never a placeholder like `vX.Y.0` or
604
604
  `v0.x.0`.
605
605
 
606
- **Write the announcement per `docs/Guides/release-notes.md`** — the canonical org guide for every
607
- release type. It is the authoritative contract; follow it and its checklist before posting. Key rules:
606
+ **Write the announcement from the actual release evidence** summarize the user-visible change, required action, and recovery path before posting. Key rules:
608
607
 
609
608
  - **Audience is the whole org** — no issue/PR numbers, repo names, commit SHAs, or internal tool jargon.
610
609
  - **Scale length and depth by release type** (major > minor > patch > hotfix): a major is a broad,
@@ -664,6 +663,11 @@ mmi-cli oracle org project sync-info --apply # omit --apply for the read-only
664
663
  release: the alignment PR's own `cli`/`infra`/`docs` checks are still running, so a plain immediate merge is
665
664
  policy-blocked — `--auto` merges once they pass. If you await with `--wait` instead, still pass `--merge`
666
665
  (`mmi-cli devops pr merge <number> --wait --merge`); `--wait` alone must not land the alignment PR. Never force.
666
+ A pre-#5903 CLI can true-merge this PR and still exit 1 with `mergeStatus: partial-cleanup` after GitHub
667
+ refuses to delete its protected `main` head. Classify that legacy receipt from the live PR: `MERGED` with
668
+ merge method `MERGE` means alignment succeeded and protected-main retention is expected. Never delete
669
+ `main`, retry the release, or call the alignment failed. Current CLIs report the same retention as
670
+ `remoteBranch.status: retained-protected` with `mergeStatus: merged`.
667
671
  - Full-track repos: `mmi-cli devops release --apply` already aligned `rc` to the released `main` (#1036 — the
668
672
  push runs inside the authority-gated train; the result reports it as `rcAlignment`). No manual `rc`
669
673
  push — if the result reports a failed alignment, investigate and rerun via the train, never bare-push.
@@ -673,11 +677,10 @@ mmi-cli oracle org project sync-info --apply # omit --apply for the read-only
673
677
  (`checkout`, #2340) and fast-forwards the others (`localSync`) — non-destructively: only a true
674
678
  fast-forward moves a branch; a diverged local branch is reported and left untouched (never a force).
675
679
  The one branch it **cannot** sync at release time is a check-gated `development` whose roll-forward is a
676
- `pr-pending` alignment PR (Hub) — that PR merges on origin *after* the release returns. That case is
677
- healed automatically by the **next session**: `mmi-cli plugin session-start` (invoked by the resume
678
- channel #3630 took SessionStart off the hook surface) runs the
679
- same fast-forward first thing, so a stale local `development`/`main`/`rc` self-heals to origin before you
680
- work — no manual `git pull`. Nothing here ever blocks or fails the release.
680
+ `pr-pending` alignment PR (Hub) — that PR merges on origin *after* the release returns. Once that PR
681
+ lands, fast-forward the local branch yourself (`git pull --ff-only` on `development`) #5908 retired
682
+ the session-start self-heal along with the rest of the hook-era plumbing. Nothing here ever blocks or
683
+ fails the release.
681
684
  - **Read the `checkout:` clause — it does not always say "returned" (#4006).** The report always prints
682
685
  one, and restoration is deliberately skipped rather than forced when the repo is not in a fit state.
683
686
  A release leaves the repo on `main` until the train moves it back, so a non-`returned` outcome means
@@ -754,6 +757,28 @@ Release-blocking for Hub tooling changes: every public artifact must match the r
754
757
  identity. Manual fallback if CI can't publish: `node scripts/release-distribution.mjs publish "X.Y.0"`
755
758
  from a machine with npm auth.
756
759
 
760
+ **Windows WebAuthn/passkey exception (#5920).** Normal publishing remains non-interactive and token-based;
761
+ headless and CI hosts keep using automation tokens. When any npm write requires a human desktop passkey on
762
+ Windows, **never run it from the agent shell**. Write the package loop to a durable `.cmd` outside disposable
763
+ worktrees (for example, `C:\Users\<you>\Documents\mmi-npm-write.cmd`):
764
+ ```bat
765
+ @echo off
766
+ set "failures=%~dpn0.failures.txt"
767
+ del "%failures%" 2>nul
768
+ for %%P in (@scope/package-a @scope/package-b) do (
769
+ call npm deprecate "%%P@*" "replacement message"
770
+ if errorlevel 1 >>"%failures%" echo %%P
771
+ )
772
+ if exist "%failures%" type "%failures%"
773
+ ```
774
+ Launch it visibly and leave the window open, then let the human tap once for each npm invocation:
775
+ ```powershell
776
+ Start-Process cmd -ArgumentList '/k', 'C:\Users\<you>\Documents\mmi-npm-write.cmd'
777
+ ```
778
+ Ignore any EOTP browser URL npm prints; it does not complete this passkey flow. Treat the failure file as the
779
+ per-package receipt and verify the corresponding registry state, for example
780
+ `npm view <pkg> deprecated --prefer-online` after `npm deprecate`.
781
+
757
782
  **npm tarball identity is packer-sensitive (#5366 / #5251).** `verify` without `--skip-npm-view`
758
783
  compares the registry's served `dist.integrity` (SSRI) against a live `npm pack` on THIS machine
759
784
  (`packedIntegrityBySurface`). A tarball SSRI covers file modes and packer (npm version) behavior, so
@@ -3,7 +3,7 @@ name: secrets
3
3
  description: Manage the full own-project vault and granted org-infra secret names.
4
4
  ---
5
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.
6
+ **Host-native invocation:** Claude `/mmi:secrets` · Codex `$mmi:secrets` · Cursor `/secrets` · jervcode `/secrets` · Hermes skill_view. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
7
 
8
8
  # /secrets — two-tier project secrets
9
9
 
@@ -3,7 +3,7 @@ name: stage
3
3
  description: Run a local test stage, or a personal cloud dev stage with --live.
4
4
  ---
5
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.
6
+ **Host-native invocation:** Claude `/mmi:stage` · Codex `$mmi:stage` · Cursor `/stage` · jervcode `/stage` · Hermes skill_view. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
7
 
8
8
  # /stage — local test environment
9
9
 
package/bin/mmi-hook DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/sh
2
- exec node "$@"
@@ -1,16 +0,0 @@
1
- :; exec node "$@" # sh half: cmd sees a label, POSIX shells exec node exactly like bin/mmi-hook
2
- :: <#
3
- @echo off
4
- rem sh/cmd/PowerShell polyglot console launcher (#4878/#5591), stored CRLF: cmd mis-parses LF-only batch files.
5
- rem The trailing # on line 1 keeps the CR out of the sh exec. Line 2 opens a PowerShell block comment so
6
- rem `powershell -File` can parse this trampoline. Line 2 is a LABEL, not `rem`: echo is still on until
7
- rem line 3, so `rem <#` was echoed as `<cwd>>rem <# ` onto every hook stdout ahead of the payload, and
8
- rem cmd never echoes a label (#5828). The space in `:: <#` is load-bearing: `::<#` stops PowerShell parsing.
9
- rem PowerShell never waits on the GUI-subsystem mmi-hook.exe, so the batch half enters that same windowless
10
- rem exe through cmd (which PowerShell does wait on). Referenced WITH the .cmd extension: an extensionless
11
- rem sibling would win PowerShell path resolution and run as neither.
12
- "%~dp0mmi-hook.exe" %*
13
- exit /b %errorlevel%
14
- #>
15
- & (Join-Path $PSScriptRoot 'mmi-hook.exe') @args
16
- exit $LASTEXITCODE
package/bin/mmi-hook.exe DELETED
Binary file
@@ -1,26 +0,0 @@
1
- {
2
- "version": 1,
3
- "hooks": {
4
- "preToolUse": [
5
- {
6
- "command": "'${CURSOR_PLUGIN_ROOT}/bin/mmi-hook-console.cmd' '${CURSOR_PLUGIN_ROOT}/scripts/hook-run.mjs' --surface cursor --gate command-ladder",
7
- "matcher": "Shell",
8
- "timeout": 10,
9
- "failClosed": true
10
- },
11
- {
12
- "command": "'${CURSOR_PLUGIN_ROOT}/bin/mmi-hook-console.cmd' '${CURSOR_PLUGIN_ROOT}/scripts/hook-run.mjs' --surface cursor --gate vault-edit",
13
- "matcher": "Write",
14
- "timeout": 5,
15
- "failClosed": true
16
- },
17
- {
18
- "command": "'${CURSOR_PLUGIN_ROOT}/bin/mmi-hook-console.cmd' '${CURSOR_PLUGIN_ROOT}/scripts/hook-run.mjs' --surface cursor --gate vault-edit",
19
- "matcher": "StrReplace",
20
- "timeout": 5,
21
- "failClosed": true
22
- }
23
- ],
24
- "postToolUse": []
25
- }
26
- }