@mutmutco/cli 3.37.0 → 3.38.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.
- package/dist/main.cjs +63 -12
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -11236,6 +11236,17 @@ function buildIssueArgs({ type, title, body, priority, repo, labels }) {
|
|
|
11236
11236
|
for (const label of labels ?? []) args.push("--label", label);
|
|
11237
11237
|
return args;
|
|
11238
11238
|
}
|
|
11239
|
+
async function ensureLabelsExist(labels, repo, deps = {}) {
|
|
11240
|
+
const run = deps.run ?? execFileP2;
|
|
11241
|
+
for (const label of new Set(labels)) {
|
|
11242
|
+
const args = ["label", "create", label, "--color", "ededed"];
|
|
11243
|
+
if (repo) args.push("--repo", repo);
|
|
11244
|
+
try {
|
|
11245
|
+
await run("gh", args, { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
11246
|
+
} catch {
|
|
11247
|
+
}
|
|
11248
|
+
}
|
|
11249
|
+
}
|
|
11239
11250
|
async function bodyArgsViaFile(args, deps = {}) {
|
|
11240
11251
|
const i = args.indexOf("--body");
|
|
11241
11252
|
if (i === -1 || i + 1 >= args.length) return { args, cleanup: async () => {
|
|
@@ -11961,6 +11972,10 @@ function parseNpmVersion(stdout) {
|
|
|
11961
11972
|
function staleTrainCliMessage(report, commandName) {
|
|
11962
11973
|
return `running mmi-cli ${report.currentVersion} is stale against released ${report.releasedVersion}; this is recoverable \u2014 run \`npm install -g @mutmutco/cli@latest\` to update to ${report.releasedVersion} (a standalone CLI shadows a plugin-provided mmi-cli on PATH and needs no Claude restart), then rerun ${commandName} so it uses the current train path. This gate runs before any train mutation, so this invocation changed nothing; any partial train state (local merge, pushed tag) is from an earlier run and the rerun resumes it safely`;
|
|
11963
11974
|
}
|
|
11975
|
+
function versionAutoUpdateAction(report, releasedSource) {
|
|
11976
|
+
if (report.ok || report.staleAgainst !== "released") return "none";
|
|
11977
|
+
return releasedSource === "gh" ? "npm-unreachable" : "npm";
|
|
11978
|
+
}
|
|
11964
11979
|
|
|
11965
11980
|
// src/plugin-cache-prune.ts
|
|
11966
11981
|
var PLUGIN_CACHE_KEEP = 2;
|
|
@@ -20721,6 +20736,7 @@ function validateBatchSpecs(specs) {
|
|
|
20721
20736
|
return { ok: errors.length === 0, errors, validated };
|
|
20722
20737
|
}
|
|
20723
20738
|
async function createIssuesBatch(specs, options, deps = {}) {
|
|
20739
|
+
const ensureLabels = deps.ensureLabels ?? ensureLabelsExist;
|
|
20724
20740
|
const client = deps.client ?? defaultGitHubClient();
|
|
20725
20741
|
const validation = validateBatchSpecs(specs);
|
|
20726
20742
|
if (!validation.ok) {
|
|
@@ -20733,6 +20749,14 @@ ${lines}`);
|
|
|
20733
20749
|
throw new Error("could not resolve repo \u2014 pass --repo owner/repo or set repo per row");
|
|
20734
20750
|
}
|
|
20735
20751
|
const rowRepo = (spec) => spec.repo ?? defaultRepo;
|
|
20752
|
+
const labelsByRepo = /* @__PURE__ */ new Map();
|
|
20753
|
+
for (const { spec } of validation.validated) {
|
|
20754
|
+
if (!spec.labels?.length) continue;
|
|
20755
|
+
const bucket = labelsByRepo.get(rowRepo(spec)) ?? /* @__PURE__ */ new Set();
|
|
20756
|
+
for (const label of spec.labels) bucket.add(label);
|
|
20757
|
+
labelsByRepo.set(rowRepo(spec), bucket);
|
|
20758
|
+
}
|
|
20759
|
+
for (const [repo, labels] of labelsByRepo) await ensureLabels([...labels], repo);
|
|
20736
20760
|
const created = [];
|
|
20737
20761
|
const failures = [];
|
|
20738
20762
|
for (const entry of validation.validated) {
|
|
@@ -21140,6 +21164,15 @@ async function fetchNpmReleasedVersion() {
|
|
|
21140
21164
|
return void 0;
|
|
21141
21165
|
}
|
|
21142
21166
|
}
|
|
21167
|
+
var NPM_INSTALL_TIMEOUT_MS = 12e4;
|
|
21168
|
+
async function npmSelfUpdateCli() {
|
|
21169
|
+
try {
|
|
21170
|
+
await runHostBin("npm", ["install", "-g", "@mutmutco/cli@latest"], { timeout: NPM_INSTALL_TIMEOUT_MS });
|
|
21171
|
+
return { ok: true, detail: "npm install -g @mutmutco/cli@latest exited 0" };
|
|
21172
|
+
} catch (e) {
|
|
21173
|
+
return { ok: false, detail: e.message.trim().slice(0, 200).replace(/\s+/g, " ") };
|
|
21174
|
+
}
|
|
21175
|
+
}
|
|
21143
21176
|
function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRepo = false) {
|
|
21144
21177
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
21145
21178
|
const installed = readInstalledPlugins(surface);
|
|
@@ -22567,7 +22600,25 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
22567
22600
|
const plugin = checkClaudePlugin({ installed, released, guardState: deps.pluginGuardState(isOrgRepo) });
|
|
22568
22601
|
checks.push(plugin);
|
|
22569
22602
|
if (!plugin.ok) restartPending = true;
|
|
22570
|
-
|
|
22603
|
+
const cliInput = { currentVersion: deps.currentCliVersion(), releasedVersion: released };
|
|
22604
|
+
const cliReport = buildVersionLagReport(cliInput);
|
|
22605
|
+
if (apply && deps.updateCli && versionAutoUpdateAction(cliReport) === "npm") {
|
|
22606
|
+
const heal = await deps.updateCli();
|
|
22607
|
+
const healEvidence = [`running: ${cliReport.currentVersion}`, `published: ${cliReport.releasedVersion}`, `heal: ${heal.detail}`];
|
|
22608
|
+
checks.push(heal.ok ? {
|
|
22609
|
+
ok: true,
|
|
22610
|
+
label: `mmi-cli ${cliReport.currentVersion} \u2192 ${cliReport.releasedVersion}`,
|
|
22611
|
+
detail: "self-updated via npm install -g; the next mmi-cli invocation runs the new version",
|
|
22612
|
+
verbose: healEvidence
|
|
22613
|
+
} : {
|
|
22614
|
+
ok: false,
|
|
22615
|
+
label: `mmi-cli ${cliReport.currentVersion} \u2192 ${cliReport.releasedVersion}`,
|
|
22616
|
+
fix: `self-update failed (${heal.detail}) \u2014 run \`npm install -g @mutmutco/cli@latest\``,
|
|
22617
|
+
verbose: healEvidence
|
|
22618
|
+
});
|
|
22619
|
+
} else {
|
|
22620
|
+
checks.push(checkCliVersion(cliInput));
|
|
22621
|
+
}
|
|
22571
22622
|
checks.push(checkPluginCache(deps.pluginCache()));
|
|
22572
22623
|
if (!opts.fast && !opts.banner && !opts.preflight) {
|
|
22573
22624
|
const probe = await deps.schedulesNotebook().catch((e) => ({
|
|
@@ -22773,6 +22824,8 @@ function mmiDoctorDeps() {
|
|
|
22773
22824
|
installedPluginVersion: installedClaudePluginVersion,
|
|
22774
22825
|
pluginGuardState: claudePluginGuardState,
|
|
22775
22826
|
releasedVersion: fetchNpmReleasedVersion,
|
|
22827
|
+
// #3272: the --apply self-heal for a stale running CLI — npm global, shadows any plugin shim (#2879).
|
|
22828
|
+
updateCli: npmSelfUpdateCli,
|
|
22776
22829
|
currentCliVersion: resolveClientVersion,
|
|
22777
22830
|
readGitignore,
|
|
22778
22831
|
writeGitignore,
|
|
@@ -23858,6 +23911,7 @@ withExamples(mutating(
|
|
|
23858
23911
|
let title;
|
|
23859
23912
|
let issueType;
|
|
23860
23913
|
let extraLabels = [];
|
|
23914
|
+
let targetRepo2;
|
|
23861
23915
|
try {
|
|
23862
23916
|
issueType = resolveCreateType(o.type, "issue create");
|
|
23863
23917
|
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises7.readFile, readStdin });
|
|
@@ -23865,33 +23919,30 @@ withExamples(mutating(
|
|
|
23865
23919
|
if (o.idempotencyKey) body = appendIdempotencyMarker(body, o.idempotencyKey);
|
|
23866
23920
|
priority = resolveCreatePriority(o.priority, "issue create");
|
|
23867
23921
|
extraLabels = [...o.label ?? []];
|
|
23922
|
+
targetRepo2 = await resolveRepo(o.repo);
|
|
23923
|
+
if (!targetRepo2) {
|
|
23924
|
+
return fail("issue create: could not resolve the target repo \u2014 run inside a git checkout or pass --repo <owner/repo>");
|
|
23925
|
+
}
|
|
23868
23926
|
args = buildIssueArgs({
|
|
23869
23927
|
type: issueType,
|
|
23870
23928
|
title,
|
|
23871
23929
|
body,
|
|
23872
23930
|
priority,
|
|
23873
|
-
repo:
|
|
23931
|
+
repo: targetRepo2,
|
|
23874
23932
|
labels: extraLabels.length ? extraLabels : void 0
|
|
23875
23933
|
});
|
|
23876
23934
|
if (o.parent !== void 0) parseIssueRef(o.parent);
|
|
23877
23935
|
} catch (e) {
|
|
23878
23936
|
return fail(`issue create: ${e.message}`, e instanceof TextArgError ? { code: e.code, offending_flag: e.offendingFlag } : void 0);
|
|
23879
23937
|
}
|
|
23880
|
-
|
|
23881
|
-
const la = ["label", "create", label, "--color", "ededed"];
|
|
23882
|
-
if (o.repo) la.push("--repo", o.repo);
|
|
23883
|
-
try {
|
|
23884
|
-
await execFileP2("gh", la, { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
23885
|
-
} catch {
|
|
23886
|
-
}
|
|
23887
|
-
}
|
|
23938
|
+
await ensureLabelsExist(extraLabels, targetRepo2);
|
|
23888
23939
|
const created = await ghCreate(args);
|
|
23889
|
-
const { projectItemId, onBoard } = await attachToProject(created.number,
|
|
23940
|
+
const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo2, priority);
|
|
23890
23941
|
let parent;
|
|
23891
23942
|
let parentLinkError;
|
|
23892
23943
|
if (o.parent !== void 0) {
|
|
23893
23944
|
try {
|
|
23894
|
-
parent = await linkSubIssue(ghRunner2, o.parent, created.url,
|
|
23945
|
+
parent = await linkSubIssue(ghRunner2, o.parent, created.url, targetRepo2);
|
|
23895
23946
|
} catch (e) {
|
|
23896
23947
|
const err = e;
|
|
23897
23948
|
parentLinkError = (err.stderr || err.message || String(e)).trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.38.0",
|
|
4
4
|
"description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|