@mutmutco/cli 3.6.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 +82 -139
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5897,7 +5897,7 @@ function parseGhPrChecksOutput(stdout) {
|
|
|
5897
5897
|
if (!lines.length) return "no-checks-reported";
|
|
5898
5898
|
let anyPending = false;
|
|
5899
5899
|
for (const line of lines) {
|
|
5900
|
-
const parts = line.split(/\s+/);
|
|
5900
|
+
const parts = line.includes(" ") ? line.split(" ") : line.split(/\s+/);
|
|
5901
5901
|
const state = (parts[1] ?? "").toLowerCase();
|
|
5902
5902
|
if (state === "fail" || state === "failure" || state === "error") return "failure";
|
|
5903
5903
|
if (state === "pass" || state === "success" || state === "skipping" || state === "skipped") continue;
|
|
@@ -5916,22 +5916,46 @@ var PR_CHECKS_POLL_MS = 3e4;
|
|
|
5916
5916
|
var PR_CHECKS_TIMEOUT_MS = 10 * 6e4;
|
|
5917
5917
|
var PR_CHECKS_SUCCESS_CONFIRMATIONS = 2;
|
|
5918
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
|
+
}
|
|
5919
5935
|
async function waitForPrChecks(deps) {
|
|
5920
|
-
|
|
5936
|
+
let { policy, reason } = await deps.resolvePolicy();
|
|
5937
|
+
const queuedStates = [];
|
|
5921
5938
|
if (policy === "no-ci") {
|
|
5922
|
-
|
|
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);
|
|
5923
5947
|
}
|
|
5924
5948
|
const now = deps.now ?? (() => Date.now());
|
|
5925
5949
|
const deadline = now() + PR_CHECKS_TIMEOUT_MS;
|
|
5926
5950
|
let lastDetail = "pending";
|
|
5927
5951
|
let successStreak = 0;
|
|
5928
5952
|
while (now() < deadline) {
|
|
5929
|
-
const state = await deps.pollChecks();
|
|
5930
|
-
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 };
|
|
5931
5955
|
if (state === "success") {
|
|
5932
5956
|
successStreak += 1;
|
|
5933
5957
|
if (successStreak >= PR_CHECKS_SUCCESS_CONFIRMATIONS) {
|
|
5934
|
-
return { policy, status: "success", detail: "checks-success" };
|
|
5958
|
+
return { policy, status: "success", reason, detail: "checks-success" };
|
|
5935
5959
|
}
|
|
5936
5960
|
lastDetail = "confirming-success";
|
|
5937
5961
|
await deps.sleep(PR_CHECKS_CONFIRM_MS);
|
|
@@ -5946,7 +5970,7 @@ async function waitForPrChecks(deps) {
|
|
|
5946
5970
|
lastDetail = state;
|
|
5947
5971
|
await deps.sleep(PR_CHECKS_POLL_MS);
|
|
5948
5972
|
}
|
|
5949
|
-
return { policy, status: "timeout", detail: lastDetail };
|
|
5973
|
+
return { policy, status: "timeout", reason, detail: lastDetail };
|
|
5950
5974
|
}
|
|
5951
5975
|
|
|
5952
5976
|
// src/bootstrap-ruleset.ts
|
|
@@ -6736,6 +6760,22 @@ async function auditRepoCi(repo, deps) {
|
|
|
6736
6760
|
detail: info.delete_branch_on_merge === true ? void 0 : "false or unavailable"
|
|
6737
6761
|
});
|
|
6738
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
|
+
}
|
|
6739
6779
|
if (deployableGated) {
|
|
6740
6780
|
checks.push({
|
|
6741
6781
|
ok: hasGateWorkflow,
|
|
@@ -6770,7 +6810,7 @@ async function auditRepoCi(repo, deps) {
|
|
|
6770
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)`
|
|
6771
6811
|
});
|
|
6772
6812
|
if (hasGateWorkflow) {
|
|
6773
|
-
const emitted = registryRequiredContexts(meta) ?? await
|
|
6813
|
+
const emitted = registryRequiredContexts(meta) ?? await getEmittedPrContexts();
|
|
6774
6814
|
if (emitted.length > 0) {
|
|
6775
6815
|
const aligned = contextsMatchRuleset(statusChecks, new Set(emitted));
|
|
6776
6816
|
checks.push({
|
|
@@ -13365,11 +13405,12 @@ function authorizeBodyHasMismatch(body) {
|
|
|
13365
13405
|
}
|
|
13366
13406
|
|
|
13367
13407
|
// src/project-set.ts
|
|
13368
|
-
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"];
|
|
13369
13409
|
var UNSET_KEY_SET = new Set(UNSET_KEYS);
|
|
13370
13410
|
var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
|
|
13371
|
-
var SECRET_CONSUMERS = ["runtime", "lambda", "actions", "agent", "box"];
|
|
13411
|
+
var SECRET_CONSUMERS = ["runtime", "build", "lambda", "actions", "agent", "box"];
|
|
13372
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)$/;
|
|
13373
13414
|
function parseSecretsCatalogVar(raw) {
|
|
13374
13415
|
let parsed;
|
|
13375
13416
|
try {
|
|
@@ -13433,6 +13474,21 @@ function parseRuntimeSecretsVar(raw) {
|
|
|
13433
13474
|
}
|
|
13434
13475
|
return out;
|
|
13435
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
|
+
}
|
|
13436
13492
|
function parseEdgeDomainsVar(raw) {
|
|
13437
13493
|
let parsed;
|
|
13438
13494
|
try {
|
|
@@ -13657,6 +13713,7 @@ var SETTABLE_VAR_KEYS = [
|
|
|
13657
13713
|
"consumesDesignSystem",
|
|
13658
13714
|
"requiredGcpApis",
|
|
13659
13715
|
"requiredRuntimeSecrets",
|
|
13716
|
+
"requiredBuildSecrets",
|
|
13660
13717
|
"edgeDomains",
|
|
13661
13718
|
"statusFieldId",
|
|
13662
13719
|
"statusOptions",
|
|
@@ -13680,7 +13737,8 @@ var SETTABLE_VAR_HINTS = {
|
|
|
13680
13737
|
oauth: "JSON {subdomains,domains,callbackPath,fofuSubdomain}",
|
|
13681
13738
|
requiredGcpApis: "comma-string",
|
|
13682
13739
|
requiredRuntimeSecrets: 'JSON stage map, e.g. {"dev":["KEY"],"rc":["KEY"],"main":["KEY"]}',
|
|
13683
|
-
|
|
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",
|
|
13684
13742
|
edgeDomains: "JSON {dev,rc,main} domain map",
|
|
13685
13743
|
statusOptions: "JSON name\u2192id map",
|
|
13686
13744
|
priorityOptions: "JSON {Urgent,High,Medium,Low}\u2192id map",
|
|
@@ -13736,6 +13794,8 @@ function buildProjectSetPatch(input) {
|
|
|
13736
13794
|
patch[key] = n;
|
|
13737
13795
|
} else if (key === "requiredRuntimeSecrets") {
|
|
13738
13796
|
patch[key] = parseRuntimeSecretsVar(raw);
|
|
13797
|
+
} else if (key === "requiredBuildSecrets") {
|
|
13798
|
+
patch[key] = parseBuildSecretsVar(raw);
|
|
13739
13799
|
} else if (key === "secrets") {
|
|
13740
13800
|
patch[key] = parseSecretsCatalogVar(raw);
|
|
13741
13801
|
} else if (key === "edgeDomains") {
|
|
@@ -15715,59 +15775,6 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
15715
15775
|
staleSurfaces: stale
|
|
15716
15776
|
};
|
|
15717
15777
|
}
|
|
15718
|
-
var MANAGED_PLUGINS = [
|
|
15719
|
-
{
|
|
15720
|
-
// Marketplace-qualified key exactly as installed_plugins.json stores it (same convention as
|
|
15721
|
-
// MMI_PLUGIN_ID = 'mmi@mutmutco') — a bare 'jervaise-powertools' would never match a real record
|
|
15722
|
-
// and the check would be a permanent no-op. The same `<marketplace>/<name>` segments also address its
|
|
15723
|
-
// Cursor Team Marketplace cache dir (~/.cursor/plugins/cache/jervaise/jervaise-powertools/<version>/).
|
|
15724
|
-
id: "jervaise-powertools@jervaise",
|
|
15725
|
-
label: "jervaise-powertools",
|
|
15726
|
-
healCommand: "jerv-cli doctor --apply",
|
|
15727
|
-
opencodePackage: "@jervaise/opencode-jerv"
|
|
15728
|
-
}
|
|
15729
|
-
];
|
|
15730
|
-
function parseManagedPluginId(id) {
|
|
15731
|
-
const at = id.lastIndexOf("@");
|
|
15732
|
-
if (at <= 0 || at === id.length - 1) return null;
|
|
15733
|
-
return { name: id.slice(0, at), marketplace: id.slice(at + 1) };
|
|
15734
|
-
}
|
|
15735
|
-
var MANAGED_PLUGIN_DRIFT_LABEL = "managed org plugin version drift (cross-surface)";
|
|
15736
|
-
function managedPluginDriftFix(drifted) {
|
|
15737
|
-
return drifted.map((d) => {
|
|
15738
|
-
const versions = d.versions.map((v) => `${v.version} (${v.surface})`).join(" vs ");
|
|
15739
|
-
return `${d.label} drift: ${versions} \u2192 run \`${d.healCommand}\``;
|
|
15740
|
-
}).join(" ; ");
|
|
15741
|
-
}
|
|
15742
|
-
function buildManagedPluginDriftCheck(input) {
|
|
15743
|
-
const base = { ok: true, label: MANAGED_PLUGIN_DRIFT_LABEL, fix: "" };
|
|
15744
|
-
if (!input.isOrgRepo) return base;
|
|
15745
|
-
const registry2 = input.plugins ?? MANAGED_PLUGINS;
|
|
15746
|
-
const drifted = [];
|
|
15747
|
-
const normalize = (v) => v.replace(/^v/, "");
|
|
15748
|
-
for (const descriptor of registry2) {
|
|
15749
|
-
const versions = [];
|
|
15750
|
-
for (const source of input.sources) {
|
|
15751
|
-
if (source.directVersions && descriptor.id in source.directVersions) {
|
|
15752
|
-
const version2 = source.directVersions[descriptor.id];
|
|
15753
|
-
if (isSemverVersion(version2)) versions.push({ surface: source.surface, version: normalize(version2) });
|
|
15754
|
-
continue;
|
|
15755
|
-
}
|
|
15756
|
-
const records = source.installed?.plugins?.[descriptor.id];
|
|
15757
|
-
if (!Array.isArray(records) || records.length === 0) continue;
|
|
15758
|
-
const recordVersion = bestRecord(records).version;
|
|
15759
|
-
const version = isSemverVersion(recordVersion) ? recordVersion : highestSemver(source.cacheVersions?.[descriptor.id] ?? []);
|
|
15760
|
-
if (!isSemverVersion(version)) continue;
|
|
15761
|
-
versions.push({ surface: source.surface, version: normalize(version) });
|
|
15762
|
-
}
|
|
15763
|
-
if (versions.length < 2) continue;
|
|
15764
|
-
const distinctVersions = new Set(versions.map((v) => v.version));
|
|
15765
|
-
if (distinctVersions.size < 2) continue;
|
|
15766
|
-
drifted.push({ pluginId: descriptor.id, label: descriptor.label, healCommand: descriptor.healCommand, versions });
|
|
15767
|
-
}
|
|
15768
|
-
if (drifted.length === 0) return base;
|
|
15769
|
-
return { ...base, ok: false, fix: managedPluginDriftFix(drifted), drifted };
|
|
15770
|
-
}
|
|
15771
15778
|
var OPENCODE_VERSION_LABEL = "installed OpenCode MMI adapter version (vs latest release)";
|
|
15772
15779
|
function buildOpencodeVersionCheck(input) {
|
|
15773
15780
|
const fix = pluginRecoveryFix("opencode");
|
|
@@ -17235,77 +17242,6 @@ function installedPluginSources() {
|
|
|
17235
17242
|
}
|
|
17236
17243
|
});
|
|
17237
17244
|
}
|
|
17238
|
-
function managedPluginSurfaceSources() {
|
|
17239
|
-
const claudeCodex = installedPluginSources().map(({ surface, installed }) => {
|
|
17240
|
-
const cacheVersions = {};
|
|
17241
|
-
for (const descriptor of MANAGED_PLUGINS) {
|
|
17242
|
-
const segments = parseManagedPluginId(descriptor.id);
|
|
17243
|
-
if (!segments) continue;
|
|
17244
|
-
try {
|
|
17245
|
-
cacheVersions[descriptor.id] = (0, import_node_fs18.readdirSync)(
|
|
17246
|
-
(0, import_node_path18.join)((0, import_node_os7.homedir)(), `.${surface}`, "plugins", "cache", segments.marketplace, segments.name),
|
|
17247
|
-
{ withFileTypes: true }
|
|
17248
|
-
).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
17249
|
-
} catch {
|
|
17250
|
-
}
|
|
17251
|
-
}
|
|
17252
|
-
return { surface, installed, cacheVersions };
|
|
17253
|
-
});
|
|
17254
|
-
return [
|
|
17255
|
-
...claudeCodex,
|
|
17256
|
-
{ surface: "cursor", directVersions: managedPluginCursorDirectVersions() },
|
|
17257
|
-
{ surface: "opencode", directVersions: managedPluginOpencodeDirectVersions() }
|
|
17258
|
-
];
|
|
17259
|
-
}
|
|
17260
|
-
function managedPluginCursorDirectVersions() {
|
|
17261
|
-
const out = {};
|
|
17262
|
-
for (const descriptor of MANAGED_PLUGINS) {
|
|
17263
|
-
const segments = parseManagedPluginId(descriptor.id);
|
|
17264
|
-
if (!segments) continue;
|
|
17265
|
-
try {
|
|
17266
|
-
const versions = (0, import_node_fs18.readdirSync)(
|
|
17267
|
-
(0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cursor", "plugins", "cache", segments.marketplace, segments.name),
|
|
17268
|
-
{ withFileTypes: true }
|
|
17269
|
-
).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
17270
|
-
out[descriptor.id] = highestSemver(versions);
|
|
17271
|
-
} catch {
|
|
17272
|
-
}
|
|
17273
|
-
}
|
|
17274
|
-
return out;
|
|
17275
|
-
}
|
|
17276
|
-
function managedPluginOpencodeDirectVersions() {
|
|
17277
|
-
const out = {};
|
|
17278
|
-
for (const descriptor of MANAGED_PLUGINS) {
|
|
17279
|
-
if (!descriptor.opencodePackage) continue;
|
|
17280
|
-
out[descriptor.id] = readOpencodePackageVersion(descriptor.opencodePackage);
|
|
17281
|
-
}
|
|
17282
|
-
return out;
|
|
17283
|
-
}
|
|
17284
|
-
function readOpencodePackageVersionFrom(packageJsonPath) {
|
|
17285
|
-
try {
|
|
17286
|
-
const parsed = JSON.parse((0, import_node_fs18.readFileSync)(packageJsonPath, "utf8"));
|
|
17287
|
-
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
|
|
17288
|
-
} catch {
|
|
17289
|
-
return void 0;
|
|
17290
|
-
}
|
|
17291
|
-
}
|
|
17292
|
-
function readOpencodePackageVersion(packageName) {
|
|
17293
|
-
const [scope, name] = packageName.startsWith("@") ? packageName.slice(1).split("/", 2) : [void 0, packageName];
|
|
17294
|
-
if (!name) return void 0;
|
|
17295
|
-
const diskCandidates = [
|
|
17296
|
-
(0, import_node_path18.join)(opencodeConfigDir(), "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"),
|
|
17297
|
-
(0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json")
|
|
17298
|
-
];
|
|
17299
|
-
const diskVersion = diskCandidates.map(readOpencodePackageVersionFrom).find((v) => v !== void 0);
|
|
17300
|
-
const packagesRoot = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "packages", ...scope ? [`@${scope}`] : []);
|
|
17301
|
-
let cacheVersion;
|
|
17302
|
-
try {
|
|
17303
|
-
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));
|
|
17304
|
-
if (cacheVersions.length) cacheVersion = cacheVersions.reduce((lowest, v) => compareVersions(v, lowest) < 0 ? v : lowest);
|
|
17305
|
-
} catch {
|
|
17306
|
-
}
|
|
17307
|
-
return pickOpencodeActiveVersion({ cacheVersion, diskVersion });
|
|
17308
|
-
}
|
|
17309
17245
|
function readClaudeSettings() {
|
|
17310
17246
|
try {
|
|
17311
17247
|
return JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path18.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
@@ -18192,7 +18128,6 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
18192
18128
|
}
|
|
18193
18129
|
}
|
|
18194
18130
|
checks.push(installedVersionCheck);
|
|
18195
|
-
checks.push(buildManagedPluginDriftCheck({ isOrgRepo, sources: managedPluginSurfaceSources() }));
|
|
18196
18131
|
let openCodeConfigSnapshot = opencodeConfigSnapshot();
|
|
18197
18132
|
const inspectOpenCode = surface === "opencode" || openCodeConfigSnapshot.hasConfig || runExtended;
|
|
18198
18133
|
if (inspectOpenCode) {
|
|
@@ -19551,7 +19486,7 @@ project.command("attest [owner/repo]").description("attest this repo's app-owned
|
|
|
19551
19486
|
const res = await attestAppGaps(slugOf(target), repo, registryClientDeps(cfg));
|
|
19552
19487
|
return reportWrite("project attest", res);
|
|
19553
19488
|
});
|
|
19554
|
-
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) => {
|
|
19555
19490
|
const cfg = await loadConfig();
|
|
19556
19491
|
let target;
|
|
19557
19492
|
try {
|
|
@@ -20185,7 +20120,8 @@ pr.command("checks-wait <number>").description("bounded wait for PR checks; skip
|
|
|
20185
20120
|
const result = await waitForPrChecks({
|
|
20186
20121
|
resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo),
|
|
20187
20122
|
pollChecks: () => pollGhPrChecks(number, repoArgs),
|
|
20188
|
-
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
20123
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
20124
|
+
log: (message) => console.warn(message)
|
|
20189
20125
|
});
|
|
20190
20126
|
if (o.json) printLine(JSON.stringify(result));
|
|
20191
20127
|
else printLine(`pr checks-wait: ${result.status}${result.reason ? ` \u2014 ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
|
|
@@ -20212,7 +20148,8 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
20212
20148
|
waitForChecks: (prNumber, repo) => waitForPrChecks({
|
|
20213
20149
|
resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
|
|
20214
20150
|
pollChecks: () => pollGhPrChecks(prNumber, repo ? ["--repo", repo] : []),
|
|
20215
|
-
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
20151
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
20152
|
+
log: (message) => console.warn(message)
|
|
20216
20153
|
}),
|
|
20217
20154
|
// #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying — a
|
|
20218
20155
|
// fresh read of state/mergeable/checks, independent of the merge call's own (possibly stale) error.
|
|
@@ -20423,6 +20360,12 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
20423
20360
|
const beforeWorktrees = parseWorktreePorcelain(
|
|
20424
20361
|
(await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout
|
|
20425
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
|
+
}
|
|
20426
20369
|
const remoteBefore = await remoteBranchExists2(headRef);
|
|
20427
20370
|
let remoteDeleteAttempted = false;
|
|
20428
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",
|