@mutmutco/cli 4.2.1 → 4.2.3
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 +181 -55
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3416,7 +3416,7 @@ var program = new Command();
|
|
|
3416
3416
|
// src/index.ts
|
|
3417
3417
|
var import_promises8 = require("node:fs/promises");
|
|
3418
3418
|
var import_node_fs43 = require("node:fs");
|
|
3419
|
-
var
|
|
3419
|
+
var import_node_child_process20 = require("node:child_process");
|
|
3420
3420
|
|
|
3421
3421
|
// src/cli-shared.ts
|
|
3422
3422
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -4522,10 +4522,29 @@ function targetFlagValueFromArgv(flag, argv) {
|
|
|
4522
4522
|
if (!next || next.startsWith("-")) return void 0;
|
|
4523
4523
|
return next;
|
|
4524
4524
|
}
|
|
4525
|
+
function survivingFlagArgs(argv, rejectedFlag, ownFlags) {
|
|
4526
|
+
const out = [];
|
|
4527
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
4528
|
+
const token = argv[i];
|
|
4529
|
+
if (!token.startsWith("--")) continue;
|
|
4530
|
+
const [name, inlineValue] = token.includes("=") ? [token.slice(0, token.indexOf("=")), token.slice(token.indexOf("=") + 1)] : [token, void 0];
|
|
4531
|
+
const values = [];
|
|
4532
|
+
if (inlineValue === void 0) {
|
|
4533
|
+
while (argv[i + 1] !== void 0 && !argv[i + 1].startsWith("-")) {
|
|
4534
|
+
values.push(argv[i + 1]);
|
|
4535
|
+
i += 1;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
if (name === rejectedFlag || !ownFlags.includes(name)) continue;
|
|
4539
|
+
out.push(inlineValue !== void 0 ? token : name, ...values);
|
|
4540
|
+
}
|
|
4541
|
+
return out;
|
|
4542
|
+
}
|
|
4525
4543
|
function formatPositionalTarget(commandPath3, argName, opts = {}) {
|
|
4526
4544
|
const concrete = opts.flag && opts.argv ? targetFlagValueFromArgv(opts.flag, opts.argv) : void 0;
|
|
4527
|
-
|
|
4528
|
-
|
|
4545
|
+
const target = concrete ?? `<${argName}>`;
|
|
4546
|
+
const kept = opts.flag && opts.argv && opts.ownFlags ? survivingFlagArgs(opts.argv, opts.flag, opts.ownFlags) : [];
|
|
4547
|
+
return [`mmi-cli ${commandPath3}`, target, ...kept].join(" ");
|
|
4529
4548
|
}
|
|
4530
4549
|
var FLAG_SYNONYMS = /* @__PURE__ */ new Map([["--outcome", "--reason"]]);
|
|
4531
4550
|
function synonymFlagFor(flag, ownFlags) {
|
|
@@ -4545,6 +4564,27 @@ var GUIDES = /* @__PURE__ */ new Map([
|
|
|
4545
4564
|
expected: ["learning report", "oracle issue create", "oracle issue list"],
|
|
4546
4565
|
did_you_mean: "learning report"
|
|
4547
4566
|
}
|
|
4567
|
+
],
|
|
4568
|
+
// #5821: `list` is the verb `oracle issue list` and `devops pr list` teach, so callers reach for
|
|
4569
|
+
// `oracle board list` next. Distance ranks the OTHER houses' `list` leaves above the board's own
|
|
4570
|
+
// reader, so the receipt pointed at `devops pr list` — a different house, for a board query.
|
|
4571
|
+
[
|
|
4572
|
+
"oracle board:list",
|
|
4573
|
+
{
|
|
4574
|
+
hint: "no `oracle board list` \u2014 the bounded board query is `oracle board read` (add `--json` for the machine-readable partition)",
|
|
4575
|
+
expected: ["oracle board read"],
|
|
4576
|
+
did_you_mean: "oracle board read"
|
|
4577
|
+
}
|
|
4578
|
+
],
|
|
4579
|
+
// #5823: `label` names a capability, not a command. The mutation door is `oracle issue edit`
|
|
4580
|
+
// (`--add-label` / `--remove-label`); distance cannot bridge noun→verb, so name it here.
|
|
4581
|
+
[
|
|
4582
|
+
"oracle issue:label",
|
|
4583
|
+
{
|
|
4584
|
+
hint: "no `oracle issue label` \u2014 labels are edited through `oracle issue edit --add-label <name>` / `--remove-label <name>`",
|
|
4585
|
+
expected: ["oracle issue edit"],
|
|
4586
|
+
did_you_mean: "oracle issue edit"
|
|
4587
|
+
}
|
|
4548
4588
|
]
|
|
4549
4589
|
]);
|
|
4550
4590
|
function unknownCommandDomainGuide(parentPath, token) {
|
|
@@ -11762,10 +11802,10 @@ var rollout_plan_default = {
|
|
|
11762
11802
|
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)."
|
|
11763
11803
|
},
|
|
11764
11804
|
baseline: {
|
|
11765
|
-
version: "4.2.
|
|
11766
|
-
tag: "v4.2.
|
|
11767
|
-
commit: "
|
|
11768
|
-
npm: "@mutmutco/cli@4.2.
|
|
11805
|
+
version: "4.2.3",
|
|
11806
|
+
tag: "v4.2.3",
|
|
11807
|
+
commit: "a54078f2ac92",
|
|
11808
|
+
npm: "@mutmutco/cli@4.2.3"
|
|
11769
11809
|
},
|
|
11770
11810
|
exitCriterion: "fleet-n-of-n",
|
|
11771
11811
|
hubOnlyShortcut: "forbidden",
|
|
@@ -11782,14 +11822,14 @@ var rollout_plan_default = {
|
|
|
11782
11822
|
repo: "mutmutco/mmi-hub",
|
|
11783
11823
|
role: "canary",
|
|
11784
11824
|
schedule: "train",
|
|
11785
|
-
v3Target: "v4.2.
|
|
11825
|
+
v3Target: "v4.2.3"
|
|
11786
11826
|
}
|
|
11787
11827
|
],
|
|
11788
11828
|
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.",
|
|
11789
11829
|
rollback: {
|
|
11790
11830
|
independent: true,
|
|
11791
|
-
mechanism: "npm dist-tag latest -> 4.2.
|
|
11792
|
-
v3Target: "v4.2.
|
|
11831
|
+
mechanism: "npm dist-tag latest -> 4.2.3 and redeploy the Hub Lambda from tag v4.2.3 (a54078f2ac92); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
11832
|
+
v3Target: "v4.2.3 (@mutmutco/cli@4.2.3, tag commit a54078f2ac92 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
11793
11833
|
}
|
|
11794
11834
|
},
|
|
11795
11835
|
{
|
|
@@ -23508,8 +23548,11 @@ var defaultDeps = {
|
|
|
23508
23548
|
},
|
|
23509
23549
|
// Re-exec the SAME command line in the fresh CLI with inherited stdio (spawn, not execFile, so
|
|
23510
23550
|
// output streams live) and hand its exit code back — the stale parent exits with it (#5762).
|
|
23511
|
-
|
|
23512
|
-
|
|
23551
|
+
// #5813: the caller supplies that command line. `process.argv` is NOT it — the house shim splices
|
|
23552
|
+
// the house root out in place, so re-reading it here re-ran the removed flat alias (`release …`
|
|
23553
|
+
// instead of `devops release …`) and the converged child refused with exit 2.
|
|
23554
|
+
reexec: (argv) => new Promise((resolve6) => {
|
|
23555
|
+
const args = [...argv];
|
|
23513
23556
|
const child2 = isWin2 ? (0, import_node_child_process10.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", windowsHide: true }) : (0, import_node_child_process10.spawn)("mmi-cli", args, { stdio: "inherit", windowsHide: true });
|
|
23514
23557
|
child2.on("exit", (code) => resolve6(code ?? 1));
|
|
23515
23558
|
child2.on("error", () => resolve6(1));
|
|
@@ -23531,7 +23574,7 @@ async function selfConvergeTrainCli(input) {
|
|
|
23531
23574
|
error: `self-converge did not take effect: mmi-cli on PATH still resolves to ${installed ?? "unknown"}, expected >= ${input.releasedVersion}`
|
|
23532
23575
|
};
|
|
23533
23576
|
}
|
|
23534
|
-
const reexecCode = await deps.reexec();
|
|
23577
|
+
const reexecCode = await deps.reexec(input.argv);
|
|
23535
23578
|
return { kind: "converged", reexecCode };
|
|
23536
23579
|
} catch (e) {
|
|
23537
23580
|
return { kind: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
@@ -23988,6 +24031,11 @@ function renderSweep(r) {
|
|
|
23988
24031
|
|
|
23989
24032
|
// src/hotfix-apply.ts
|
|
23990
24033
|
var HOTFIX_RELEASE_WORKFLOWS = ["deploy.yml", "publish.yml"];
|
|
24034
|
+
function hotfixStatusWorkflows(deployModel) {
|
|
24035
|
+
if (deployModel === "registry-publish") return ["publish.yml"];
|
|
24036
|
+
if (deployModel === void 0 || deployModel === "hub-serverless" || deployModel === "tenant-container" || deployModel === "solo-container") return HOTFIX_RELEASE_WORKFLOWS;
|
|
24037
|
+
return [];
|
|
24038
|
+
}
|
|
23991
24039
|
var HOTFIX_RUN_FIND_ATTEMPTS = 10;
|
|
23992
24040
|
var HOTFIX_RUN_FIND_DELAY_MS = 15e3;
|
|
23993
24041
|
var HOTFIX_VERIFY_ATTEMPTS = 5;
|
|
@@ -24623,29 +24671,35 @@ async function runHotfixStatus(deps, versionInput) {
|
|
|
24623
24671
|
const work = (async () => {
|
|
24624
24672
|
const ctx = await buildTrainApplyContext(deps);
|
|
24625
24673
|
await deps.run("git", ["fetch", "origin", "--tags"]);
|
|
24674
|
+
let workflows = HOTFIX_RELEASE_WORKFLOWS;
|
|
24675
|
+
let warnings = [];
|
|
24676
|
+
try {
|
|
24677
|
+
workflows = hotfixStatusWorkflows(await resolveHotfixDeployModel(deps, ctx));
|
|
24678
|
+
} catch (e) {
|
|
24679
|
+
warnings = [`deploy model unverified (${e.message ?? e}) \u2014 probed the default deploy.yml+publish.yml run list, so an unproven run may hold the verdict at unverified`];
|
|
24680
|
+
}
|
|
24626
24681
|
let tag;
|
|
24627
24682
|
let version;
|
|
24628
|
-
let warnings = [];
|
|
24629
24683
|
if (versionInput) {
|
|
24630
24684
|
({ tag, version } = normalizeHotfixVersion(versionInput));
|
|
24631
24685
|
} else {
|
|
24632
24686
|
const latest = await deps.run("git", ["tag", "--list", "v[0-9]*.[0-9]*.[0-9]*", "--merged", "origin/main", "--sort=-v:refname"]).then((out) => out.split("\n").map((s) => s.trim()).find((t) => /^v\d+\.\d+\.\d+$/.test(t)));
|
|
24633
24687
|
if (latest) {
|
|
24634
|
-
const latestFacts = await gatherHotfixFacts(deps, ctx, latest, latest.slice(1));
|
|
24688
|
+
const latestFacts = await gatherHotfixFacts(deps, ctx, latest, latest.slice(1), workflows);
|
|
24635
24689
|
const latestDerived = deriveHotfixState(latestFacts);
|
|
24636
24690
|
if (latestDerived.state !== "complete") {
|
|
24637
24691
|
return { ...ctx, command: "hotfix-status", ...latestFacts, ...latestDerived, warnings };
|
|
24638
24692
|
}
|
|
24639
24693
|
}
|
|
24640
|
-
const found = await findInFlightHotfixVersion(deps, ctx, latest);
|
|
24641
|
-
warnings = supersededHotfixWarnings(found.superseded, latest);
|
|
24694
|
+
const found = await findInFlightHotfixVersion(deps, ctx, latest, workflows);
|
|
24695
|
+
warnings = [...warnings, ...supersededHotfixWarnings(found.superseded, latest)];
|
|
24642
24696
|
if (found.inFlight) {
|
|
24643
24697
|
({ tag, version } = found.inFlight);
|
|
24644
24698
|
} else {
|
|
24645
24699
|
({ tag, version } = await deriveHotfixVersion(deps));
|
|
24646
24700
|
}
|
|
24647
24701
|
}
|
|
24648
|
-
const facts = await gatherHotfixFacts(deps, ctx, tag, version);
|
|
24702
|
+
const facts = await gatherHotfixFacts(deps, ctx, tag, version, workflows);
|
|
24649
24703
|
return { ...ctx, command: "hotfix-status", ...facts, ...deriveHotfixState(facts), warnings };
|
|
24650
24704
|
})();
|
|
24651
24705
|
let timer;
|
|
@@ -24664,7 +24718,7 @@ async function runHotfixStatus(deps, versionInput) {
|
|
|
24664
24718
|
if (timer) clearTimeout(timer);
|
|
24665
24719
|
}
|
|
24666
24720
|
}
|
|
24667
|
-
async function gatherHotfixFacts(deps, ctx, tag, version) {
|
|
24721
|
+
async function gatherHotfixFacts(deps, ctx, tag, version, workflows = HOTFIX_RELEASE_WORKFLOWS) {
|
|
24668
24722
|
const branchExists = Boolean(clean3(await deps.run("git", ["ls-remote", "origin", `refs/heads/${hotfixBranch(tag)}`])));
|
|
24669
24723
|
const pr2 = await findHotfixPr(deps, ctx, tag);
|
|
24670
24724
|
const remoteTag = clean3(await deps.run("git", ["ls-remote", "origin", `refs/tags/${tag}`]));
|
|
@@ -24673,7 +24727,7 @@ async function gatherHotfixFacts(deps, ctx, tag, version) {
|
|
|
24673
24727
|
const releaseExists = await hotfixReleaseExists(deps, ctx, tag);
|
|
24674
24728
|
const runs = [];
|
|
24675
24729
|
if (releaseExists && tagSha) {
|
|
24676
|
-
for (const workflow of
|
|
24730
|
+
for (const workflow of workflows) {
|
|
24677
24731
|
try {
|
|
24678
24732
|
const out = await deps.run("gh", [
|
|
24679
24733
|
"run",
|
|
@@ -24729,7 +24783,7 @@ function supersededHotfixWarnings(superseded, latestMainTag) {
|
|
|
24729
24783
|
if (superseded.length === 0) return [];
|
|
24730
24784
|
return [`skipped superseded hotfix marker(s) ${superseded.join(", ")} \u2014 merged to main but never released and at/below the latest released tag ${latestMainTag ?? "(none)"}; later releases already absorbed them (#976). Not actionable \u2014 run mmi-cli devops hotfix start for a new fix.`];
|
|
24731
24785
|
}
|
|
24732
|
-
async function findInFlightHotfixVersion(deps, ctx, latestMainTag) {
|
|
24786
|
+
async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = HOTFIX_RELEASE_WORKFLOWS) {
|
|
24733
24787
|
const tags = /* @__PURE__ */ new Set();
|
|
24734
24788
|
const out = await deps.run("gh", [
|
|
24735
24789
|
"pr",
|
|
@@ -24759,13 +24813,13 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag) {
|
|
|
24759
24813
|
const fresh = latestMainTag ? sorted.filter((t) => compareHotfixVersions(t, latestMainTag) > 0) : sorted;
|
|
24760
24814
|
for (const tag of fresh) {
|
|
24761
24815
|
const version = tag.slice(1);
|
|
24762
|
-
const facts = await gatherHotfixFacts(deps, ctx, tag, version);
|
|
24816
|
+
const facts = await gatherHotfixFacts(deps, ctx, tag, version, workflows);
|
|
24763
24817
|
if (deriveHotfixState(facts).state !== "complete") return { inFlight: { tag, version }, superseded: [] };
|
|
24764
24818
|
}
|
|
24765
24819
|
const stale = latestMainTag ? sorted.filter((t) => compareHotfixVersions(t, latestMainTag) <= 0) : [];
|
|
24766
24820
|
const superseded = [];
|
|
24767
24821
|
for (const tag of stale) {
|
|
24768
|
-
const facts = await gatherHotfixFacts(deps, ctx, tag, tag.slice(1));
|
|
24822
|
+
const facts = await gatherHotfixFacts(deps, ctx, tag, tag.slice(1), workflows);
|
|
24769
24823
|
if (deriveHotfixState(facts).state !== "complete") superseded.push(tag);
|
|
24770
24824
|
}
|
|
24771
24825
|
return { inFlight: null, superseded };
|
|
@@ -32376,7 +32430,53 @@ async function fetchRestCorePool(gh = defaultGhApi) {
|
|
|
32376
32430
|
}
|
|
32377
32431
|
|
|
32378
32432
|
// src/pr-create-docs-check.ts
|
|
32433
|
+
var import_node_child_process18 = require("node:child_process");
|
|
32379
32434
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
32435
|
+
function catFileBatch(root, ref, paths) {
|
|
32436
|
+
if (paths.length === 0) return Promise.resolve([]);
|
|
32437
|
+
return new Promise((resolve6) => {
|
|
32438
|
+
const chunks = [];
|
|
32439
|
+
let settled = false;
|
|
32440
|
+
const child2 = (0, import_node_child_process18.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
32441
|
+
const finish = () => {
|
|
32442
|
+
if (settled) return;
|
|
32443
|
+
settled = true;
|
|
32444
|
+
clearTimeout(timer);
|
|
32445
|
+
resolve6(parseCatFileBatch(Buffer.concat(chunks), paths.length));
|
|
32446
|
+
};
|
|
32447
|
+
const timer = setTimeout(() => {
|
|
32448
|
+
child2.kill("SIGKILL");
|
|
32449
|
+
finish();
|
|
32450
|
+
}, GIT_TIMEOUT_MS2);
|
|
32451
|
+
timer.unref?.();
|
|
32452
|
+
child2.stdout.on("data", (c) => chunks.push(c));
|
|
32453
|
+
child2.on("error", finish);
|
|
32454
|
+
child2.stdin.on("error", () => {
|
|
32455
|
+
});
|
|
32456
|
+
child2.on("close", finish);
|
|
32457
|
+
child2.stdin.end(paths.map((path2) => `${ref}:${path2}
|
|
32458
|
+
`).join(""));
|
|
32459
|
+
});
|
|
32460
|
+
}
|
|
32461
|
+
function parseCatFileBatch(out, expected) {
|
|
32462
|
+
const results = [];
|
|
32463
|
+
let at = 0;
|
|
32464
|
+
while (results.length < expected) {
|
|
32465
|
+
const eol = out.indexOf(10, at);
|
|
32466
|
+
if (eol === -1) break;
|
|
32467
|
+
const header = out.toString("utf8", at, eol);
|
|
32468
|
+
at = eol + 1;
|
|
32469
|
+
if (header.endsWith(" missing")) {
|
|
32470
|
+
results.push(void 0);
|
|
32471
|
+
continue;
|
|
32472
|
+
}
|
|
32473
|
+
const size = Number(header.slice(header.lastIndexOf(" ") + 1));
|
|
32474
|
+
if (!Number.isInteger(size) || size < 0 || at + size > out.length) break;
|
|
32475
|
+
results.push(out.toString("utf8", at, at + size));
|
|
32476
|
+
at = at + size + 1;
|
|
32477
|
+
}
|
|
32478
|
+
return results;
|
|
32479
|
+
}
|
|
32380
32480
|
function createPrCreateDocsIndexDeps() {
|
|
32381
32481
|
return {
|
|
32382
32482
|
worktreeRoot: async () => {
|
|
@@ -32405,20 +32505,13 @@ function createPrCreateDocsIndexDeps() {
|
|
|
32405
32505
|
},
|
|
32406
32506
|
listDocsAtRef: async (root, ref) => {
|
|
32407
32507
|
try {
|
|
32408
|
-
const { stdout } = await execFileP2("git", ["-C", root, "ls-tree", "-r", "--name-only", ref, "--", "docs"], { timeout: GIT_TIMEOUT_MS2 });
|
|
32409
|
-
return stdout.split(
|
|
32508
|
+
const { stdout } = await execFileP2("git", ["-C", root, "ls-tree", "-r", "-z", "--name-only", ref, "--", "docs"], { timeout: GIT_TIMEOUT_MS2 });
|
|
32509
|
+
return stdout.split("\0").filter(Boolean);
|
|
32410
32510
|
} catch {
|
|
32411
32511
|
return [];
|
|
32412
32512
|
}
|
|
32413
32513
|
},
|
|
32414
|
-
|
|
32415
|
-
try {
|
|
32416
|
-
const { stdout } = await execFileP2("git", ["-C", root, "show", `${ref}:${path2}`], { timeout: GIT_TIMEOUT_MS2 });
|
|
32417
|
-
return stdout;
|
|
32418
|
-
} catch {
|
|
32419
|
-
return void 0;
|
|
32420
|
-
}
|
|
32421
|
-
}
|
|
32514
|
+
readManyAtRef: (root, ref, paths) => catFileBatch(root, ref, paths)
|
|
32422
32515
|
};
|
|
32423
32516
|
}
|
|
32424
32517
|
async function checkDocsIndexAtHead(opts, deps) {
|
|
@@ -32433,10 +32526,10 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
32433
32526
|
const paths = await deps.listDocsAtRef(root, ref);
|
|
32434
32527
|
if (!paths.includes(DOCS_INDEX_PATH)) return void 0;
|
|
32435
32528
|
const relDocs = paths.filter((p) => p.startsWith("docs/") && p.endsWith(".md")).map((p) => p.slice("docs/".length)).filter(isRoutableDocsPath);
|
|
32436
|
-
const
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
|
|
32529
|
+
const contents = await deps.readManyAtRef(root, ref, [DOCS_INDEX_PATH, ...relDocs.map((rel) => `docs/${rel}`)]);
|
|
32530
|
+
const [indexContent, ...docContents] = contents;
|
|
32531
|
+
if (contents.length !== relDocs.length + 1) return void 0;
|
|
32532
|
+
if (indexContent === void 0 || docContents.some((c) => c === void 0)) return void 0;
|
|
32440
32533
|
const contentByPath = new Map(relDocs.map((rel, i) => [rel, docContents[i] ?? ""]));
|
|
32441
32534
|
const atRefDeps = {
|
|
32442
32535
|
listDocs: () => relDocs,
|
|
@@ -32452,7 +32545,7 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
32452
32545
|
}
|
|
32453
32546
|
return {
|
|
32454
32547
|
ok: false,
|
|
32455
|
-
detail: `${DOCS_INDEX_PATH} is stale at the commit this PR would open from \u2014 it no longer matches the docs/ tree there`,
|
|
32548
|
+
detail: `${DOCS_INDEX_PATH} is stale at the commit this PR would open from \u2014 it no longer matches the docs/ tree there (checked ${ref} over ${relDocs.length} indexed doc${relDocs.length === 1 ? "" : "s"})`,
|
|
32456
32549
|
fix: "run `mmi-cli oracle docs index --write`, commit docs/index.md, and push again before retrying `pr create`"
|
|
32457
32550
|
};
|
|
32458
32551
|
}
|
|
@@ -33907,12 +34000,18 @@ function catalogCommandPaths(manifest) {
|
|
|
33907
34000
|
function nearestExplainTargets(query, paths, limit = 5) {
|
|
33908
34001
|
const parentPath = query.includes(" ") ? query.slice(0, query.lastIndexOf(" ")) : void 0;
|
|
33909
34002
|
const leaf = query.includes(" ") ? query.slice(query.lastIndexOf(" ") + 1) : query;
|
|
33910
|
-
const guide = unknownCommandDomainGuide(parentPath, leaf);
|
|
33911
|
-
if (guide) return guide.expected.slice(0, limit);
|
|
33912
34003
|
const all = [...paths];
|
|
33913
34004
|
const leafOf = (p) => p.slice(p.lastIndexOf(" ") + 1);
|
|
34005
|
+
const siblings = parentPath ? all.filter((p) => p !== query && p.startsWith(`${parentPath} `) && !p.slice(parentPath.length + 1).includes(" ")).sort() : [];
|
|
34006
|
+
const withSiblings = (primary) => {
|
|
34007
|
+
const out2 = [...primary];
|
|
34008
|
+
for (const sibling of siblings) if (!out2.includes(sibling)) out2.push(sibling);
|
|
34009
|
+
return out2.slice(0, limit);
|
|
34010
|
+
};
|
|
34011
|
+
const guide = unknownCommandDomainGuide(parentPath, leaf);
|
|
34012
|
+
if (guide) return withSiblings(guide.expected);
|
|
33914
34013
|
const exactLeaf = all.filter((p) => p !== query && leafOf(p) === leaf).sort();
|
|
33915
|
-
if (exactLeaf.length) return exactLeaf
|
|
34014
|
+
if (exactLeaf.length) return withSiblings(exactLeaf);
|
|
33916
34015
|
const out = [];
|
|
33917
34016
|
const nearPath = didYouMean(query, all);
|
|
33918
34017
|
if (nearPath) out.push(nearPath);
|
|
@@ -33923,7 +34022,7 @@ function nearestExplainTargets(query, paths, limit = 5) {
|
|
|
33923
34022
|
if (out.length >= limit) break;
|
|
33924
34023
|
}
|
|
33925
34024
|
}
|
|
33926
|
-
return out
|
|
34025
|
+
return withSiblings(out);
|
|
33927
34026
|
}
|
|
33928
34027
|
function explainCommandManifest(manifest, command, opts = {}) {
|
|
33929
34028
|
const base = {
|
|
@@ -36588,6 +36687,9 @@ function diagnoseSurface(evidence) {
|
|
|
36588
36687
|
if (evidence.manifest === "invalid") return { ...base, state: "corrupt" };
|
|
36589
36688
|
if (!evidence.releasedVersion) return { ...base, state: "freshness-unknown" };
|
|
36590
36689
|
if (evidence.installedVersion && evidence.releasedVersion && compareVersions(evidence.installedVersion, evidence.releasedVersion) < 0) {
|
|
36690
|
+
if (evidence.stagedVersion && evidence.stagedVersion === evidence.releasedVersion) {
|
|
36691
|
+
return { ...base, state: "staged" };
|
|
36692
|
+
}
|
|
36591
36693
|
return { ...base, state: "stale" };
|
|
36592
36694
|
}
|
|
36593
36695
|
return { ...base, state: "clean" };
|
|
@@ -36598,14 +36700,16 @@ function reloadInstruction(descriptor) {
|
|
|
36598
36700
|
return `${verb} ${descriptor.displayName}`;
|
|
36599
36701
|
}
|
|
36600
36702
|
function planSurfaceRepair(diagnosis) {
|
|
36601
|
-
if (diagnosis.state === "clean" || diagnosis.state === "pending-reload" || diagnosis.state === "skipped" || diagnosis.state === "freshness-unknown") return null;
|
|
36703
|
+
if (diagnosis.state === "clean" || diagnosis.state === "pending-reload" || diagnosis.state === "staged" || diagnosis.state === "skipped" || diagnosis.state === "freshness-unknown") return null;
|
|
36602
36704
|
const descriptor = diagnosis.descriptor;
|
|
36603
36705
|
if (descriptor.repairOwner === "mmi-hub") {
|
|
36604
36706
|
return {
|
|
36605
36707
|
supported: false,
|
|
36606
36708
|
owner: "mmi-hub",
|
|
36607
36709
|
changes: [],
|
|
36608
|
-
|
|
36710
|
+
// #5833: name the SURFACE this row is about — the literal "Hermes plugin" sent every other
|
|
36711
|
+
// mmi-hub-owned surface (Claude Code included) chasing a different host that was already current.
|
|
36712
|
+
instruction: `run \`mmi-hub update\` to converge the MMI-managed ${descriptor.displayName} plugin, then ${reloadInstruction(descriptor)}`
|
|
36609
36713
|
};
|
|
36610
36714
|
}
|
|
36611
36715
|
if (descriptor.repairOwner !== "mmi-cli") {
|
|
@@ -36632,6 +36736,8 @@ function buildSurfaceDoctorCheck(diagnosis) {
|
|
|
36632
36736
|
skipped: "skipped \u2014 host not active",
|
|
36633
36737
|
clean: diagnosis.repairDetail ? `clean \u2014 repaired and verified (${diagnosis.repairDetail})` : `clean${versions ? ` \u2014 ${versions}` : ""}`,
|
|
36634
36738
|
"pending-reload": `pending ${descriptor.token === "hermes" ? "fresh process" : descriptor.reload === "workspace" ? "reload" : "restart"} \u2014 repaired to ${diagnosis.releasedVersion}, but ${diagnosis.installedVersion} is still the version this host has loaded${diagnosis.repairDetail ? ` (${diagnosis.repairDetail})` : ""}`,
|
|
36739
|
+
// #5833: the update already staged the released tree; the wording mirrors `mmi-hub status`.
|
|
36740
|
+
staged: `loaded ${diagnosis.installedVersion}; staged ${diagnosis.releasedVersion} for next launch \u2014 ${descriptor.token === "hermes" ? "start a fresh process" : descriptor.reload === "workspace" ? "reload" : "restart"} ${descriptor.displayName} to converge`,
|
|
36635
36741
|
"freshness-unknown": `${diagnosis.installedVersion ?? "installed"} \u2014 freshness UNKNOWN, the published version could not be read`,
|
|
36636
36742
|
stale: `stale${versions ? ` \u2014 ${versions}` : ""}`,
|
|
36637
36743
|
missing: "missing \u2014 no install record",
|
|
@@ -36643,14 +36749,14 @@ function buildSurfaceDoctorCheck(diagnosis) {
|
|
|
36643
36749
|
id: `${descriptor.token}-plugin`,
|
|
36644
36750
|
surface: descriptor.token,
|
|
36645
36751
|
state,
|
|
36646
|
-
ok: state === "clean" || state === "skipped" || state === "pending-reload",
|
|
36752
|
+
ok: state === "clean" || state === "skipped" || state === "pending-reload" || state === "staged",
|
|
36647
36753
|
...state === "freshness-unknown" ? { reportOnly: true } : {},
|
|
36648
|
-
// Nothing is broken, so this is not a ✗ — but
|
|
36754
|
+
// Nothing is broken, so this is not a ✗ — but these are the OK rows an operator must act on, and the
|
|
36649
36755
|
// short default lane and the SessionStart banner both filter to failures unless a row says otherwise.
|
|
36650
|
-
...state === "pending-reload" ? { warn: true } : {},
|
|
36756
|
+
...state === "pending-reload" || state === "staged" ? { warn: true } : {},
|
|
36651
36757
|
label: `${descriptor.displayName} plugin`,
|
|
36652
36758
|
detail: detailByState[state],
|
|
36653
|
-
...plan ? { fix: plan.instruction } : state === "pending-reload" ? { fix: reloadInstruction(descriptor) } : state === "freshness-unknown" ? { fix: "check `mmi-cli --version` against `npm view @mutmutco/cli version`, then rerun doctor" } : {},
|
|
36759
|
+
...plan ? { fix: plan.instruction } : state === "pending-reload" || state === "staged" ? { fix: reloadInstruction(descriptor) } : state === "freshness-unknown" ? { fix: "check `mmi-cli --version` against `npm view @mutmutco/cli version`, then rerun doctor" } : {},
|
|
36654
36760
|
verbose: [
|
|
36655
36761
|
// The three numbers the legacy builder used to print. They belong here now that this is the only
|
|
36656
36762
|
// plugin row, and a report that names a state without naming the versions behind it is not evidence.
|
|
@@ -37758,7 +37864,7 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
37758
37864
|
var import_node_fs42 = require("node:fs");
|
|
37759
37865
|
var import_node_os18 = require("node:os");
|
|
37760
37866
|
var import_node_path39 = require("node:path");
|
|
37761
|
-
var
|
|
37867
|
+
var import_node_child_process19 = require("node:child_process");
|
|
37762
37868
|
var import_node_util8 = require("node:util");
|
|
37763
37869
|
|
|
37764
37870
|
// src/discard-sink.ts
|
|
@@ -37767,13 +37873,13 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
|
|
|
37767
37873
|
}
|
|
37768
37874
|
|
|
37769
37875
|
// src/doctor-io.ts
|
|
37770
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
37876
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process19.execFile);
|
|
37771
37877
|
function execFileCapture(file, args, opts = {}) {
|
|
37772
37878
|
const sink = nodeDiscardSinkPath();
|
|
37773
37879
|
const inFd = (0, import_node_fs42.openSync)(sink, "r");
|
|
37774
37880
|
const errFd = (0, import_node_fs42.openSync)(sink, "w");
|
|
37775
37881
|
try {
|
|
37776
|
-
return (0,
|
|
37882
|
+
return (0, import_node_child_process19.execFileSync)(file, args, {
|
|
37777
37883
|
...opts,
|
|
37778
37884
|
encoding: "utf8",
|
|
37779
37885
|
stdio: [inFd, "pipe", errFd]
|
|
@@ -37784,6 +37890,18 @@ function execFileCapture(file, args, opts = {}) {
|
|
|
37784
37890
|
}
|
|
37785
37891
|
}
|
|
37786
37892
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
37893
|
+
function stagedClaudePluginVersion() {
|
|
37894
|
+
try {
|
|
37895
|
+
const stagingRoot = (0, import_node_path39.join)((0, import_node_os18.homedir)(), ".claude", "plugins", "staging");
|
|
37896
|
+
const record = JSON.parse((0, import_node_fs42.readFileSync)((0, import_node_path39.join)(stagingRoot, "launch-target.json"), "utf8"));
|
|
37897
|
+
if (typeof record.target !== "string" || typeof record.incoming !== "string") return void 0;
|
|
37898
|
+
const manifest = JSON.parse((0, import_node_fs42.readFileSync)((0, import_node_path39.join)(record.incoming, ".claude-plugin", "plugin.json"), "utf8"));
|
|
37899
|
+
if (typeof manifest.version !== "string") return void 0;
|
|
37900
|
+
return manifest.version === record.target ? record.target : void 0;
|
|
37901
|
+
} catch {
|
|
37902
|
+
return void 0;
|
|
37903
|
+
}
|
|
37904
|
+
}
|
|
37787
37905
|
function installedClaudePluginVersion() {
|
|
37788
37906
|
try {
|
|
37789
37907
|
const file = JSON.parse(
|
|
@@ -38042,6 +38160,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38042
38160
|
const hermes = token === "hermes" ? readHermesPluginEvidence(process.env) : void 0;
|
|
38043
38161
|
const kimi = token === "kimi" ? kimiPluginHostEvidence(surfaceConfigRoot("kimi")) : void 0;
|
|
38044
38162
|
const installedVersion = hermes?.installedVersion ?? installedSurfacePluginVersion(runtimeSurface);
|
|
38163
|
+
const stagedVersion = token === "claude" ? stagedClaudePluginVersion() : void 0;
|
|
38045
38164
|
surfaceEvidence = {
|
|
38046
38165
|
descriptor,
|
|
38047
38166
|
// Hermes' root is the host evidence: a configured but uninstalled MMI tree is missing, while an
|
|
@@ -38053,7 +38172,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38053
38172
|
manifest: hermes?.manifest ?? (installedVersion ? "valid" : snapshot.installRecordPresent ? "invalid" : "missing"),
|
|
38054
38173
|
guardState: buildPluginGuardDecision(snapshot).state,
|
|
38055
38174
|
...kimi?.receipt ? { receipt: kimi.receipt } : {},
|
|
38056
|
-
...installedVersion ? { installedVersion } : {}
|
|
38175
|
+
...installedVersion ? { installedVersion } : {},
|
|
38176
|
+
...stagedVersion ? { stagedVersion } : {}
|
|
38057
38177
|
};
|
|
38058
38178
|
return surfaceEvidence;
|
|
38059
38179
|
};
|
|
@@ -38280,7 +38400,9 @@ async function requireFreshTrainCli(commandName) {
|
|
|
38280
38400
|
const converge = await selfConvergeTrainCli({
|
|
38281
38401
|
currentVersion: resolveClientVersion(),
|
|
38282
38402
|
releasedVersion: report.releasedVersion,
|
|
38283
|
-
commandName
|
|
38403
|
+
commandName,
|
|
38404
|
+
// The typed, house-prefixed argv — never the spliced process.argv (#5813).
|
|
38405
|
+
argv: INVOKED_ARGV
|
|
38284
38406
|
});
|
|
38285
38407
|
if (converge.kind === "failed") {
|
|
38286
38408
|
throw new Error(`${commandName}: ${converge.error}. ${staleTrainCliMessage(report, commandName)}`);
|
|
@@ -38375,7 +38497,10 @@ function positionalTargetForm(cmd, opts = {}) {
|
|
|
38375
38497
|
const args = cmd.registeredArguments ?? [];
|
|
38376
38498
|
const first = args[0];
|
|
38377
38499
|
if (!first) return void 0;
|
|
38378
|
-
return formatPositionalTarget(canonicalPathFor(commandPath2(cmd)) ?? commandPath2(cmd), first.name(),
|
|
38500
|
+
return formatPositionalTarget(canonicalPathFor(commandPath2(cmd)) ?? commandPath2(cmd), first.name(), {
|
|
38501
|
+
...opts,
|
|
38502
|
+
ownFlags: commandOwnLongFlags(cmd)
|
|
38503
|
+
});
|
|
38379
38504
|
}
|
|
38380
38505
|
function unknownCommandCandidates(parent, allPaths) {
|
|
38381
38506
|
if (!parent) return [...allPaths];
|
|
@@ -38470,6 +38595,7 @@ function envelopeAwareWriteErr(str) {
|
|
|
38470
38595
|
if (unknownFlagJsonHandled && argvWantsJson2()) return;
|
|
38471
38596
|
process.stderr.write(str);
|
|
38472
38597
|
}
|
|
38598
|
+
var INVOKED_ARGV = process.argv.slice(2);
|
|
38473
38599
|
var program2 = new Command();
|
|
38474
38600
|
program2.name("mmi-cli").description("MMI Future Hub CLI \u2014 the org control plane for agentic coding.").version(resolveClientVersion()).configureOutput({ writeErr: envelopeAwareWriteErr }).showHelpAfterError(PARSE_HINT_SENTINEL);
|
|
38475
38601
|
program2.addHelpText(
|
|
@@ -38696,7 +38822,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
38696
38822
|
try {
|
|
38697
38823
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
38698
38824
|
if (o.repo) args.push("--repo", o.repo);
|
|
38699
|
-
spawnDetachedSelf(args, { spawn:
|
|
38825
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process20.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
38700
38826
|
} catch {
|
|
38701
38827
|
}
|
|
38702
38828
|
}
|
package/package.json
CHANGED