@mutmutco/cli 4.6.42 → 4.6.44
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/dist/main.cjs +94 -20
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -25273,6 +25273,31 @@ function missingPlaceholders(rendered) {
|
|
|
25273
25273
|
for (const m of rendered.matchAll(PLACEHOLDER_RE)) out.add(m[1]);
|
|
25274
25274
|
return [...out];
|
|
25275
25275
|
}
|
|
25276
|
+
function seedMatchesReleaseTrack(seed, releaseTrack) {
|
|
25277
|
+
if (!seed.releaseTracks?.length) return true;
|
|
25278
|
+
return releaseTrack != null && seed.releaseTracks.includes(releaseTrack);
|
|
25279
|
+
}
|
|
25280
|
+
var TRAIN_ONLY_BEGIN = "mmi-train-only:start";
|
|
25281
|
+
var TRAIN_ONLY_END = "mmi-train-only:end";
|
|
25282
|
+
function renderSelfSeedForTrack(content, releaseTrack) {
|
|
25283
|
+
if (releaseTrack !== "single") return content;
|
|
25284
|
+
const lines2 = content.split(/(?<=\n)/);
|
|
25285
|
+
const out = [];
|
|
25286
|
+
let skipping = false;
|
|
25287
|
+
for (const line of lines2) {
|
|
25288
|
+
if (!skipping && line.includes(TRAIN_ONLY_BEGIN)) {
|
|
25289
|
+
skipping = true;
|
|
25290
|
+
continue;
|
|
25291
|
+
}
|
|
25292
|
+
if (skipping) {
|
|
25293
|
+
if (line.includes(TRAIN_ONLY_END)) skipping = false;
|
|
25294
|
+
continue;
|
|
25295
|
+
}
|
|
25296
|
+
out.push(line);
|
|
25297
|
+
}
|
|
25298
|
+
if (skipping) throw new Error(`unterminated ${TRAIN_ONLY_BEGIN} region`);
|
|
25299
|
+
return out.join("");
|
|
25300
|
+
}
|
|
25276
25301
|
function isPropagatableSeed(seed) {
|
|
25277
25302
|
return seed.ownership === "org" && seed.source === "self" || seed.managedBlock != null;
|
|
25278
25303
|
}
|
|
@@ -26639,6 +26664,7 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack, requiredCheckBranc
|
|
|
26639
26664
|
out.CLASS ??= cls;
|
|
26640
26665
|
out.PROJECT_OWNER ??= parsed.owner;
|
|
26641
26666
|
const track = releaseTrack ?? resolveBootstrapReleaseTrack(cls);
|
|
26667
|
+
out.RELEASE_TRACK ??= track;
|
|
26642
26668
|
const runtime = out.GATE_RUNTIME === "python" ? "python" : "node";
|
|
26643
26669
|
for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime, requiredCheckBranches, requiredChecks))) {
|
|
26644
26670
|
out[key] ??= value;
|
|
@@ -26998,7 +27024,10 @@ function renderBootstrapAncillaryPlan(labels, environments, exceptions = []) {
|
|
|
26998
27024
|
].join("\n");
|
|
26999
27025
|
}
|
|
27000
27026
|
function resolveSeedContent(seed, vars, readFile11) {
|
|
27001
|
-
if (seed.source === "self")
|
|
27027
|
+
if (seed.source === "self") {
|
|
27028
|
+
const own = readFile11(seed.target);
|
|
27029
|
+
return own == null ? null : renderSelfSeedForTrack(own, vars.RELEASE_TRACK);
|
|
27030
|
+
}
|
|
27002
27031
|
if (seed.source.startsWith("seed:")) {
|
|
27003
27032
|
const tmpl = readFile11(`skills/bootstrap/seeds/${seed.source.slice("seed:".length)}`);
|
|
27004
27033
|
return tmpl == null ? null : renderSeed(tmpl, vars);
|
|
@@ -28527,10 +28556,10 @@ var rollout_plan_default = {
|
|
|
28527
28556
|
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)."
|
|
28528
28557
|
},
|
|
28529
28558
|
baseline: {
|
|
28530
|
-
version: "4.6.
|
|
28531
|
-
tag: "v4.6.
|
|
28532
|
-
commit: "
|
|
28533
|
-
npm: "@mutmutco/cli@4.6.
|
|
28559
|
+
version: "4.6.44",
|
|
28560
|
+
tag: "v4.6.44",
|
|
28561
|
+
commit: "2ce6b7e5afc3",
|
|
28562
|
+
npm: "@mutmutco/cli@4.6.44"
|
|
28534
28563
|
},
|
|
28535
28564
|
exitCriterion: "fleet-n-of-n",
|
|
28536
28565
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28547,14 +28576,14 @@ var rollout_plan_default = {
|
|
|
28547
28576
|
repo: "mutmutco/mmi-hub",
|
|
28548
28577
|
role: "canary",
|
|
28549
28578
|
schedule: "train",
|
|
28550
|
-
v3Target: "v4.6.
|
|
28579
|
+
v3Target: "v4.6.44"
|
|
28551
28580
|
}
|
|
28552
28581
|
],
|
|
28553
28582
|
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.",
|
|
28554
28583
|
rollback: {
|
|
28555
28584
|
independent: true,
|
|
28556
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28557
|
-
v3Target: "v4.6.
|
|
28585
|
+
mechanism: "npm dist-tag latest -> 4.6.44 and redeploy the Hub Lambda from tag v4.6.44 (2ce6b7e5afc3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28586
|
+
v3Target: "v4.6.44 (@mutmutco/cli@4.6.44, tag commit 2ce6b7e5afc3 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28558
28587
|
}
|
|
28559
28588
|
},
|
|
28560
28589
|
{
|
|
@@ -31670,17 +31699,17 @@ function isBundleOnlySuiteMember(repo, suite) {
|
|
|
31670
31699
|
if (!suite) return false;
|
|
31671
31700
|
return repo.split("/")[1]?.toLowerCase() !== suite.toLowerCase();
|
|
31672
31701
|
}
|
|
31702
|
+
var MM_GOES_LIVE = "MM-Work and its modules merge to development and it goes live; there is no release, rc or candidate stage.";
|
|
31673
31703
|
function suiteMemberRefusal(repo, meta, lane, solo = false, cut) {
|
|
31674
31704
|
const suite = meta?.suite;
|
|
31675
31705
|
if (!suite) return null;
|
|
31676
31706
|
if (!isBundleOnlySuiteMember(repo, suite)) {
|
|
31677
31707
|
if (solo || cut?.token === SUITE_LEAD_TOKEN && cut.repo.toLowerCase() === repo.toLowerCase()) return null;
|
|
31678
|
-
|
|
31679
|
-
return `${repo} is the ${suite} suite lead; a plain ${lane} would cut it without its members: ${route}. A lead-only ${lane} needs --suite-solo --owner-go.`;
|
|
31708
|
+
return `${repo} is the ${suite} suite lead; ${MM_GOES_LIVE} There is no ${lane} to run: land the change through a normal PR to development and state what landed.`;
|
|
31680
31709
|
}
|
|
31681
31710
|
if (authorisedSuiteCut(repo, meta, cut)) return null;
|
|
31682
31711
|
if (lane !== "rcand" && solo) return null;
|
|
31683
|
-
return `${repo} is part of the ${suite} suite;
|
|
31712
|
+
return `${repo} is part of the ${suite} suite; ${MM_GOES_LIVE} A suite member never asks for, proposes or prompts an rc, release or hotfix: there is none, so state what landed and stop.`;
|
|
31684
31713
|
}
|
|
31685
31714
|
|
|
31686
31715
|
// src/tenant-control-parse.ts
|
|
@@ -42162,6 +42191,31 @@ function compactRouteManifest(manifest, onlyHouse) {
|
|
|
42162
42191
|
}
|
|
42163
42192
|
};
|
|
42164
42193
|
}
|
|
42194
|
+
function filterCompactRouteManifest(output, match) {
|
|
42195
|
+
return { ...output, routes: output.routes.filter((route) => match.test(route.path)) };
|
|
42196
|
+
}
|
|
42197
|
+
function pruneManifestTree(node, match) {
|
|
42198
|
+
const subcommands = node.subcommands.map((child2) => pruneManifestTree(child2, match)).filter((child2) => child2 !== void 0);
|
|
42199
|
+
if (match.test(node.name) || match.test(node.path) || subcommands.length > 0) {
|
|
42200
|
+
return { ...node, subcommands };
|
|
42201
|
+
}
|
|
42202
|
+
return void 0;
|
|
42203
|
+
}
|
|
42204
|
+
function filterTreeAndIndex(tree, index, match) {
|
|
42205
|
+
return {
|
|
42206
|
+
tree: pruneManifestTree(tree, match) ?? { ...tree, subcommands: [] },
|
|
42207
|
+
index: index.filter((leaf) => match.test(leaf.name) || match.test(leaf.path))
|
|
42208
|
+
};
|
|
42209
|
+
}
|
|
42210
|
+
function filterCommandManifest(manifest, match) {
|
|
42211
|
+
const full = filterTreeAndIndex(manifest.tree, manifest.index, match);
|
|
42212
|
+
const primary = filterTreeAndIndex(manifest.primary_tree, manifest.primary_index, match);
|
|
42213
|
+
return { ...manifest, tree: full.tree, index: full.index, primary_tree: primary.tree, primary_index: primary.index };
|
|
42214
|
+
}
|
|
42215
|
+
function filterPrimaryCommandManifest(manifest, match) {
|
|
42216
|
+
const filtered = filterTreeAndIndex(manifest.tree, manifest.index, match);
|
|
42217
|
+
return { ...manifest, tree: filtered.tree, index: filtered.index };
|
|
42218
|
+
}
|
|
42165
42219
|
var DOOR_DECLARATIONS = [
|
|
42166
42220
|
{ door: "board", question: "What is on my board \u2014 and what is next?", path: "board read", source: "the live GitHub Project v2 board, read through the Hub board API" },
|
|
42167
42221
|
{ door: "registry", question: "What repos and projects exist, with what META?", path: "org project list", source: "the Hub org registry (DDB)" },
|
|
@@ -51008,7 +51062,7 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
51008
51062
|
detail: `expected: ${requiredProductRulesetRef} (apply as an active repo ruleset after bootstrap)`
|
|
51009
51063
|
});
|
|
51010
51064
|
}
|
|
51011
|
-
if (repoClass === "deployable") {
|
|
51065
|
+
if (repoClass === "deployable" && effectiveTrack !== "single") {
|
|
51012
51066
|
const trainScript = "scripts/next-version.mjs";
|
|
51013
51067
|
checks.push({ ok: await contentExists2(deps, repo, baseBranch, trainScript), label: `train tooling script exists: ${trainScript}` });
|
|
51014
51068
|
}
|
|
@@ -51734,7 +51788,9 @@ function registerBootstrapCommands(program3) {
|
|
|
51734
51788
|
for (const repo of targets) {
|
|
51735
51789
|
const cls = classOf(repo);
|
|
51736
51790
|
const meta = metaOf(repo);
|
|
51737
|
-
const
|
|
51791
|
+
const track = resolveReleaseTrack(metaWithDeclaredClass(meta, cls), void 0, repo);
|
|
51792
|
+
const seeds = byteComparableSeeds(manifest, cls).filter((s) => seedMatchesReleaseTrack(s, track));
|
|
51793
|
+
const trackHubContents = new Map([...hubContents].map(([t, c]) => [t, c == null ? c : renderSelfSeedForTrack(c, track)]));
|
|
51738
51794
|
seedsPerRepo = Math.max(seedsPerRepo, seeds.length);
|
|
51739
51795
|
const baseBranch = cls === "content" ? "main" : "development";
|
|
51740
51796
|
const reads = [];
|
|
@@ -51749,9 +51805,8 @@ function registerBootstrapCommands(program3) {
|
|
|
51749
51805
|
}
|
|
51750
51806
|
reads.push({ target: seed.target, content });
|
|
51751
51807
|
}
|
|
51752
|
-
findings.push(...auditRepoSeedDrift(repo, seeds,
|
|
51808
|
+
findings.push(...auditRepoSeedDrift(repo, seeds, trackHubContents, reads));
|
|
51753
51809
|
findings.push(...declaredRenderWaivers(manifest, repo));
|
|
51754
|
-
const track = resolveReleaseTrack(metaWithDeclaredClass(meta, cls), void 0, repo);
|
|
51755
51810
|
const github = await readBootstrapGitHubFacts(repo, track, controlClient);
|
|
51756
51811
|
const deployFacts = await fetchDeployFactsBySlug((repo.split("/").pop() ?? repo).toLowerCase(), registryClientDeps(await loadConfig()));
|
|
51757
51812
|
const controlRows = planBootstrapControlPlane({
|
|
@@ -52176,6 +52231,7 @@ function registerBootstrapCommands(program3) {
|
|
|
52176
52231
|
if (!seed.classes.includes(o.class)) continue;
|
|
52177
52232
|
if (!seedMatchesDeployModel(seed, applyDeployModel)) continue;
|
|
52178
52233
|
if (!seedMatchesProjectType(seed, applyProjectType)) continue;
|
|
52234
|
+
if (!seedMatchesReleaseTrack(seed, vars.RELEASE_TRACK)) continue;
|
|
52179
52235
|
const waiver = seed.waivers?.[slug];
|
|
52180
52236
|
if (waiver) {
|
|
52181
52237
|
actions.push({ action: "skip", target: seed.target, ownership: seed.ownership, reason: `waived: ${waiver}` });
|
|
@@ -52670,7 +52726,12 @@ LIVE apply to ${repo}:
|
|
|
52670
52726
|
const readSeedFile2 = (path2) => (0, import_node_fs40.existsSync)(path2) ? (0, import_node_fs40.readFileSync)(path2, "utf8") : null;
|
|
52671
52727
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
52672
52728
|
const { projects, classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(seed.classes, `target '${seed.target}'`);
|
|
52673
|
-
const
|
|
52729
|
+
const trackOf = (repo) => resolveReleaseTrack(
|
|
52730
|
+
projects.find((p) => (p.repos ?? []).some((r) => r.toLowerCase() === repo.toLowerCase())),
|
|
52731
|
+
void 0,
|
|
52732
|
+
repo
|
|
52733
|
+
);
|
|
52734
|
+
const repos = inClassRepos.filter((repo) => seedMatchesReleaseTrack(seed, trackOf(repo))).map((repo) => {
|
|
52674
52735
|
const slug = repo.split("/").pop().toLowerCase();
|
|
52675
52736
|
const waiver = seed.waivers?.[slug];
|
|
52676
52737
|
return { repo, slug, waiver };
|
|
@@ -52718,7 +52779,7 @@ LIVE apply to ${repo}:
|
|
|
52718
52779
|
}
|
|
52719
52780
|
desired = resolved.content;
|
|
52720
52781
|
} else {
|
|
52721
|
-
desired = hubContent;
|
|
52782
|
+
desired = renderSelfSeedForTrack(hubContent, trackOf(r.repo));
|
|
52722
52783
|
}
|
|
52723
52784
|
desiredByRepo.set(r.repo, desired);
|
|
52724
52785
|
const drift = compareSeedBytes(desired, content);
|
|
@@ -59669,7 +59730,7 @@ function branchRulesReadHint(path2) {
|
|
|
59669
59730
|
const match = new RegExp(`^repos/(${NAME})/(${NAME})/(?:rules/branches/[^/?#]+|branches/[^/?#]+/protection(?:/|$)|rulesets(?:/|$))`).exec(path2.split("?")[0]);
|
|
59670
59731
|
if (!match) return "";
|
|
59671
59732
|
const slug = `${match[1]}/${match[2]}`;
|
|
59672
|
-
return ` No raw rules, rulesets or branch-protection read is on the App allowlist, so retrying path or query variants cannot work \u2014 inspect a PR's required status contexts with \`MMI_ACTOR=app mmi-cli devops pr checks-required <pr> --repo ${slug} --json\`, or verify repository ruleset and branch-protection health with \`mmi-cli devops bootstrap verify --repo ${slug} --json\` and \`mmi-cli devops ci audit --repo ${slug} --json
|
|
59733
|
+
return ` No raw rules, rulesets or branch-protection read is on the App allowlist, so retrying path or query variants cannot work \u2014 inspect a PR's required status contexts with \`MMI_ACTOR=app mmi-cli devops pr checks-required <pr> --repo ${slug} --json\`, or verify repository ruleset and branch-protection health with \`mmi-cli devops bootstrap verify --repo ${slug} --json\` and \`mmi-cli devops ci audit --repo ${slug} --json\`. Stepping around it with a raw \`gh api repos/${slug}/rulesets/<id> > file\` is not a substitute \u2014 when that read fails it leaves an EMPTY file, and a later require/JSON.parse throws 'Unexpected end of JSON input' that looks like corrupt data while the real refusal is only on stderr; check the exit code and file size, or ask a human token holder for the raw ruleset body.`;
|
|
59673
59734
|
}
|
|
59674
59735
|
function commitsReadHint(path2) {
|
|
59675
59736
|
const clean4 = path2.split("?")[0];
|
|
@@ -73693,11 +73754,24 @@ function registerDeveloperCommands(program3) {
|
|
|
73693
73754
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
73694
73755
|
}
|
|
73695
73756
|
});
|
|
73696
|
-
program3.command("commands").description("print the grouped agentic-coding map; JSON defaults to bounded routes").option("--all", "human: include operational depth; JSON: emit the exhaustive schema-2 manifest").option("--json", "machine-readable progressive discovery").option("--primary", "with --json, emit the compatibility schema-2 primary tree and index").addOption(new Option("--house <house>", "with --json, limit routes to one house").choices([...HOUSE_ROOTS, "core"])).action((o) => {
|
|
73757
|
+
program3.command("commands").description("print the grouped agentic-coding map; JSON defaults to bounded routes").option("--all", "human: include operational depth; JSON: emit the exhaustive schema-2 manifest").option("--json", "machine-readable progressive discovery; the compact default emits an OBJECT {schema_version, scope:'routes', routes[{path,house,summary,detail}], houses, doors, next{full:'mmi-cli commands --all --json'}} \u2014 never a top-level `commands` array; --all --json emits the exhaustive schema-2 manifest {scope:'all', tree, index, houses, doors, flat_alias_cut, primary_tree, primary_index, error_codes}").option("--primary", "with --json, emit the compatibility schema-2 primary tree and index").addOption(new Option("--house <house>", "with --json, limit routes to one house").choices([...HOUSE_ROOTS, "core"])).addOption(new Option("--match <regex>", "with --json, keep only routes/commands whose path or name matches this regular expression (case-insensitive)")).action((o) => {
|
|
73697
73758
|
if (o.house && !o.json) {
|
|
73698
73759
|
fail("commands: --house requires --json", { code: ERROR_CODES.ERR_CONFLICTING_FLAGS, offending_flag: "--house" });
|
|
73699
73760
|
return;
|
|
73700
73761
|
}
|
|
73762
|
+
if (o.match && !o.json) {
|
|
73763
|
+
fail("commands: --match requires --json", { code: ERROR_CODES.ERR_CONFLICTING_FLAGS, offending_flag: "--match" });
|
|
73764
|
+
return;
|
|
73765
|
+
}
|
|
73766
|
+
let match;
|
|
73767
|
+
if (o.match) {
|
|
73768
|
+
try {
|
|
73769
|
+
match = new RegExp(o.match, "i");
|
|
73770
|
+
} catch {
|
|
73771
|
+
fail(`commands: --match '${o.match}' is not a valid regular expression`, { code: ERROR_CODES.ERR_INVALID_INPUT, offending_flag: "--match" });
|
|
73772
|
+
return;
|
|
73773
|
+
}
|
|
73774
|
+
}
|
|
73701
73775
|
const conflicting = [o.all ? "--all" : "", o.primary ? "--primary" : "", o.house ? "--house" : ""].filter(Boolean);
|
|
73702
73776
|
if (o.json && conflicting.length > 1) {
|
|
73703
73777
|
fail(`commands: ${conflicting.join(" and ")} are mutually exclusive`, { code: ERROR_CODES.ERR_CONFLICTING_FLAGS, offending_flag: conflicting.join(",") });
|
|
@@ -73705,7 +73779,7 @@ function registerDeveloperCommands(program3) {
|
|
|
73705
73779
|
}
|
|
73706
73780
|
const manifest = buildCommandManifest(program3);
|
|
73707
73781
|
if (o.json) {
|
|
73708
|
-
const output = o.all ? manifest : o.primary ? primaryCommandManifest(manifest) : compactRouteManifest(manifest, o.house);
|
|
73782
|
+
const output = o.all ? match ? filterCommandManifest(manifest, match) : manifest : o.primary ? match ? filterPrimaryCommandManifest(primaryCommandManifest(manifest), match) : primaryCommandManifest(manifest) : match ? filterCompactRouteManifest(compactRouteManifest(manifest, o.house), match) : compactRouteManifest(manifest, o.house);
|
|
73709
73783
|
consoleIo.log(JSON.stringify(output, null, 2));
|
|
73710
73784
|
return;
|
|
73711
73785
|
}
|
package/package.json
CHANGED