@mutmutco/cli 3.5.0 → 3.7.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 +92 -147
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -4529,9 +4529,16 @@ function buildSessionStartPlan(verbs) {
|
|
|
4529
4529
|
]
|
|
4530
4530
|
};
|
|
4531
4531
|
}
|
|
4532
|
-
|
|
4532
|
+
var WIN32_TRAMPOLINE = "const{spawn}=require('node:child_process');const a=JSON.parse(process.argv[1]);const c=spawn(a.cmd,a.args,{stdio:'ignore',windowsHide:true,cwd:a.cwd});c.on('exit',x=>process.exit(x??0));c.on('error',()=>process.exit(1));";
|
|
4533
|
+
function spawnDetachedSelf(args, deps, opts = {}) {
|
|
4533
4534
|
try {
|
|
4534
|
-
|
|
4535
|
+
const platform2 = deps.platform ?? process.platform;
|
|
4536
|
+
if (platform2 === "win32") {
|
|
4537
|
+
const payload = JSON.stringify({ cmd: deps.execPath, args: [deps.scriptPath, ...args], cwd: opts.cwd });
|
|
4538
|
+
deps.spawn(deps.execPath, ["-e", WIN32_TRAMPOLINE, payload], { detached: true, stdio: "ignore", windowsHide: true }).unref();
|
|
4539
|
+
return;
|
|
4540
|
+
}
|
|
4541
|
+
deps.spawn(deps.execPath, [deps.scriptPath, ...args], { detached: true, stdio: "ignore", windowsHide: true, ...opts.cwd ? { cwd: opts.cwd } : {} }).unref();
|
|
4535
4542
|
} catch {
|
|
4536
4543
|
}
|
|
4537
4544
|
}
|
|
@@ -5890,7 +5897,7 @@ function parseGhPrChecksOutput(stdout) {
|
|
|
5890
5897
|
if (!lines.length) return "no-checks-reported";
|
|
5891
5898
|
let anyPending = false;
|
|
5892
5899
|
for (const line of lines) {
|
|
5893
|
-
const parts = line.split(/\s+/);
|
|
5900
|
+
const parts = line.includes(" ") ? line.split(" ") : line.split(/\s+/);
|
|
5894
5901
|
const state = (parts[1] ?? "").toLowerCase();
|
|
5895
5902
|
if (state === "fail" || state === "failure" || state === "error") return "failure";
|
|
5896
5903
|
if (state === "pass" || state === "success" || state === "skipping" || state === "skipped") continue;
|
|
@@ -5909,22 +5916,46 @@ var PR_CHECKS_POLL_MS = 3e4;
|
|
|
5909
5916
|
var PR_CHECKS_TIMEOUT_MS = 10 * 6e4;
|
|
5910
5917
|
var PR_CHECKS_SUCCESS_CONFIRMATIONS = 2;
|
|
5911
5918
|
var PR_CHECKS_CONFIRM_MS = 5e3;
|
|
5919
|
+
var NO_CI_LIVE_CHECKS_CONTRADICTION_REASON = "ci:none contradicted by live check runs on PR head";
|
|
5920
|
+
function decidePrMergeNoCiGuard(checks, policyReason = "registry META ci:none") {
|
|
5921
|
+
if (checks === "no-checks-reported") return { action: "proceed" };
|
|
5922
|
+
const staleNote = `registry says no-ci (${policyReason}), but live PR checks exist`;
|
|
5923
|
+
if (checks === "success") {
|
|
5924
|
+
return { action: "proceed", note: `${staleNote}; proceeding because they are green` };
|
|
5925
|
+
}
|
|
5926
|
+
return {
|
|
5927
|
+
action: "refuse",
|
|
5928
|
+
message: [
|
|
5929
|
+
`${staleNote} and are not green (${checks}).`,
|
|
5930
|
+
"Fix the checks or use `mmi-cli pr land` so the CLI waits.",
|
|
5931
|
+
"Then correct registry META: `mmi-cli project set <owner/repo> --var requiredChecks=<check-run-display-names>`."
|
|
5932
|
+
].join(" ")
|
|
5933
|
+
};
|
|
5934
|
+
}
|
|
5912
5935
|
async function waitForPrChecks(deps) {
|
|
5913
|
-
|
|
5936
|
+
let { policy, reason } = await deps.resolvePolicy();
|
|
5937
|
+
const queuedStates = [];
|
|
5914
5938
|
if (policy === "no-ci") {
|
|
5915
|
-
|
|
5939
|
+
const firstState = await deps.pollChecks();
|
|
5940
|
+
if (firstState === "no-checks-reported") {
|
|
5941
|
+
return { policy, status: "skipped", reason };
|
|
5942
|
+
}
|
|
5943
|
+
reason = NO_CI_LIVE_CHECKS_CONTRADICTION_REASON;
|
|
5944
|
+
deps.log?.(`merge CI policy contradiction: ${reason}; waiting for live PR checks`);
|
|
5945
|
+
policy = "wait-for-checks";
|
|
5946
|
+
queuedStates.push(firstState);
|
|
5916
5947
|
}
|
|
5917
5948
|
const now = deps.now ?? (() => Date.now());
|
|
5918
5949
|
const deadline = now() + PR_CHECKS_TIMEOUT_MS;
|
|
5919
5950
|
let lastDetail = "pending";
|
|
5920
5951
|
let successStreak = 0;
|
|
5921
5952
|
while (now() < deadline) {
|
|
5922
|
-
const state = await deps.pollChecks();
|
|
5923
|
-
if (state === "failure") return { policy, status: "failure", detail: lastDetail };
|
|
5953
|
+
const state = queuedStates.shift() ?? await deps.pollChecks();
|
|
5954
|
+
if (state === "failure") return { policy, status: "failure", reason, detail: lastDetail };
|
|
5924
5955
|
if (state === "success") {
|
|
5925
5956
|
successStreak += 1;
|
|
5926
5957
|
if (successStreak >= PR_CHECKS_SUCCESS_CONFIRMATIONS) {
|
|
5927
|
-
return { policy, status: "success", detail: "checks-success" };
|
|
5958
|
+
return { policy, status: "success", reason, detail: "checks-success" };
|
|
5928
5959
|
}
|
|
5929
5960
|
lastDetail = "confirming-success";
|
|
5930
5961
|
await deps.sleep(PR_CHECKS_CONFIRM_MS);
|
|
@@ -5939,7 +5970,7 @@ async function waitForPrChecks(deps) {
|
|
|
5939
5970
|
lastDetail = state;
|
|
5940
5971
|
await deps.sleep(PR_CHECKS_POLL_MS);
|
|
5941
5972
|
}
|
|
5942
|
-
return { policy, status: "timeout", detail: lastDetail };
|
|
5973
|
+
return { policy, status: "timeout", reason, detail: lastDetail };
|
|
5943
5974
|
}
|
|
5944
5975
|
|
|
5945
5976
|
// src/bootstrap-ruleset.ts
|
|
@@ -6729,6 +6760,22 @@ async function auditRepoCi(repo, deps) {
|
|
|
6729
6760
|
detail: info.delete_branch_on_merge === true ? void 0 : "false or unavailable"
|
|
6730
6761
|
});
|
|
6731
6762
|
const hasGateWorkflow = repoClass === "hub" ? true : repoClass === "content" ? true : await contentExists(deps, repo, baseBranch, PRODUCT_GATE_PATH);
|
|
6763
|
+
let emittedPrContexts;
|
|
6764
|
+
const getEmittedPrContexts = async () => {
|
|
6765
|
+
emittedPrContexts ??= await resolveEmittedPrContexts(deps, repo, baseBranch);
|
|
6766
|
+
return emittedPrContexts;
|
|
6767
|
+
};
|
|
6768
|
+
if (explicitNoCi) {
|
|
6769
|
+
const emitted = await getEmittedPrContexts();
|
|
6770
|
+
if (emitted.length > 0) {
|
|
6771
|
+
checks.push({
|
|
6772
|
+
ok: false,
|
|
6773
|
+
label: "registry no-ci contradicts PR workflows",
|
|
6774
|
+
detail: `registry META declares no-ci but pull_request workflows emit [${emitted.join(", ")}]`,
|
|
6775
|
+
remediation: `Fix the workflows or correct registry META: mmi-cli project set ${repo} --var requiredChecks=${JSON.stringify(emitted)}`
|
|
6776
|
+
});
|
|
6777
|
+
}
|
|
6778
|
+
}
|
|
6732
6779
|
if (deployableGated) {
|
|
6733
6780
|
checks.push({
|
|
6734
6781
|
ok: hasGateWorkflow,
|
|
@@ -6763,7 +6810,7 @@ async function auditRepoCi(repo, deps) {
|
|
|
6763
6810
|
remediation: hasRequiredChecks ? void 0 : `Import ${PRODUCT_RULESET_REF} as an active repository ruleset (GitHub \u2192 Settings \u2192 Rules \u2192 Rulesets) \u2014 target: bootstrap --apply automation (#1440)`
|
|
6764
6811
|
});
|
|
6765
6812
|
if (hasGateWorkflow) {
|
|
6766
|
-
const emitted = registryRequiredContexts(meta) ?? await
|
|
6813
|
+
const emitted = registryRequiredContexts(meta) ?? await getEmittedPrContexts();
|
|
6767
6814
|
if (emitted.length > 0) {
|
|
6768
6815
|
const aligned = contextsMatchRuleset(statusChecks, new Set(emitted));
|
|
6769
6816
|
checks.push({
|
|
@@ -13358,11 +13405,12 @@ function authorizeBodyHasMismatch(body) {
|
|
|
13358
13405
|
}
|
|
13359
13406
|
|
|
13360
13407
|
// src/project-set.ts
|
|
13361
|
-
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "gate"];
|
|
13408
|
+
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "gate"];
|
|
13362
13409
|
var UNSET_KEY_SET = new Set(UNSET_KEYS);
|
|
13363
13410
|
var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
|
|
13364
|
-
var SECRET_CONSUMERS = ["runtime", "lambda", "actions", "agent", "box"];
|
|
13411
|
+
var SECRET_CONSUMERS = ["runtime", "build", "lambda", "actions", "agent", "box"];
|
|
13365
13412
|
var SECRET_ENV_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
13413
|
+
var BUILD_SECRET_REF_RE = /^(?:[A-Z_][A-Z0-9_]*=)?(?:[A-Z_][A-Z0-9_]*|(?:mm-fofu|_org\/[a-z0-9][a-z0-9-]*):[A-Z_][A-Z0-9_]*|@github-packages-token)$/;
|
|
13366
13414
|
function parseSecretsCatalogVar(raw) {
|
|
13367
13415
|
let parsed;
|
|
13368
13416
|
try {
|
|
@@ -13426,6 +13474,21 @@ function parseRuntimeSecretsVar(raw) {
|
|
|
13426
13474
|
}
|
|
13427
13475
|
return out;
|
|
13428
13476
|
}
|
|
13477
|
+
function parseBuildSecretsVar(raw) {
|
|
13478
|
+
let parsed;
|
|
13479
|
+
try {
|
|
13480
|
+
parsed = JSON.parse(raw);
|
|
13481
|
+
} catch {
|
|
13482
|
+
throw new Error('project set: requiredBuildSecrets must be JSON, e.g. ["NODE_AUTH_TOKEN=@github-packages-token"]');
|
|
13483
|
+
}
|
|
13484
|
+
if (!Array.isArray(parsed)) {
|
|
13485
|
+
throw new Error("project set: requiredBuildSecrets must be a flat array (build secrets are not stage-mapped)");
|
|
13486
|
+
}
|
|
13487
|
+
if (parsed.some((n) => typeof n !== "string" || !BUILD_SECRET_REF_RE.test(n) || n === "@github-packages-token")) {
|
|
13488
|
+
throw new Error("project set: requiredBuildSecrets must be a flat array of ENVID=<ref> or bare <ref> strings");
|
|
13489
|
+
}
|
|
13490
|
+
return parsed;
|
|
13491
|
+
}
|
|
13429
13492
|
function parseEdgeDomainsVar(raw) {
|
|
13430
13493
|
let parsed;
|
|
13431
13494
|
try {
|
|
@@ -13650,6 +13713,7 @@ var SETTABLE_VAR_KEYS = [
|
|
|
13650
13713
|
"consumesDesignSystem",
|
|
13651
13714
|
"requiredGcpApis",
|
|
13652
13715
|
"requiredRuntimeSecrets",
|
|
13716
|
+
"requiredBuildSecrets",
|
|
13653
13717
|
"edgeDomains",
|
|
13654
13718
|
"statusFieldId",
|
|
13655
13719
|
"statusOptions",
|
|
@@ -13673,7 +13737,8 @@ var SETTABLE_VAR_HINTS = {
|
|
|
13673
13737
|
oauth: "JSON {subdomains,domains,callbackPath,fofuSubdomain}",
|
|
13674
13738
|
requiredGcpApis: "comma-string",
|
|
13675
13739
|
requiredRuntimeSecrets: 'JSON stage map, e.g. {"dev":["KEY"],"rc":["KEY"],"main":["KEY"]}',
|
|
13676
|
-
|
|
13740
|
+
requiredBuildSecrets: 'JSON flat array, e.g. ["NODE_AUTH_TOKEN=@github-packages-token"]',
|
|
13741
|
+
secrets: "JSON catalog map keyed by KEY {key,purpose,group,owner,stages[],consumers[]} \u2014 merged per entry; clear with --unset secrets; prefer --secrets-file",
|
|
13677
13742
|
edgeDomains: "JSON {dev,rc,main} domain map",
|
|
13678
13743
|
statusOptions: "JSON name\u2192id map",
|
|
13679
13744
|
priorityOptions: "JSON {Urgent,High,Medium,Low}\u2192id map",
|
|
@@ -13729,6 +13794,8 @@ function buildProjectSetPatch(input) {
|
|
|
13729
13794
|
patch[key] = n;
|
|
13730
13795
|
} else if (key === "requiredRuntimeSecrets") {
|
|
13731
13796
|
patch[key] = parseRuntimeSecretsVar(raw);
|
|
13797
|
+
} else if (key === "requiredBuildSecrets") {
|
|
13798
|
+
patch[key] = parseBuildSecretsVar(raw);
|
|
13732
13799
|
} else if (key === "secrets") {
|
|
13733
13800
|
patch[key] = parseSecretsCatalogVar(raw);
|
|
13734
13801
|
} else if (key === "edgeDomains") {
|
|
@@ -15708,59 +15775,6 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
15708
15775
|
staleSurfaces: stale
|
|
15709
15776
|
};
|
|
15710
15777
|
}
|
|
15711
|
-
var MANAGED_PLUGINS = [
|
|
15712
|
-
{
|
|
15713
|
-
// Marketplace-qualified key exactly as installed_plugins.json stores it (same convention as
|
|
15714
|
-
// MMI_PLUGIN_ID = 'mmi@mutmutco') — a bare 'jervaise-powertools' would never match a real record
|
|
15715
|
-
// and the check would be a permanent no-op. The same `<marketplace>/<name>` segments also address its
|
|
15716
|
-
// Cursor Team Marketplace cache dir (~/.cursor/plugins/cache/jervaise/jervaise-powertools/<version>/).
|
|
15717
|
-
id: "jervaise-powertools@jervaise",
|
|
15718
|
-
label: "jervaise-powertools",
|
|
15719
|
-
healCommand: "jerv-cli doctor --apply",
|
|
15720
|
-
opencodePackage: "@jervaise/opencode-jerv"
|
|
15721
|
-
}
|
|
15722
|
-
];
|
|
15723
|
-
function parseManagedPluginId(id) {
|
|
15724
|
-
const at = id.lastIndexOf("@");
|
|
15725
|
-
if (at <= 0 || at === id.length - 1) return null;
|
|
15726
|
-
return { name: id.slice(0, at), marketplace: id.slice(at + 1) };
|
|
15727
|
-
}
|
|
15728
|
-
var MANAGED_PLUGIN_DRIFT_LABEL = "managed org plugin version drift (cross-surface)";
|
|
15729
|
-
function managedPluginDriftFix(drifted) {
|
|
15730
|
-
return drifted.map((d) => {
|
|
15731
|
-
const versions = d.versions.map((v) => `${v.version} (${v.surface})`).join(" vs ");
|
|
15732
|
-
return `${d.label} drift: ${versions} \u2192 run \`${d.healCommand}\``;
|
|
15733
|
-
}).join(" ; ");
|
|
15734
|
-
}
|
|
15735
|
-
function buildManagedPluginDriftCheck(input) {
|
|
15736
|
-
const base = { ok: true, label: MANAGED_PLUGIN_DRIFT_LABEL, fix: "" };
|
|
15737
|
-
if (!input.isOrgRepo) return base;
|
|
15738
|
-
const registry2 = input.plugins ?? MANAGED_PLUGINS;
|
|
15739
|
-
const drifted = [];
|
|
15740
|
-
const normalize = (v) => v.replace(/^v/, "");
|
|
15741
|
-
for (const descriptor of registry2) {
|
|
15742
|
-
const versions = [];
|
|
15743
|
-
for (const source of input.sources) {
|
|
15744
|
-
if (source.directVersions && descriptor.id in source.directVersions) {
|
|
15745
|
-
const version2 = source.directVersions[descriptor.id];
|
|
15746
|
-
if (isSemverVersion(version2)) versions.push({ surface: source.surface, version: normalize(version2) });
|
|
15747
|
-
continue;
|
|
15748
|
-
}
|
|
15749
|
-
const records = source.installed?.plugins?.[descriptor.id];
|
|
15750
|
-
if (!Array.isArray(records) || records.length === 0) continue;
|
|
15751
|
-
const recordVersion = bestRecord(records).version;
|
|
15752
|
-
const version = isSemverVersion(recordVersion) ? recordVersion : highestSemver(source.cacheVersions?.[descriptor.id] ?? []);
|
|
15753
|
-
if (!isSemverVersion(version)) continue;
|
|
15754
|
-
versions.push({ surface: source.surface, version: normalize(version) });
|
|
15755
|
-
}
|
|
15756
|
-
if (versions.length < 2) continue;
|
|
15757
|
-
const distinctVersions = new Set(versions.map((v) => v.version));
|
|
15758
|
-
if (distinctVersions.size < 2) continue;
|
|
15759
|
-
drifted.push({ pluginId: descriptor.id, label: descriptor.label, healCommand: descriptor.healCommand, versions });
|
|
15760
|
-
}
|
|
15761
|
-
if (drifted.length === 0) return base;
|
|
15762
|
-
return { ...base, ok: false, fix: managedPluginDriftFix(drifted), drifted };
|
|
15763
|
-
}
|
|
15764
15778
|
var OPENCODE_VERSION_LABEL = "installed OpenCode MMI adapter version (vs latest release)";
|
|
15765
15779
|
function buildOpencodeVersionCheck(input) {
|
|
15766
15780
|
const fix = pluginRecoveryFix("opencode");
|
|
@@ -17228,77 +17242,6 @@ function installedPluginSources() {
|
|
|
17228
17242
|
}
|
|
17229
17243
|
});
|
|
17230
17244
|
}
|
|
17231
|
-
function managedPluginSurfaceSources() {
|
|
17232
|
-
const claudeCodex = installedPluginSources().map(({ surface, installed }) => {
|
|
17233
|
-
const cacheVersions = {};
|
|
17234
|
-
for (const descriptor of MANAGED_PLUGINS) {
|
|
17235
|
-
const segments = parseManagedPluginId(descriptor.id);
|
|
17236
|
-
if (!segments) continue;
|
|
17237
|
-
try {
|
|
17238
|
-
cacheVersions[descriptor.id] = (0, import_node_fs18.readdirSync)(
|
|
17239
|
-
(0, import_node_path18.join)((0, import_node_os7.homedir)(), `.${surface}`, "plugins", "cache", segments.marketplace, segments.name),
|
|
17240
|
-
{ withFileTypes: true }
|
|
17241
|
-
).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
17242
|
-
} catch {
|
|
17243
|
-
}
|
|
17244
|
-
}
|
|
17245
|
-
return { surface, installed, cacheVersions };
|
|
17246
|
-
});
|
|
17247
|
-
return [
|
|
17248
|
-
...claudeCodex,
|
|
17249
|
-
{ surface: "cursor", directVersions: managedPluginCursorDirectVersions() },
|
|
17250
|
-
{ surface: "opencode", directVersions: managedPluginOpencodeDirectVersions() }
|
|
17251
|
-
];
|
|
17252
|
-
}
|
|
17253
|
-
function managedPluginCursorDirectVersions() {
|
|
17254
|
-
const out = {};
|
|
17255
|
-
for (const descriptor of MANAGED_PLUGINS) {
|
|
17256
|
-
const segments = parseManagedPluginId(descriptor.id);
|
|
17257
|
-
if (!segments) continue;
|
|
17258
|
-
try {
|
|
17259
|
-
const versions = (0, import_node_fs18.readdirSync)(
|
|
17260
|
-
(0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cursor", "plugins", "cache", segments.marketplace, segments.name),
|
|
17261
|
-
{ withFileTypes: true }
|
|
17262
|
-
).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
17263
|
-
out[descriptor.id] = highestSemver(versions);
|
|
17264
|
-
} catch {
|
|
17265
|
-
}
|
|
17266
|
-
}
|
|
17267
|
-
return out;
|
|
17268
|
-
}
|
|
17269
|
-
function managedPluginOpencodeDirectVersions() {
|
|
17270
|
-
const out = {};
|
|
17271
|
-
for (const descriptor of MANAGED_PLUGINS) {
|
|
17272
|
-
if (!descriptor.opencodePackage) continue;
|
|
17273
|
-
out[descriptor.id] = readOpencodePackageVersion(descriptor.opencodePackage);
|
|
17274
|
-
}
|
|
17275
|
-
return out;
|
|
17276
|
-
}
|
|
17277
|
-
function readOpencodePackageVersionFrom(packageJsonPath) {
|
|
17278
|
-
try {
|
|
17279
|
-
const parsed = JSON.parse((0, import_node_fs18.readFileSync)(packageJsonPath, "utf8"));
|
|
17280
|
-
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
|
|
17281
|
-
} catch {
|
|
17282
|
-
return void 0;
|
|
17283
|
-
}
|
|
17284
|
-
}
|
|
17285
|
-
function readOpencodePackageVersion(packageName) {
|
|
17286
|
-
const [scope, name] = packageName.startsWith("@") ? packageName.slice(1).split("/", 2) : [void 0, packageName];
|
|
17287
|
-
if (!name) return void 0;
|
|
17288
|
-
const diskCandidates = [
|
|
17289
|
-
(0, import_node_path18.join)(opencodeConfigDir(), "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"),
|
|
17290
|
-
(0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json")
|
|
17291
|
-
];
|
|
17292
|
-
const diskVersion = diskCandidates.map(readOpencodePackageVersionFrom).find((v) => v !== void 0);
|
|
17293
|
-
const packagesRoot = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "packages", ...scope ? [`@${scope}`] : []);
|
|
17294
|
-
let cacheVersion;
|
|
17295
|
-
try {
|
|
17296
|
-
const cacheVersions = (0, import_node_fs18.readdirSync)(packagesRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith(`${name}@`) && !e.name.startsWith(".")).map((e) => readOpencodePackageVersionFrom((0, import_node_path18.join)(packagesRoot, e.name, "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"))).filter((v) => Boolean(v));
|
|
17297
|
-
if (cacheVersions.length) cacheVersion = cacheVersions.reduce((lowest, v) => compareVersions(v, lowest) < 0 ? v : lowest);
|
|
17298
|
-
} catch {
|
|
17299
|
-
}
|
|
17300
|
-
return pickOpencodeActiveVersion({ cacheVersion, diskVersion });
|
|
17301
|
-
}
|
|
17302
17245
|
function readClaudeSettings() {
|
|
17303
17246
|
try {
|
|
17304
17247
|
return JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path18.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
@@ -18185,7 +18128,6 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
18185
18128
|
}
|
|
18186
18129
|
}
|
|
18187
18130
|
checks.push(installedVersionCheck);
|
|
18188
|
-
checks.push(buildManagedPluginDriftCheck({ isOrgRepo, sources: managedPluginSurfaceSources() }));
|
|
18189
18131
|
let openCodeConfigSnapshot = opencodeConfigSnapshot();
|
|
18190
18132
|
const inspectOpenCode = surface === "opencode" || openCodeConfigSnapshot.hasConfig || runExtended;
|
|
18191
18133
|
if (inspectOpenCode) {
|
|
@@ -19206,12 +19148,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
19206
19148
|
try {
|
|
19207
19149
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
|
|
19208
19150
|
if (o.repo) args.push("--repo", o.repo);
|
|
19209
|
-
(
|
|
19210
|
-
detached: true,
|
|
19211
|
-
stdio: "ignore",
|
|
19212
|
-
windowsHide: true,
|
|
19213
|
-
cwd: process.cwd()
|
|
19214
|
-
}).unref();
|
|
19151
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process12.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
19215
19152
|
} catch {
|
|
19216
19153
|
}
|
|
19217
19154
|
}
|
|
@@ -19549,7 +19486,7 @@ project.command("attest [owner/repo]").description("attest this repo's app-owned
|
|
|
19549
19486
|
const res = await attestAppGaps(slugOf(target), repo, registryClientDeps(cfg));
|
|
19550
19487
|
return reportWrite("project attest", res);
|
|
19551
19488
|
});
|
|
19552
|
-
project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").option("--class <class>", "deployable | content").option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (v2 capability shape)`).option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).option("--var <KEY=VALUE...>", settableVarHelp()).option("--secrets-file <path>", "read the #2244 secrets catalog map (JSON) from a file and
|
|
19489
|
+
project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").option("--class <class>", "deployable | content").option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (v2 capability shape)`).option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).option("--var <KEY=VALUE...>", settableVarHelp()).option("--secrets-file <path>", "read the #2244 secrets catalog map (JSON) from a file and merge it per entry into the existing catalog (clear all with --unset secrets)").option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, requiredBuildSecrets, secrets, edgeDomains, requiredGcpApis, publishRequired").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
19553
19490
|
const cfg = await loadConfig();
|
|
19554
19491
|
let target;
|
|
19555
19492
|
try {
|
|
@@ -20183,7 +20120,8 @@ pr.command("checks-wait <number>").description("bounded wait for PR checks; skip
|
|
|
20183
20120
|
const result = await waitForPrChecks({
|
|
20184
20121
|
resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo),
|
|
20185
20122
|
pollChecks: () => pollGhPrChecks(number, repoArgs),
|
|
20186
|
-
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
20123
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
20124
|
+
log: (message) => console.warn(message)
|
|
20187
20125
|
});
|
|
20188
20126
|
if (o.json) printLine(JSON.stringify(result));
|
|
20189
20127
|
else printLine(`pr checks-wait: ${result.status}${result.reason ? ` \u2014 ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
|
|
@@ -20210,7 +20148,8 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
20210
20148
|
waitForChecks: (prNumber, repo) => waitForPrChecks({
|
|
20211
20149
|
resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
|
|
20212
20150
|
pollChecks: () => pollGhPrChecks(prNumber, repo ? ["--repo", repo] : []),
|
|
20213
|
-
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
20151
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
20152
|
+
log: (message) => console.warn(message)
|
|
20214
20153
|
}),
|
|
20215
20154
|
// #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying — a
|
|
20216
20155
|
// fresh read of state/mergeable/checks, independent of the merge call's own (possibly stale) error.
|
|
@@ -20421,6 +20360,12 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
20421
20360
|
const beforeWorktrees = parseWorktreePorcelain(
|
|
20422
20361
|
(await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout
|
|
20423
20362
|
);
|
|
20363
|
+
const ciPolicy = await resolveMergeCiPolicyForCheckout(o.repo);
|
|
20364
|
+
if (ciPolicy.policy === "no-ci") {
|
|
20365
|
+
const guard = decidePrMergeNoCiGuard(await pollGhPrChecks(number, repoArgs), ciPolicy.reason);
|
|
20366
|
+
if (guard.action === "refuse") throw new Error(`gh pr merge ${number}: ${guard.message}`);
|
|
20367
|
+
if (guard.note) console.warn(`pr merge: ${guard.note}`);
|
|
20368
|
+
}
|
|
20424
20369
|
const remoteBefore = await remoteBranchExists2(headRef);
|
|
20425
20370
|
let remoteDeleteAttempted = false;
|
|
20426
20371
|
let remoteNotAttemptedReason;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.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",
|