@mutmutco/claude-plugin 4.5.163 → 4.5.165

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mmi",
3
3
  "displayName": "MMI",
4
- "version": "4.5.163",
4
+ "version": "4.5.165",
5
5
  "mmiCompat": "4.x",
6
6
  "description": "MMI workflow skills and org gates delivery.",
7
7
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -14631,6 +14631,15 @@ function latestClaimMarker(comments) {
14631
14631
  }
14632
14632
  return void 0;
14633
14633
  }
14634
+ function latestClaimRelease(comments) {
14635
+ for (let i = comments.length - 1; i >= 0; i -= 1) {
14636
+ const body = comments[i].body;
14637
+ const releases = parseClaimReleases(body);
14638
+ if (!parseClaimMarkers(body).length && !releases.length) continue;
14639
+ return latestClaimMarker([comments[i]]) ? void 0 : releases[releases.length - 1];
14640
+ }
14641
+ return void 0;
14642
+ }
14634
14643
  function formatClaimAge(ageMs) {
14635
14644
  if (ageMs === void 0 || !Number.isFinite(ageMs) || ageMs < 0) return "unknown age";
14636
14645
  const minutes = Math.floor(ageMs / 6e4);
@@ -14838,6 +14847,7 @@ async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
14838
14847
  const comments = await client.restPaginate(`repos/${repo}/issues/${number}/comments`);
14839
14848
  const rows = comments.map((comment) => ({ body: comment.body ?? "", login: comment.user?.login ?? void 0 }));
14840
14849
  out.marker = latestClaimMarker(rows);
14850
+ if (!out.marker) out.releasedAt = latestClaimRelease(rows)?.ts;
14841
14851
  for (let i = comments.length - 1; i >= 0; i -= 1) {
14842
14852
  if (comments[i].created_at && !latestClaimMarker([rows[i]])) {
14843
14853
  out.lastCommentAt = comments[i].created_at;
@@ -14919,8 +14929,9 @@ function liveEvidenceLines(evidence, repo) {
14919
14929
  live.push(evidence.lastCommentAt && Date.parse(evidence.lastCommentAt) > Date.parse(evidence.marker.ts) ? `claim marker from ${marker} is ${formatClaimAge(evidence.markerAgeMs)} old but the issue was commented ${formatClaimAge(idleMs)} ago (session not locally probeable)` : `claim marker from ${marker} is only ${formatClaimAge(evidence.markerAgeMs)} old (session not locally probeable)`);
14920
14930
  }
14921
14931
  }
14922
- if (evidence.openPr && workIsLive(evidence.openPrAt)) live.push(evidence.openPr);
14923
- if (evidence.branch && workIsLive(evidence.branchAt)) live.push(`live branch ${evidence.branch} on ${repo}`);
14932
+ const sinceRelease = (at) => !evidence.releasedAt || at === void 0 || Date.parse(at) > Date.parse(evidence.releasedAt);
14933
+ if (evidence.openPr && workIsLive(evidence.openPrAt) && sinceRelease(evidence.openPrAt)) live.push(evidence.openPr);
14934
+ if (evidence.branch && workIsLive(evidence.branchAt) && sinceRelease(evidence.branchAt)) live.push(`live branch ${evidence.branch} on ${repo}`);
14924
14935
  return live;
14925
14936
  }
14926
14937
  function staleWorkLines(evidence) {
@@ -19051,10 +19062,10 @@ var rollout_plan_default = {
19051
19062
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
19052
19063
  },
19053
19064
  baseline: {
19054
- version: "4.5.163",
19055
- tag: "v4.5.163",
19056
- commit: "0d3f3c9549d7",
19057
- npm: "@mutmutco/cli@4.5.163"
19065
+ version: "4.5.165",
19066
+ tag: "v4.5.165",
19067
+ commit: "e50afbd193c3",
19068
+ npm: "@mutmutco/cli@4.5.165"
19058
19069
  },
19059
19070
  exitCriterion: "fleet-n-of-n",
19060
19071
  hubOnlyShortcut: "forbidden",
@@ -19071,14 +19082,14 @@ var rollout_plan_default = {
19071
19082
  repo: "mutmutco/mmi-hub",
19072
19083
  role: "canary",
19073
19084
  schedule: "train",
19074
- v3Target: "v4.5.163"
19085
+ v3Target: "v4.5.165"
19075
19086
  }
19076
19087
  ],
19077
19088
  rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
19078
19089
  rollback: {
19079
19090
  independent: true,
19080
- mechanism: "npm dist-tag latest -> 4.5.163 and redeploy the Hub Lambda from tag v4.5.163 (0d3f3c9549d7); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
19081
- v3Target: "v4.5.163 (@mutmutco/cli@4.5.163, tag commit 0d3f3c9549d7 \u2014 last known-good release carrying the repo-index v4-only contract)"
19091
+ mechanism: "npm dist-tag latest -> 4.5.165 and redeploy the Hub Lambda from tag v4.5.165 (e50afbd193c3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
19092
+ v3Target: "v4.5.165 (@mutmutco/cli@4.5.165, tag commit e50afbd193c3 \u2014 last known-good release carrying the repo-index v4-only contract)"
19082
19093
  }
19083
19094
  },
