@mutmutco/claude-plugin 4.6.12 → 4.6.13

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.6.12",
4
+ "version": "4.6.13",
5
5
  "mmiCompat": "4.x",
6
6
  "description": "MMI workflow skills and org gates delivery.",
7
7
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -1674,6 +1674,7 @@ __export(board_write_exports, {
1674
1674
  BoardAttachError: () => BoardAttachError,
1675
1675
  addIssueToProjectViaHub: () => addIssueToProjectViaHub,
1676
1676
  boardAttachHttpError: () => boardAttachHttpError,
1677
+ createBoardIssueViaHub: () => createBoardIssueViaHub,
1677
1678
  writeHubBoardField: () => writeHubBoardField
1678
1679
  });
1679
1680
  async function writeHubBoardField(request) {
@@ -1715,6 +1716,18 @@ function boardAttachHttpError(status, detail) {
1715
1716
  const code = status === 401 ? "hub_session_missing" : status === 403 ? "forbidden" : status === 429 ? "rate_limited" : status >= 500 ? "hub_unavailable" : "refused";
1716
1717
  return new BoardAttachError(code, status, detail);
1717
1718
  }
1719
+ async function createBoardIssueViaHub(request) {
1720
+ const config = await loadConfig();
1721
+ const res = await fetch(`${config.sagaApiUrl?.replace(/\/$/, "")}/board/issues`, {
1722
+ method: "POST",
1723
+ headers: await hubHeaders({ "content-type": "application/json" }),
1724
+ body: JSON.stringify(request),
1725
+ signal: AbortSignal.timeout(25e3)
1726
+ });
1727
+ const body = await res.json().catch(() => null);
1728
+ if (!res.ok || !body?.number || !body.url) throw new Error(`board issue create HTTP ${res.status}: ${body?.error ?? "unconfirmed result"}`);
1729
+ return { number: body.number, url: body.url, projectItemId: body.projectItemId, onBoard: Boolean(body.onBoard) };
1730
+ }
1718
1731
  var BOARD_ATTACH_HINTS, BoardAttachError;
1719
1732
  var init_board_write = __esm({
1720
1733
  "src/board-write.ts"() {
@@ -1723,7 +1736,7 @@ var init_board_write = __esm({
1723
1736
  BOARD_ATTACH_HINTS = {
1724
1737
  // #8965: honest, not a promise — attach did NOT run. App automation never has a Hub session by design.
1725
1738
  hub_session_missing: "No Hub session (an App installation token cannot open one). Hub adds new issues in registered repos to their board automatically when they are opened; check membership with `mmi-cli oracle board show <n> --repo <owner/repo>`. A human login or MMI_HUB_TOKEN (Actions /auth/session-ci) is needed only to attach older or transferred issues.",
1726
- forbidden: "the Hub denied this login: attach needs org-owner or repo-admin authority on <owner/repo>",
1739
+ forbidden: "the Hub denied this login: attach needs org-owner, repo-admin or verified repository write authority on <owner/repo>",
1727
1740
  hub_unavailable: "the Hub or GitHub failed transiently; the add is idempotent, so rerun `mmi-cli oracle board attach <issue>`",
1728
1741
  rate_limited: "GitHub quota is exhausted; rerun `mmi-cli oracle board attach <issue>` after the reset",
1729
1742
  refused: "the Hub refused this attach; check the board registration for <owner/repo>"
@@ -27010,6 +27023,24 @@ async function reconcileOrgNoAgentFilesRuleset(plan, client, org = ORG_LOGIN) {
27010
27023
  };
27011
27024
  }
27012
27025
 
27026
+ // src/registry-repositories.ts
27027
+ function qualifyRegistryRepo(raw) {
27028
+ return raw.includes("/") ? raw : `mutmutco/${raw}`;
27029
+ }
27030
+ function registryRepoKey(repo) {
27031
+ return repo.toLowerCase();
27032
+ }
27033
+ function repoProjectClaim(project2, repoKey) {
27034
+ if (!project2) return 0;
27035
+ const slug = typeof project2.slug === "string" ? project2.slug.toLowerCase() : void 0;
27036
+ if (slug !== void 0 && slug === repoKey.split("/")[1]) return 3;
27037
+ if (project2.projectType !== "hub-service") return 2;
27038
+ return 1;
27039
+ }
27040
+ function preferPrimaryRepoProject(current, candidate, repoKey) {
27041
+ return repoProjectClaim(candidate, repoKey) > repoProjectClaim(current, repoKey) ? candidate : current;
27042
+ }
27043
+
27013
27044
  // src/safe-json.ts
27014
27045
  function safeJson(text, fallback) {
27015
27046
  try {
@@ -27291,17 +27322,23 @@ async function auditOrgAccess(targets, deps, matrix = {}, dataAccess, sanctioned
27291
27322
  }
27292
27323
  function loadAccessTargets(projectsJson) {
27293
27324
  const projects = safeJson(projectsJson, {}).projects ?? [];
27294
- const seen = /* @__PURE__ */ new Set();
27295
- const targets = [];
27325
+ const byRepo = /* @__PURE__ */ new Map();
27296
27326
  for (const project2 of projects) {
27297
27327
  for (const repo of project2.repos ?? []) {
27298
- if (seen.has(repo)) continue;
27299
- seen.add(repo);
27300
- const cls = project2.class === "content" || project2.deployModel === "content" ? "content" : "deployable";
27301
- const releaseTrack = cls === "content" ? "trunk" : resolveReleaseTrack(project2, void 0, repo);
27302
- targets.push({ repo, class: cls, releaseTrack });
27328
+ const key = repo.toLowerCase();
27329
+ const existing = byRepo.get(key);
27330
+ byRepo.set(key, {
27331
+ repo: existing?.repo ?? repo,
27332
+ project: preferPrimaryRepoProject(existing?.project, project2, key)
27333
+ });
27303
27334
  }
27304
27335
  }
27336
+ const targets = [];
27337
+ for (const { repo, project: project2 } of byRepo.values()) {
27338
+ const cls = project2.class === "content" || project2.deployModel === "content" ? "content" : "deployable";
27339
+ const releaseTrack = cls === "content" ? "trunk" : resolveReleaseTrack(project2, void 0, repo);
27340
+ targets.push({ repo, class: cls, releaseTrack });
27341
+ }
27305
27342
  return targets;
27306
27343
  }
27307
27344
  function resolveOrgAuditTargets(registryProjects) {
@@ -27665,10 +27702,10 @@ var rollout_plan_default = {
27665
27702
  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)."
27666
27703
  },
27667
27704
  baseline: {
27668
- version: "4.6.12",
27669
- tag: "v4.6.12",
27670
- commit: "9d7baf6ad8c7",
27671
- npm: "@mutmutco/cli@4.6.12"
27705
+ version: "4.6.13",
27706
+ tag: "v4.6.13",
27707
+ commit: "70ee5f41b61b",
27708
+ npm: "@mutmutco/cli@4.6.13"
27672
27709
  },
27673
27710
  exitCriterion: "fleet-n-of-n",
27674
27711
  hubOnlyShortcut: "forbidden",
@@ -27685,14 +27722,14 @@ var rollout_plan_default = {
27685
27722
  repo: "mutmutco/mmi-hub",
27686
27723
  role: "canary",
27687
27724
  schedule: "train",
27688
- v3Target: "v4.6.12"
27725
+ v3Target: "v4.6.13"
27689
27726
  }
27690
27727
  ],
27691
27728
  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.",
27692
27729
  rollback: {
27693
27730
  independent: true,
27694
- mechanism: "npm dist-tag latest -> 4.6.12 and redeploy the Hub Lambda from tag v4.6.12 (9d7baf6ad8c7); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
27695
- v3Target: "v4.6.12 (@mutmutco/cli@4.6.12, tag commit 9d7baf6ad8c7 \u2014 last known-good release carrying the repo-index v4-only contract)"
27731
+ mechanism: "npm dist-tag latest -> 4.6.13 and redeploy the Hub Lambda from tag v4.6.13 (70ee5f41b61b); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
27732
+ v3Target: "v4.6.13 (@mutmutco/cli@4.6.13, tag commit 70ee5f41b61b \u2014 last known-good release carrying the repo-index v4-only contract)"
27696
27733
  }
27697
27734
  },
27698
27735
  {
@@ -27923,7 +27960,10 @@ function buildOrgVersionsReport(projects, probes, generatedAt) {
27923
27960
  name: String(project2.name ?? project2.slug ?? "?")
27924
27961
  });
27925
27962
  }
27926
- for (const repo of repos2) projectByRepo.set(repo.trim().toLowerCase(), projectByRepo.get(repo.trim().toLowerCase()) ?? project2);
27963
+ for (const repo of repos2) {
27964
+ const key = repo.trim().toLowerCase();
27965
+ projectByRepo.set(key, preferPrimaryRepoProject(projectByRepo.get(key), project2, key));
27966
+ }
27927
27967
  }
27928
27968
  const repos = orgVersionRepos(projects).map((repo) => {
27929
27969
  const project2 = projectByRepo.get(repo.toLowerCase());
@@ -28942,7 +28982,7 @@ function buildFleetTrackInventory(projects, releaseProbes, pluginProbes, generat
28942
28982
  }
28943
28983
  for (const repo of repos2) {
28944
28984
  const key = repo.trim().toLowerCase();
28945
- projectByRepo.set(key, projectByRepo.get(key) ?? project2);
28985
+ projectByRepo.set(key, preferPrimaryRepoProject(projectByRepo.get(key), project2, key));
28946
28986
  }
28947
28987
  }
28948
28988
  const repos = orgVersionRepos(projects).map((repo) => {
@@ -29030,15 +29070,16 @@ function defaultFleetTrackInventoryDeps(consistent = false) {
29030
29070
  async function readFleetTrackInventory(deps = defaultFleetTrackInventoryDeps()) {
29031
29071
  const projects = await deps.projects();
29032
29072
  const repos = orgVersionRepos(projects);
29033
- const branchByRepo = /* @__PURE__ */ new Map();
29073
+ const projectByRepo = /* @__PURE__ */ new Map();
29034
29074
  for (const project2 of projects) {
29035
29075
  for (const repo of (project2.repos ?? []).filter(validRepo2)) {
29036
29076
  const key = repo.trim().toLowerCase();
29037
- if (!branchByRepo.has(key)) {
29038
- branchByRepo.set(key, typeof project2.branch === "string" ? project2.branch : void 0);
29039
- }
29077
+ projectByRepo.set(key, preferPrimaryRepoProject(projectByRepo.get(key), project2, key));
29040
29078
  }
29041
29079
  }
29080
+ const branchByRepo = new Map(
29081
+ [...projectByRepo].map(([key, project2]) => [key, typeof project2.branch === "string" ? project2.branch : void 0])
29082
+ );
29042
29083
  const [releaseProbes, pluginProbes, onboardingProbes] = await Promise.all([
29043
29084
  mapBounded2(repos, 6, (repo) => readOrgVersionProbe(deps.query, repo)),
29044
29085
  mapBounded2(repos, 6, (repo) => readFleetPluginProbe(deps.query, repo, branchByRepo.get(repo.toLowerCase()))),
@@ -39110,14 +39151,6 @@ function renderPinDiffs(tick) {
39110
39151
  return tick.diffs.map((d) => [`${d.repo}:`, ...d.files.flatMap((f) => f.changes.map((c) => ` ${f.path}:${c.line} ${c.action} ${c.from === "development" ? "@development" : c.from.slice(0, 12)} -> ${c.to.slice(0, 12)}`))].join("\n")).join("\n");
39111
39152
  }
39112
39153
 
39113
- // src/registry-repositories.ts
39114
- function qualifyRegistryRepo(raw) {
39115
- return raw.includes("/") ? raw : `mutmutco/${raw}`;
39116
- }
39117
- function registryRepoKey(repo) {
39118
- return repo.toLowerCase();
39119
- }
39120
-
39121
39154
  // src/ci-audit.ts
39122
39155
  var import_yaml2 = __toESM(require_dist(), 1);
39123
39156
  var HUB_REPO5 = "mutmutco/MMI-Hub";
@@ -39426,6 +39459,11 @@ function rulesetStatusChecks(rulesets) {
39426
39459
  function sortedUnique(values) {
39427
39460
  return [...new Set(values)].sort((a, b) => a.localeCompare(b));
39428
39461
  }
39462
+ function isGitHub404(e) {
39463
+ const err = e;
39464
+ if (err?.status === 404) return true;
39465
+ return /\b404\b|\bnot found\b/i.test(String(err?.message ?? ""));
39466
+ }
39429
39467
  async function restJson2(deps, path2, fallback) {
39430
39468
  try {
39431
39469
  return await deps.client.rest("GET", path2) ?? fallback;
@@ -39795,7 +39833,23 @@ async function auditRepoCi(repo, callerDeps) {
39795
39833
  const registryRequiredChecks = meta?.requiredChecks;
39796
39834
  const explicitNoCi = registryCi === "none" || registryRequiredChecks === null || Array.isArray(registryRequiredChecks) && registryRequiredChecks.length === 0;
39797
39835
  const deployableGated = repoClass === "deployable" && !explicitNoCi;
39798
- const info = await restJson2(deps, `repos/${repo}`, {});
39836
+ let info;
39837
+ try {
39838
+ info = await deps.client.rest("GET", `repos/${repo}`) ?? {};
39839
+ } catch (e) {
39840
+ if (isGitHub404(e)) {
39841
+ return {
39842
+ repo,
39843
+ class: repoClass,
39844
+ mergePolicy: "wait-for-checks",
39845
+ ok: false,
39846
+ checks: [],
39847
+ explicitNoCi,
39848
+ excludedReason: "GitHub repository returned 404 (not found)"
39849
+ };
39850
+ }
39851
+ info = {};
39852
+ }
39799
39853
  if (info.archived === true) {
39800
39854
  return {
39801
39855
  repo,
@@ -41876,8 +41930,9 @@ async function runPrLandOnce(prNumber, options, deps) {
41876
41930
  if (!verdict.ok) {
41877
41931
  return { ...base, error: `train authority unavailable: ${verdict.error}` };
41878
41932
  }
41879
- base.train = { role: verdict.authority.role, train: verdict.authority.train };
41880
- if (!verdict.authority.train) {
41933
+ base.train = { role: verdict.authority.role, train: verdict.authority.train, ...verdict.authority.developmentMerge !== void 0 ? { developmentMerge: verdict.authority.developmentMerge } : {} };
41934
+ const canMerge = verdict.authority.train || resolved.base === "development" && verdict.authority.developmentMerge === true;
41935
+ if (!canMerge) {
41881
41936
  return {
41882
41937
  ...base,
41883
41938
  error: `@${verdict.authority.login ?? "caller"} is ${verdict.authority.role} \u2014 train not authorized on ${repo}; cannot land PR`
@@ -48676,7 +48731,11 @@ async function propagationScope(classes, what) {
48676
48731
  } catch (e) {
48677
48732
  return fail(e.message);
48678
48733
  }
48679
- const bySlugMeta = new Map(projects.flatMap((p) => (p.repos ?? []).map((r) => [(r.includes("/") ? r : `mutmutco/${r}`).toLowerCase(), p])));
48734
+ const bySlugMeta = /* @__PURE__ */ new Map();
48735
+ for (const p of projects) for (const r of p.repos ?? []) {
48736
+ const key = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
48737
+ bySlugMeta.set(key, preferPrimaryRepoProject(bySlugMeta.get(key), p, key));
48738
+ }
48680
48739
  const classOf = (repo) => bySlugMeta.get(repo.toLowerCase())?.class ?? "deployable";
48681
48740
  const seedClasses = classes ?? [...new Set(rosterRepos.map(classOf))];
48682
48741
  const inClassRepos = rosterRepos.filter((repo) => seedClasses.includes(classOf(repo)));
@@ -48889,13 +48948,12 @@ function registerBootstrapCommands(program3) {
48889
48948
  return failGraceful("bootstrap drift: the registry roster is unreadable or empty \u2014 refusing to report a fleet verdict from a scope this command could not establish (#3808)");
48890
48949
  }
48891
48950
  targets = collectRegistryRepos(projects);
48892
- const byRepo = /* @__PURE__ */ new Map();
48893
48951
  const metaByRepo = /* @__PURE__ */ new Map();
48894
48952
  for (const p of projects) for (const r of p.repos ?? []) {
48895
48953
  const key = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
48896
- byRepo.set(key, p.class ?? "deployable");
48897
- metaByRepo.set(key, p);
48954
+ metaByRepo.set(key, preferPrimaryRepoProject(metaByRepo.get(key), p, key));
48898
48955
  }
48956
+ const byRepo = new Map([...metaByRepo].map(([key, p]) => [key, p.class ?? "deployable"]));
48899
48957
  classOf = (repo) => byRepo.get(repo.toLowerCase()) ?? "deployable";
48900
48958
  metaOf = (repo) => metaByRepo.get(repo.toLowerCase()) ?? null;
48901
48959
  }
@@ -55094,6 +55152,9 @@ async function openSuitePinPr(deps, input) {
55094
55152
  }
55095
55153
  }
55096
55154
 
55155
+ // src/command-register-collaboration.ts
55156
+ init_board_write();
55157
+
55097
55158
  // src/pr-title-check.ts
55098
55159
  var CONVENTIONAL_PR_TITLE = /^(feat|fix|docs|chore|refactor|perf|test|build|ci|revert|style)(\([^)]+\))?!?: .+/;
55099
55160
  var EXEMPT_PR_TITLE = /^Merge pull request /;
@@ -59500,8 +59561,25 @@ function registerCollaborationCommands(program3) {
59500
59561
  }
59501
59562
  if (refusal && !surfaceWaived()) return fail(refusal.message, refusal.payload);
59502
59563
  }
59503
- await ensureLabelsExist(extraLabels, targetRepo2);
59504
- const created = await ghCreate(args);
59564
+ let created;
59565
+ try {
59566
+ await ensureLabelsExist(extraLabels, targetRepo2);
59567
+ created = await ghCreate(args);
59568
+ } catch (error) {
59569
+ if (o.parent !== void 0 || !/Could not resolve to a Repository|HTTP 404|Not Found/i.test(error.message)) throw error;
59570
+ const filed = await createBoardIssueViaHub({ repo: targetRepo2, title, body, type: issueType, priority, labels: extraLabels });
59571
+ const nextStep = filed.onBoard ? void 0 : `mmi-cli oracle board attach ${targetRepo2}#${filed.number}`;
59572
+ console.log(JSON.stringify({
59573
+ status: filed.onBoard ? "created" : "partial",
59574
+ ...filed,
59575
+ label: issueType,
59576
+ priority,
59577
+ credential: "app",
59578
+ ...nextStep ? { boardAttach: "failed", nextStep, nextSteps: [nextStep] } : {},
59579
+ ...surfaceInferred ? { surface_inferred: true, surface: surfaceInferred.label, surface_reason: surfaceInferred.reason } : {}
59580
+ }));
59581
+ return;
59582
+ }
59505
59583
  if (isGhCreateRateLimited(created)) {
59506
59584
  console.log(JSON.stringify(created));
59507
59585
  process.exitCode = 1;
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.6.12",
3
+ "version": "4.6.13",
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.6.12",
3
+ "version": "4.6.13",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",
@@ -4,6 +4,10 @@ GitHub repository admins hold project-admin authority on their own projects with
4
4
  registry enrollment. Registry-listed admins with repository write access remain supported.
5
5
  Use `mmi-cli oracle org access role <owner/repo> --json` to verify authority for release,
6
6
  candidate, hotfix, and runtime operations. Organization membership role alone is not this check.
7
+ Verified repository writers (GitHub write, maintain or admin) may land PRs targeting `development`
8
+ on non-Hub repositories. `/train-authority` reports this separately as `developmentMerge`;
9
+ it does not grant release, candidate, hotfix or runtime train authority. MMI-Hub development
10
+ merges remain master-only.
7
11
  The Hub distribution train remains organization-master-only. It runs in the `hub-release` workflow on
8
12
  our self-hosted runners, dispatched by an org owner, never on a workstation (#9137).
9
13
  A 503 `github-rate-limited` answer means the org App's hourly GitHub allowance is used up. It is