19084
19095
  {
@@ -33317,7 +33328,7 @@ function parseGateVar(raw) {
33317
33328
  }
33318
33329
  var SECRET_OUTPUT_PATH_RE = /^\/[A-Za-z0-9._/-]+$/;
33319
33330
  var SECRET_OUTPUT_REPO_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/;
33320
- var SECRET_OUTPUT_KEY_RE = /^[A-Z][A-Z0-9_]*$/;
33331
+ var SECRET_OUTPUT_KEY_RE = /^(?:(?:dev|rc|main)\/)?[A-Z][A-Z0-9_]*$/;
33321
33332
  function parseTenantTaskSecretOutput(raw, label) {
33322
33333
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
33323
33334
  throw new Error(`org project set: ${label} must be an object {path,vault:{repo,key},overwrite?}`);
@@ -33341,7 +33352,7 @@ function parseTenantTaskSecretOutput(raw, label) {
33341
33352
  throw new Error(`org project set: ${label}.vault.repo must be owner/name`);
33342
33353
  }
33343
33354
  if (typeof vaultMap.key !== "string" || !SECRET_OUTPUT_KEY_RE.test(vaultMap.key)) {
33344
- throw new Error(`org project set: ${label}.vault.key must be UPPER_SNAKE`);
33355
+ throw new Error(`org project set: ${label}.vault.key must be UPPER_SNAKE or <dev|rc|main>/UPPER_SNAKE`);
33345
33356
  }
33346
33357
  if (value.overwrite !== void 0 && typeof value.overwrite !== "boolean") {
33347
33358
  throw new Error(`org project set: ${label}.overwrite must be true or false`);
@@ -40963,13 +40974,30 @@ async function runPrLand(prNumber, options, deps) {
40963
40974
  }
40964
40975
  const ciPolicy = await deps.resolveCiPolicy(repo);
40965
40976
  base.ciPolicy = ciPolicy;
40977
+ const pinned = options.expectHead;
40978
+ if (pinned) base.pinnedHead = { expected: pinned };
40979
+ const headDrift = async (stage) => {
40980
+ if (!pinned) return void 0;
40981
+ if (!deps.readHeadSha) throw new Error("pinned head reader unavailable");
40982
+ const observed = await deps.readHeadSha(prNumber, repo);
40983
+ base.pinnedHead = { expected: pinned, observed };
40984
+ if (observed === pinned) return void 0;
40985
+ return {
40986
+ ...base,
40987
+ pinnedHead: { ...base.pinnedHead, refused: "drift" },
40988
+ error: `pr land #${prNumber}: head ${observed} is not the pinned ${pinned} (${stage}) \u2014 refusing to land a head that was not checked (--expect-head)`
40989
+ };
40990
+ };
40991
+ const drift = await headDrift("before landing");
40992
+ if (drift) return drift;
40966
40993
  const hasQueue = await deps.hasMergeQueue?.(repo, resolved.base) ?? false;
40967
40994
  let queueSnapshot;
40995
+ const queued = { base: resolved.base, state: "queued" };
40968
40996
  if (hasQueue) {
40969
- base.mergeQueue = { base: resolved.base, state: "queued" };
40970
40997
  if (!deps.readMergeQueue) throw new Error("merge queue reader unavailable");
40971
40998
  queueSnapshot = await deps.readMergeQueue(prNumber, repo);
40972
40999
  if (queueSnapshot.isInMergeQueue || queueSnapshot.state === "MERGED") {
41000
+ base.mergeQueue = queued;
40973
41001
  base.mergeStatus = "auto-merge-enqueued";
40974
41002
  return pollPrMergeQueue(prNumber, options, deps, base, queueSnapshot);
40975
41003
  }
@@ -40986,7 +41014,16 @@ async function runPrLand(prNumber, options, deps) {
40986
41014
  base.checksWait = await deps.waitForChecks(prNumber, repo);
40987
41015
  const waitError = checksWaitError(base.checksWait);
40988
41016
  if (waitError) return { ...base, error: waitError };
41017
+ const driftAfterChecks = await headDrift("after checks");
41018
+ if (driftAfterChecks) return driftAfterChecks;
40989
41019
  let prState = await deps.readPrState(prNumber, repo);
41020
+ if (pinned && prState.mergeStateStatus === "BEHIND") {
41021
+ return {
41022
+ ...base,
41023
+ pinnedHead: { ...base.pinnedHead, refused: "behind" },
41024
+ error: `pr land #${prNumber}: head is BEHIND ${resolved.base} and pinned by --expect-head \u2014 refusing to update it. Update and re-check the head yourself, then land with the new SHA`
41025
+ };
41026
+ }
40990
41027
  if (prState.mergeStateStatus === "BEHIND" && deps.updateBranch) {
40991
41028
  try {
40992
41029
  base.branchUpdated = await deps.updateBranch(prNumber, repo);
@@ -41010,6 +41047,7 @@ async function runPrLand(prNumber, options, deps) {
41010
41047
  return { ...base, error: merge.error ?? "merge failed" };
41011
41048
  }
41012
41049
  if (hasQueue) {
41050
+ base.mergeQueue = queued;
41013
41051
  if (merge.mergeStatus === "merged") {
41014
41052
  return queueMerged(base, deps);
41015
41053
  }
@@ -41080,6 +41118,7 @@ async function pollPrMergeQueue(pr, options, deps, base, snapshot, deadlineMs) {
41080
41118
  const result2 = {
41081
41119
  ...base,
41082
41120
  mergeQueue: { ...base.mergeQueue, state: "ejected", ...run ? { ejectedRun: run } : {} },
41121
+ ...base.pinnedHead ? { pinnedHead: { ...base.pinnedHead, refused: "ejected" } } : {},
41083
41122
  error: `merge queue ejected PR #${pr} \u2014 ${run ? `merge_group run ${run.id} ${run.conclusion} ${run.url}` : "merge_group run not found"}`
41084
41123
  };
41085
41124
  deps.queueProgress?.(result2);
@@ -41105,7 +41144,7 @@ async function landMany(prs, landOne, cleanup, hooks) {
41105
41144
  const safe = async (pr) => landOne(pr).catch((e) => ({ status: "failed", repo: "", pr, base: "", error: e.message }));
41106
41145
  return Promise.all(prs.map(async (pr) => {
41107
41146
  let result = await safe(pr);
41108
- if (prLandOutcome(result) === "ejected") {
41147
+ if (prLandOutcome(result) === "ejected" && !result.pinnedHead) {
41109
41148
  hooks?.onRetry?.(pr, result);
41110
41149
  result = { ...await safe(pr), retriedAfterEjection: true };
41111
41150
  }
@@ -41985,6 +42024,7 @@ function buildPrMergeArgs(input) {
41985
42024
  const args = ["pr", "merge", input.number, ...input.repoArgs, input.method];
41986
42025
  if (input.deleteBranch !== false) args.push("--delete-branch");
41987
42026
  if (input.bodyFile) args.push("--body-file", input.bodyFile);
42027
+ if (input.matchHeadCommit) args.push("--match-head-commit", input.matchHeadCommit);
41988
42028
  if (input.auto) args.push("--auto");
41989
42029
  return args;
41990
42030
  }
@@ -42226,12 +42266,12 @@ async function ghMergeAutoEnqueue(prNumber, repo, method, extra) {
42226
42266
  bodyText: method === "--squash" ? extra?.bodyText : void 0
42227
42267
  });
42228
42268
  try {
42229
- return await mergeAutoEnqueueWithBody(prNumber, args, method, io, overrideBody?.path, extra?.mergeQueue);
42269
+ return await mergeAutoEnqueueWithBody(prNumber, args, method, io, overrideBody?.path, extra?.mergeQueue, extra?.matchHeadCommit);
42230
42270
  } finally {
42231
42271
  overrideBody?.cleanup();
42232
42272
  }
42233
42273
  }
42234
- async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, mergeQueue = false) {
42274
+ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, mergeQueue = false, matchHeadCommit) {
42235
42275
  const headRef = (await io.gh(["pr", "view", prNumber, ...args, "--json", "headRefName", "--jq", ".headRefName"], GC_GH_TIMEOUT_MS).catch(() => "")).trim();
42236
42276
  const deleteBranch = !mergeQueue && !isProtectedBranch(headRef);
42237
42277
  const readMergeState = () => readGhPrStateWithRetry(
@@ -42251,7 +42291,7 @@ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, me
42251
42291
  return s.ok && s.state === "MERGED";
42252
42292
  },
42253
42293
  reEnqueue: async () => {
42254
- await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
42294
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile, matchHeadCommit }), GH_MUTATION_TIMEOUT_MS);
42255
42295
  }
42256
42296
  });
42257
42297
  if (confirmation === "merged") return { mergeStatus: "merged" };
@@ -42259,7 +42299,7 @@ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, me
42259
42299
  return { mergeStatus: "failed", error: "auto-merge did not stick \u2014 GitHub reported no autoMergeRequest after enqueue; retry once the PR has a pending check" };
42260
42300
  };
42261
42301
  try {
42262
- await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
42302
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile, matchHeadCommit }), GH_MUTATION_TIMEOUT_MS);
42263
42303
  } catch (e) {
42264
42304
  const message2 = String(e.message || "");
42265
42305
  if (/already been merged/i.test(message2)) return { mergeStatus: "merged" };
@@ -42267,7 +42307,7 @@ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, me
42267
42307
  if (note) return { mergeStatus: "failed", error: note };
42268
42308
  if (mergeAutoRejectedPrAlreadyClean(message2)) {
42269
42309
  try {
42270
- await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
42310
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false, deleteBranch, bodyFile, matchHeadCommit }), GH_MUTATION_TIMEOUT_MS);
42271
42311
  } catch (e2) {
42272
42312
  const m2 = String(e2.message || "");
42273
42313
  if (/already been merged/i.test(m2)) return { mergeStatus: "merged" };
@@ -42278,7 +42318,7 @@ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, me
42278
42318
  return { mergeStatus: "failed", error: `could not confirm PR state after clean-status fallback: ${afterDirect.error}` };
42279
42319
  }
42280
42320
  try {
42281
- await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
42321
+ await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile, matchHeadCommit }), GH_MUTATION_TIMEOUT_MS);
42282
42322
  } catch (e3) {
42283
42323
  const m3 = String(e3.message || "");
42284
42324
  if (/already been merged/i.test(m3)) return { mergeStatus: "merged" };
@@ -47844,9 +47884,14 @@ ${list}`);
47844
47884
  }
47845
47885
  class PrHeadBehindBaseError extends Error {
47846
47886
  }
47847
- pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").option("--timeout <minutes>", `merge-queue wait per PR (default ${PR_LAND_QUEUE_TIMEOUT_MS / 6e4} minutes, one window per ${PR_LAND_QUEUE_BATCH_SIZE} PRs \u2014 the queue's own check timeout is 60)`).option("--preserve-worktree", "after a queue merge keep the local worktree and branch (skips the post-merge cleanup)").action(async (numbers, opts) => {
47887
+ pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").option("--timeout <minutes>", `merge-queue wait per PR (default ${PR_LAND_QUEUE_TIMEOUT_MS / 6e4} minutes, one window per ${PR_LAND_QUEUE_BATCH_SIZE} PRs \u2014 the queue's own check timeout is 60)`).option("--preserve-worktree", "after a queue merge keep the local worktree and branch (skips the post-merge cleanup)").option("--expect-head <sha>", "pin the PR head to this full 40-hex SHA (#8456): checked before landing, after checks and by GitHub at enqueue (--match-head-commit); never updates a BEHIND head and never retries an ejection \u2014 it stops with pinnedHead.refused instead. One PR only").action(async (numbers, opts) => {
47848
47888
  const timeoutMinutes = opts.timeout === void 0 ? void 0 : Number(opts.timeout);
47849
47889
  if (timeoutMinutes !== void 0 && !(timeoutMinutes > 0)) return fail("pr land: --timeout must be a positive number of minutes");
47890
+ if (opts.expectHead !== void 0) {
47891
+ if (!/^[0-9a-f]{40}$/i.test(opts.expectHead)) return fail("pr land: --expect-head needs the full 40-hex head SHA");
47892
+ if (numbers.length !== 1) return fail("pr land: --expect-head pins one PR head; land one PR per call");
47893
+ opts.expectHead = opts.expectHead.toLowerCase();
47894
+ }
47850
47895
  const queueTimeoutMs = timeoutMinutes !== void 0 ? Math.round(timeoutMinutes * 6e4) : PR_LAND_QUEUE_TIMEOUT_MS * Math.ceil(numbers.length / PR_LAND_QUEUE_BATCH_SIZE);
47851
47896
  let inFlight = numbers.length;
47852
47897
  const landOne = async (numberArg) => {
@@ -47887,7 +47932,12 @@ ${list}`);
47887
47932
  }
47888
47933
  if (landClosingGuardVerdict.message) console.warn(landClosingGuardVerdict.message);
47889
47934
  let mergeQueueEnabled = false;
47890
- const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false, enqueueOnly: o.enqueueOnly, queueTimeoutMs }, {
47935
+ const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false, enqueueOnly: o.enqueueOnly, queueTimeoutMs, expectHead: o.expectHead }, {
47936
+ readHeadSha: async (prNumber, repo) => {
47937
+ const sha = (await fetchRestPrSnapshot(prNumber, repo)).headSha;
47938
+ if (!sha) throw new Error(`pr land: could not read the head SHA of PR #${prNumber}`);
47939
+ return sha.toLowerCase();
47940
+ },
47891
47941
  pollDelayMs: (attempt) => prLandPollDelayMs(attempt, inFlight),
47892
47942
  hasMergeQueue: async (repo, base) => mergeQueueEnabled = await hasPrMergeQueue(repo, base),
47893
47943
  readMergeQueue: readPrMergeQueue,
@@ -47985,9 +48035,11 @@ ${list}`);
47985
48035
  mergeAuto: (prNumber, repo) => ghMergeAutoEnqueue(prNumber, repo, "--squash", {
47986
48036
  allowedClosing: landClosingGuardInput?.closing,
47987
48037
  bodyText: landSquashBody,
47988
- mergeQueue: mergeQueueEnabled
48038
+ mergeQueue: mergeQueueEnabled,
48039
+ matchHeadCommit: o.expectHead
47989
48040
  }),
47990
- updateBranch: (prNumber, repo) => prLandUpdateBranch(prNumber, repo, Boolean(o.repo)),
48041
+ // #8456: a pinned land has no update lane at all.
48042
+ ...o.expectHead ? {} : { updateBranch: (prNumber, repo) => prLandUpdateBranch(prNumber, repo, Boolean(o.repo)) },
47991
48043
  pollMerged: async (prNumber, repo, deadlineMs) => {
47992
48044
  let lastFailure;
47993
48045
  while (Date.now() < deadlineMs) {
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.163",
3
+ "version": "4.5.165",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/claude-plugin",
3
- "version": "4.5.163",
3
+ "version": "4.5.165",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",