@mutmutco/cli 4.2.0 → 4.2.1
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 +279 -229
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3415,8 +3415,8 @@ var program = new Command();
|
|
|
3415
3415
|
|
|
3416
3416
|
// src/index.ts
|
|
3417
3417
|
var import_promises8 = require("node:fs/promises");
|
|
3418
|
-
var
|
|
3419
|
-
var
|
|
3418
|
+
var import_node_fs43 = require("node:fs");
|
|
3419
|
+
var import_node_child_process19 = require("node:child_process");
|
|
3420
3420
|
|
|
3421
3421
|
// src/cli-shared.ts
|
|
3422
3422
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -5538,7 +5538,7 @@ function commandLadderHint() {
|
|
|
5538
5538
|
}
|
|
5539
5539
|
|
|
5540
5540
|
// src/index.ts
|
|
5541
|
-
var
|
|
5541
|
+
var import_node_path40 = require("node:path");
|
|
5542
5542
|
|
|
5543
5543
|
// src/merge-ci-policy.ts
|
|
5544
5544
|
function resolveMergeCiPolicy(input) {
|
|
@@ -11762,10 +11762,10 @@ var rollout_plan_default = {
|
|
|
11762
11762
|
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
11763
|
},
|
|
11764
11764
|
baseline: {
|
|
11765
|
-
version: "4.2.
|
|
11766
|
-
tag: "v4.2.
|
|
11767
|
-
commit: "
|
|
11768
|
-
npm: "@mutmutco/cli@4.2.
|
|
11765
|
+
version: "4.2.1",
|
|
11766
|
+
tag: "v4.2.1",
|
|
11767
|
+
commit: "66e67b6905a1",
|
|
11768
|
+
npm: "@mutmutco/cli@4.2.1"
|
|
11769
11769
|
},
|
|
11770
11770
|
exitCriterion: "fleet-n-of-n",
|
|
11771
11771
|
hubOnlyShortcut: "forbidden",
|
|
@@ -11782,14 +11782,14 @@ var rollout_plan_default = {
|
|
|
11782
11782
|
repo: "mutmutco/mmi-hub",
|
|
11783
11783
|
role: "canary",
|
|
11784
11784
|
schedule: "train",
|
|
11785
|
-
v3Target: "v4.2.
|
|
11785
|
+
v3Target: "v4.2.1"
|
|
11786
11786
|
}
|
|
11787
11787
|
],
|
|
11788
11788
|
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
11789
|
rollback: {
|
|
11790
11790
|
independent: true,
|
|
11791
|
-
mechanism: "npm dist-tag latest -> 4.2.
|
|
11792
|
-
v3Target: "v4.2.
|
|
11791
|
+
mechanism: "npm dist-tag latest -> 4.2.1 and redeploy the Hub Lambda from tag v4.2.1 (66e67b6905a1); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
11792
|
+
v3Target: "v4.2.1 (@mutmutco/cli@4.2.1, tag commit 66e67b6905a1 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
11793
11793
|
}
|
|
11794
11794
|
},
|
|
11795
11795
|
{
|
|
@@ -14739,10 +14739,16 @@ async function correlateWorkflowRun(deps, args) {
|
|
|
14739
14739
|
async function watchTenantRun(deps, runId, repo = HUB_REPO3) {
|
|
14740
14740
|
if (runId == null) return "pending";
|
|
14741
14741
|
let watchSaidSuccess = true;
|
|
14742
|
+
const startedAt = Date.now();
|
|
14743
|
+
deps.warn?.(`watching workflow run ${runId} (${repo})`);
|
|
14744
|
+
const heartbeat = deps.warn ? setInterval(() => deps.warn?.(`workflow run ${runId} (${repo}) still running \u2014 ${Math.floor((Date.now() - startedAt) / 6e4)}m elapsed`), 6e4) : void 0;
|
|
14745
|
+
heartbeat?.unref();
|
|
14742
14746
|
try {
|
|
14743
14747
|
await deps.run("gh", ["run", "watch", String(runId), "--repo", repo, "--exit-status"]);
|
|
14744
14748
|
} catch {
|
|
14745
14749
|
watchSaidSuccess = false;
|
|
14750
|
+
} finally {
|
|
14751
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
14746
14752
|
}
|
|
14747
14753
|
const sleep2 = resolveSleep(deps);
|
|
14748
14754
|
let confirmError;
|
|
@@ -16338,8 +16344,9 @@ async function runReleasePublishRetry(deps, runId, options = {}) {
|
|
|
16338
16344
|
}
|
|
16339
16345
|
};
|
|
16340
16346
|
const run = await readRun();
|
|
16341
|
-
|
|
16342
|
-
|
|
16347
|
+
const retryableConclusion = run.conclusion === "failure" || run.conclusion === "cancelled";
|
|
16348
|
+
if (run.databaseId !== runId || run.workflowName !== "publish" || run.event !== "release" || run.status !== "completed" || !retryableConclusion) {
|
|
16349
|
+
throw new Error(`run ${runId} is not a completed failed or cancelled Hub publish release run; nothing was written`);
|
|
16343
16350
|
}
|
|
16344
16351
|
const tag = run.headBranch ?? "";
|
|
16345
16352
|
const tagSha = run.headSha ?? "";
|
|
@@ -16350,13 +16357,16 @@ async function runReleasePublishRetry(deps, runId, options = {}) {
|
|
|
16350
16357
|
throw new Error(`run ${runId} does not match origin ${tag}; nothing was written`);
|
|
16351
16358
|
}
|
|
16352
16359
|
await verifyPublishedRelease(deps, ctx.repo, tag, "main", tagSha);
|
|
16353
|
-
await deps.run("gh", [
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
+
await deps.run("gh", [
|
|
16361
|
+
"run",
|
|
16362
|
+
"rerun",
|
|
16363
|
+
String(runId),
|
|
16364
|
+
"--repo",
|
|
16365
|
+
ctx.repo,
|
|
16366
|
+
...run.conclusion === "failure" ? ["--failed"] : []
|
|
16367
|
+
]);
|
|
16368
|
+
if (options.watch && await watchTenantRun(deps, runId, ctx.repo) !== "success") {
|
|
16369
|
+
throw new Error(`publish run ${runId} did not finish successfully after retry`);
|
|
16360
16370
|
}
|
|
16361
16371
|
return {
|
|
16362
16372
|
command: "release-retry-publish",
|
|
@@ -16366,7 +16376,7 @@ async function runReleasePublishRetry(deps, runId, options = {}) {
|
|
|
16366
16376
|
runId,
|
|
16367
16377
|
runUrl: run.url ?? `https://github.com/${ctx.repo}/actions/runs/${runId}`,
|
|
16368
16378
|
status: options.watch ? "success" : "pending",
|
|
16369
|
-
note: options.watch ? `publish run ${runId} retried and passed` : `publish run ${runId}
|
|
16379
|
+
note: options.watch ? `publish run ${runId} retried and passed` : `publish run ${runId} queued for retry`
|
|
16370
16380
|
};
|
|
16371
16381
|
}
|
|
16372
16382
|
async function runTrainApplyPipeline(mode, input) {
|
|
@@ -17707,6 +17717,12 @@ async function finalizeCiReconcile(repo, deps, result, before, pendingReason) {
|
|
|
17707
17717
|
return result;
|
|
17708
17718
|
}
|
|
17709
17719
|
}
|
|
17720
|
+
function ciReconcileExitFailed(input) {
|
|
17721
|
+
if (!input.apply || input.applyResults.length === 0) {
|
|
17722
|
+
return !input.audit.ok || input.applyResults.some((result) => result.errors.length > 0 || result.postApply?.state === "failed");
|
|
17723
|
+
}
|
|
17724
|
+
return input.applyResults.some((result) => result.errors.length > 0 || result.postApply == null || result.postApply.state !== "clean" && result.postApply.state !== "repaired");
|
|
17725
|
+
}
|
|
17710
17726
|
async function authoritativeSafetyHold(authority, deps, repo, baseBranch) {
|
|
17711
17727
|
if (authority.coversReleaseBranches) {
|
|
17712
17728
|
const prOnly = authority.contexts.filter((context) => TRAIN_PR_ONLY_CONTEXTS.has(context));
|
|
@@ -20933,11 +20949,11 @@ function findBoardItem(items, selector, board) {
|
|
|
20933
20949
|
}
|
|
20934
20950
|
async function resolveCurrentRepo(options, deps) {
|
|
20935
20951
|
if (options.repo) return options.repo;
|
|
20936
|
-
const
|
|
20952
|
+
const git3 = deps.git ?? defaultGit;
|
|
20937
20953
|
let gitFailure;
|
|
20938
20954
|
let originUrl = "";
|
|
20939
20955
|
try {
|
|
20940
|
-
originUrl = await
|
|
20956
|
+
originUrl = await git3(["remote", "get-url", "origin"]);
|
|
20941
20957
|
} catch (e) {
|
|
20942
20958
|
const read = gitReadState(e);
|
|
20943
20959
|
if (read.state === "failed") gitFailure = read.detail;
|
|
@@ -23804,31 +23820,31 @@ var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
|
23804
23820
|
function checkHotfixCoverage(options = {}) {
|
|
23805
23821
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
23806
23822
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
23807
|
-
const
|
|
23823
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process12.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
23808
23824
|
const revList = (range) => {
|
|
23809
|
-
const out =
|
|
23825
|
+
const out = git3(["rev-list", "--no-merges", range]).trim();
|
|
23810
23826
|
return out ? out.split("\n") : [];
|
|
23811
23827
|
};
|
|
23812
23828
|
const isAncestor = (sha, ref) => {
|
|
23813
23829
|
try {
|
|
23814
|
-
|
|
23830
|
+
git3(["merge-base", "--is-ancestor", sha, ref]);
|
|
23815
23831
|
return true;
|
|
23816
23832
|
} catch {
|
|
23817
23833
|
return false;
|
|
23818
23834
|
}
|
|
23819
23835
|
};
|
|
23820
23836
|
const patchId = (sha) => {
|
|
23821
|
-
const diff =
|
|
23837
|
+
const diff = git3(["show", "--no-color", "--pretty=format:", sha]);
|
|
23822
23838
|
if (!diff.trim()) return null;
|
|
23823
|
-
const out =
|
|
23839
|
+
const out = git3(["patch-id", "--stable"], { input: diff }).trim();
|
|
23824
23840
|
return out ? out.split(" ")[0] : null;
|
|
23825
23841
|
};
|
|
23826
23842
|
const changedPaths = (sha) => {
|
|
23827
|
-
const out =
|
|
23843
|
+
const out = git3(["show", "--no-color", "--name-only", "--pretty=format:", sha]).trim();
|
|
23828
23844
|
return out ? out.split("\n") : [];
|
|
23829
23845
|
};
|
|
23830
23846
|
const cherrySources = (sha) => {
|
|
23831
|
-
const message =
|
|
23847
|
+
const message = git3(["log", "-1", "--format=%B", sha]);
|
|
23832
23848
|
return [...message.matchAll(CHERRY_TRAILER)].map((m) => m[1]);
|
|
23833
23849
|
};
|
|
23834
23850
|
const mainOnly = revList(`${rcRef}..${mainRef}`);
|
|
@@ -23843,7 +23859,7 @@ function checkHotfixCoverage(options = {}) {
|
|
|
23843
23859
|
}
|
|
23844
23860
|
};
|
|
23845
23861
|
const commits = mainOnly.map((sha) => {
|
|
23846
|
-
const subject =
|
|
23862
|
+
const subject = git3(["log", "-1", "--format=%s", sha]).trim();
|
|
23847
23863
|
const paths = changedPaths(sha);
|
|
23848
23864
|
if (paths.length > 0 && paths.every((p) => manifestPaths.includes(p))) {
|
|
23849
23865
|
return { sha, subject, coverage: "exempt-distribution" };
|
|
@@ -23872,21 +23888,21 @@ function checkHotfixCoverage(options = {}) {
|
|
|
23872
23888
|
}
|
|
23873
23889
|
function checkHotfixCarries(options) {
|
|
23874
23890
|
const { cwd = process.cwd(), branch, baseRef, targets } = options;
|
|
23875
|
-
const
|
|
23891
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process12.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
23876
23892
|
const isAncestor = (sha, ref) => {
|
|
23877
23893
|
try {
|
|
23878
|
-
|
|
23894
|
+
git3(["merge-base", "--is-ancestor", sha, ref]);
|
|
23879
23895
|
return true;
|
|
23880
23896
|
} catch {
|
|
23881
23897
|
return false;
|
|
23882
23898
|
}
|
|
23883
23899
|
};
|
|
23884
23900
|
const revList = (range) => {
|
|
23885
|
-
const out =
|
|
23901
|
+
const out = git3(["rev-list", "--no-merges", range]).trim();
|
|
23886
23902
|
return out ? out.split("\n") : [];
|
|
23887
23903
|
};
|
|
23888
23904
|
const cherrySources = (sha) => {
|
|
23889
|
-
const message =
|
|
23905
|
+
const message = git3(["log", "-1", "--format=%B", sha]);
|
|
23890
23906
|
return [...message.matchAll(CHERRY_TRAILER)].map((m) => m[1]);
|
|
23891
23907
|
};
|
|
23892
23908
|
const carried = /* @__PURE__ */ new Set();
|
|
@@ -25074,12 +25090,25 @@ function runSpawnPolicy(root) {
|
|
|
25074
25090
|
}
|
|
25075
25091
|
|
|
25076
25092
|
// src/test-policy-core.ts
|
|
25077
|
-
var
|
|
25093
|
+
var import_node_child_process15 = require("node:child_process");
|
|
25078
25094
|
var import_node_fs25 = require("node:fs");
|
|
25079
25095
|
var import_node_path22 = require("node:path");
|
|
25080
25096
|
|
|
25081
25097
|
// ../scripts/test-command-policy-core.mjs
|
|
25098
|
+
var import_node_child_process14 = require("node:child_process");
|
|
25082
25099
|
var TEST_COMMAND_CLASS = "test";
|
|
25100
|
+
var TRAILER_KEY = "Test-Policy-Override";
|
|
25101
|
+
var OVERRIDE_RE = /^Test-Policy-Override:\s*(.+)$/im;
|
|
25102
|
+
var REC = "";
|
|
25103
|
+
var FLD = "";
|
|
25104
|
+
var WAIVABLE_KINDS = [
|
|
25105
|
+
"mandatory-zone-untested",
|
|
25106
|
+
"unrequested-test-file",
|
|
25107
|
+
"protected-removed",
|
|
25108
|
+
"stale-protected-entry",
|
|
25109
|
+
"stale-satisfied-by"
|
|
25110
|
+
];
|
|
25111
|
+
var TEST_WORK_KIND = "unrequested-test-file";
|
|
25083
25112
|
function translateGlob(glob) {
|
|
25084
25113
|
let out = "";
|
|
25085
25114
|
for (let i = 0; i < glob.length; i += 1) {
|
|
@@ -25122,7 +25151,7 @@ function matchedMandatoryGlobs(paths, mandatory) {
|
|
|
25122
25151
|
return list.some((path2) => re.test(path2));
|
|
25123
25152
|
});
|
|
25124
25153
|
}
|
|
25125
|
-
function evaluateTestCommandPolicy({ paths, mandatory, regulated = true } = {}) {
|
|
25154
|
+
function evaluateTestCommandPolicy({ paths, mandatory, regulated = true, override = null } = {}) {
|
|
25126
25155
|
const configuredMandatoryCount = mandatoryGlobList(mandatory).length;
|
|
25127
25156
|
if (!regulated) {
|
|
25128
25157
|
return {
|
|
@@ -25135,7 +25164,8 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true } = {})
|
|
|
25135
25164
|
};
|
|
25136
25165
|
}
|
|
25137
25166
|
const matched = matchedMandatoryGlobs(paths, mandatory);
|
|
25138
|
-
const
|
|
25167
|
+
const overrideAuthorizes = Array.isArray(override?.kinds) && override.kinds.includes(TEST_WORK_KIND);
|
|
25168
|
+
const testCommandsAllowed = matched.length > 0 || overrideAuthorizes;
|
|
25139
25169
|
return {
|
|
25140
25170
|
configuredMandatoryCount,
|
|
25141
25171
|
matchedMandatoryGlobs: matched,
|
|
@@ -25148,22 +25178,78 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true } = {})
|
|
|
25148
25178
|
}
|
|
25149
25179
|
};
|
|
25150
25180
|
}
|
|
25181
|
+
function git(args, cwd) {
|
|
25182
|
+
return (0, import_node_child_process14.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
25183
|
+
}
|
|
25184
|
+
function parseScope(value) {
|
|
25185
|
+
const scoped = /^\[([^\]]*)\]\s*([\s\S]*)$/.exec(value);
|
|
25186
|
+
if (!scoped) return { kinds: [...WAIVABLE_KINDS], reason: value, unknown: [] };
|
|
25187
|
+
const named = scoped[1].split(",").map((k) => k.trim()).filter(Boolean);
|
|
25188
|
+
return {
|
|
25189
|
+
kinds: named,
|
|
25190
|
+
reason: scoped[2].trim(),
|
|
25191
|
+
unknown: named.filter((k) => !WAIVABLE_KINDS.includes(k))
|
|
25192
|
+
};
|
|
25193
|
+
}
|
|
25194
|
+
function invisibleTrailer(sha, line) {
|
|
25195
|
+
return {
|
|
25196
|
+
kind: "malformed-override-trailer",
|
|
25197
|
+
paths: [],
|
|
25198
|
+
detail: `OVERRIDE TRAILER GIT CANNOT SEE \u2014 commit ${sha.slice(0, 8)} carries
|
|
25199
|
+
${line}
|
|
25200
|
+
but \`git log --format='%(trailers:key=${TRAILER_KEY})'\` reports nothing for it, so the waiver would
|
|
25201
|
+
exist only to the regex that granted it. The trailer was chosen over a flag because it can be
|
|
25202
|
+
AUDITED, and a reason the auditor cannot see is not a reason this gate accepts (#3628).
|
|
25203
|
+
Git reads trailers from the LAST paragraph only, and every line of that paragraph must be a
|
|
25204
|
+
trailer or an indented continuation \u2014 one bare line such as \`Closes #123\` disqualifies the whole
|
|
25205
|
+
block. Indent the continuation lines, and leave nothing but trailers in that paragraph.
|
|
25206
|
+
This is fixable forward: push a LATER commit on this branch carrying a well-formed trailer.
|
|
25207
|
+
The nearest waiver wins, and it supersedes this one \u2014 no force-push, no re-filed branch.`
|
|
25208
|
+
};
|
|
25209
|
+
}
|
|
25210
|
+
function unknownScope(sha, unknown) {
|
|
25211
|
+
return {
|
|
25212
|
+
kind: "malformed-override-trailer",
|
|
25213
|
+
paths: [],
|
|
25214
|
+
detail: `UNKNOWN OVERRIDE SCOPE \u2014 commit ${sha.slice(0, 8)} scopes its waiver to ${unknown.join(", ")}, which this
|
|
25215
|
+
gate cannot report.
|
|
25216
|
+
Waivable kinds: ${WAIVABLE_KINDS.join(", ")}.
|
|
25217
|
+
Refused rather than widened: treating a typo as "waive everything" is how a scoped waiver turns
|
|
25218
|
+
into a blanket exemption without anyone deciding it should.`
|
|
25219
|
+
};
|
|
25220
|
+
}
|
|
25221
|
+
function isShallowRepository(cwd) {
|
|
25222
|
+
try {
|
|
25223
|
+
return git(["rev-parse", "--is-shallow-repository"], cwd).trim() !== "false";
|
|
25224
|
+
} catch {
|
|
25225
|
+
return true;
|
|
25226
|
+
}
|
|
25227
|
+
}
|
|
25228
|
+
function readOverride(base, cwd) {
|
|
25229
|
+
const format = `%H${FLD}%(trailers:key=${TRAILER_KEY},valueonly,unfold)${FLD}%B${REC}`;
|
|
25230
|
+
const refusals = [];
|
|
25231
|
+
let override = null;
|
|
25232
|
+
for (const record of git(["log", `${base}..HEAD`, `--format=${format}`], cwd).split(REC)) {
|
|
25233
|
+
const [sha, trailer, body] = record.replace(/^\s+/, "").split(FLD);
|
|
25234
|
+
if (!sha) continue;
|
|
25235
|
+
if (override) break;
|
|
25236
|
+
const value = (trailer ?? "").trim();
|
|
25237
|
+
if (!value) {
|
|
25238
|
+
const shaped = OVERRIDE_RE.exec(body ?? "");
|
|
25239
|
+
if (shaped) refusals.push(invisibleTrailer(sha, shaped[0].trim()));
|
|
25240
|
+
continue;
|
|
25241
|
+
}
|
|
25242
|
+
const { kinds, reason, unknown } = parseScope(value);
|
|
25243
|
+
if (unknown.length > 0) refusals.push(unknownScope(sha, unknown));
|
|
25244
|
+
else override = { sha, reason, kinds };
|
|
25245
|
+
}
|
|
25246
|
+
return { override, refusals };
|
|
25247
|
+
}
|
|
25151
25248
|
|
|
25152
25249
|
// src/test-policy-core.ts
|
|
25153
25250
|
var POLICY_FILE = "test-policy.json";
|
|
25154
25251
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
25155
25252
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
25156
|
-
var TRAILER_KEY = "Test-Policy-Override";
|
|
25157
|
-
var OVERRIDE_RE = /^Test-Policy-Override:\s*(.+)$/im;
|
|
25158
|
-
var REC = "";
|
|
25159
|
-
var FLD = "";
|
|
25160
|
-
var WAIVABLE_KINDS = [
|
|
25161
|
-
"mandatory-zone-untested",
|
|
25162
|
-
"unrequested-test-file",
|
|
25163
|
-
"protected-removed",
|
|
25164
|
-
"stale-protected-entry",
|
|
25165
|
-
"stale-satisfied-by"
|
|
25166
|
-
];
|
|
25167
25253
|
function translate(glob) {
|
|
25168
25254
|
let out = "";
|
|
25169
25255
|
for (let i = 0; i < glob.length; i++) {
|
|
@@ -25521,15 +25607,15 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
25521
25607
|
}
|
|
25522
25608
|
return findings;
|
|
25523
25609
|
}
|
|
25524
|
-
function
|
|
25525
|
-
return (0,
|
|
25610
|
+
function git2(args, cwd) {
|
|
25611
|
+
return (0, import_node_child_process15.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
25526
25612
|
}
|
|
25527
25613
|
var COAUTHOR_KEY = "Co-authored-by";
|
|
25528
25614
|
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
25529
25615
|
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
25530
25616
|
function parseTrailers(message, cwd) {
|
|
25531
25617
|
try {
|
|
25532
|
-
return (0,
|
|
25618
|
+
return (0, import_node_child_process15.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
25533
25619
|
windowsHide: true,
|
|
25534
25620
|
cwd,
|
|
25535
25621
|
input: message,
|
|
@@ -25566,19 +25652,12 @@ function resolveBase(cwd, explicit) {
|
|
|
25566
25652
|
const candidates = [explicit, process.env.TEST_POLICY_BASE, "origin/development", "origin/main"].filter(Boolean);
|
|
25567
25653
|
for (const ref of candidates) {
|
|
25568
25654
|
try {
|
|
25569
|
-
return
|
|
25655
|
+
return git2(["merge-base", "HEAD", ref], cwd).trim();
|
|
25570
25656
|
} catch {
|
|
25571
25657
|
}
|
|
25572
25658
|
}
|
|
25573
25659
|
return null;
|
|
25574
25660
|
}
|
|
25575
|
-
function isShallowRepository(cwd) {
|
|
25576
|
-
try {
|
|
25577
|
-
return git(["rev-parse", "--is-shallow-repository"], cwd).trim() !== "false";
|
|
25578
|
-
} catch {
|
|
25579
|
-
return true;
|
|
25580
|
-
}
|
|
25581
|
-
}
|
|
25582
25661
|
function untrustworthyRange(cwd, base) {
|
|
25583
25662
|
if (!base) {
|
|
25584
25663
|
return {
|
|
@@ -25600,65 +25679,11 @@ function untrustworthyRange(cwd, base) {
|
|
|
25600
25679
|
Fetch full history \u2014 actions/checkout with \`fetch-depth: 0\`, or \`git fetch --unshallow\`.`
|
|
25601
25680
|
};
|
|
25602
25681
|
}
|
|
25603
|
-
function
|
|
25604
|
-
|
|
25605
|
-
if (!scoped) return { kinds: [...WAIVABLE_KINDS], reason: value, unknown: [] };
|
|
25606
|
-
const named = scoped[1].split(",").map((k) => k.trim()).filter(Boolean);
|
|
25607
|
-
return {
|
|
25608
|
-
kinds: named,
|
|
25609
|
-
reason: scoped[2].trim(),
|
|
25610
|
-
unknown: named.filter((k) => !WAIVABLE_KINDS.includes(k))
|
|
25611
|
-
};
|
|
25612
|
-
}
|
|
25613
|
-
function invisibleTrailer(sha, line) {
|
|
25614
|
-
return {
|
|
25615
|
-
kind: "malformed-override-trailer",
|
|
25616
|
-
paths: [],
|
|
25617
|
-
detail: `OVERRIDE TRAILER GIT CANNOT SEE \u2014 commit ${sha.slice(0, 8)} carries
|
|
25618
|
-
${line}
|
|
25619
|
-
but \`git log --format='%(trailers:key=${TRAILER_KEY})'\` reports nothing for it, so the waiver would
|
|
25620
|
-
exist only to the regex that granted it. The trailer was chosen over a flag because it can be
|
|
25621
|
-
AUDITED, and a reason the auditor cannot see is not a reason this gate accepts (#3628).
|
|
25622
|
-
Git reads trailers from the LAST paragraph only, and every line of that paragraph must be a
|
|
25623
|
-
trailer or an indented continuation \u2014 one bare line such as \`Closes #123\` disqualifies the whole
|
|
25624
|
-
block. Indent the continuation lines, and leave nothing but trailers in that paragraph.
|
|
25625
|
-
This is fixable forward: push a LATER commit on this branch carrying a well-formed trailer.
|
|
25626
|
-
The nearest waiver wins, and it supersedes this one \u2014 no force-push, no re-filed branch.`
|
|
25627
|
-
};
|
|
25628
|
-
}
|
|
25629
|
-
function unknownScope(sha, unknown) {
|
|
25630
|
-
return {
|
|
25631
|
-
kind: "malformed-override-trailer",
|
|
25632
|
-
paths: [],
|
|
25633
|
-
detail: `UNKNOWN OVERRIDE SCOPE \u2014 commit ${sha.slice(0, 8)} scopes its waiver to ${unknown.join(", ")}, which this
|
|
25634
|
-
gate cannot report.
|
|
25635
|
-
Waivable kinds: ${WAIVABLE_KINDS.join(", ")}.
|
|
25636
|
-
Refused rather than widened: treating a typo as "waive everything" is how a scoped waiver turns
|
|
25637
|
-
into a blanket exemption without anyone deciding it should.`
|
|
25638
|
-
};
|
|
25639
|
-
}
|
|
25640
|
-
function readOverride(base, cwd) {
|
|
25641
|
-
const format = `%H${FLD}%(trailers:key=${TRAILER_KEY},valueonly,unfold)${FLD}%B${REC}`;
|
|
25642
|
-
const refusals = [];
|
|
25643
|
-
let override = null;
|
|
25644
|
-
for (const record of git(["log", `${base}..HEAD`, `--format=${format}`], cwd).split(REC)) {
|
|
25645
|
-
const [sha, trailer, body] = record.replace(/^\s+/, "").split(FLD);
|
|
25646
|
-
if (!sha) continue;
|
|
25647
|
-
if (override) break;
|
|
25648
|
-
const value = (trailer ?? "").trim();
|
|
25649
|
-
if (!value) {
|
|
25650
|
-
const shaped = OVERRIDE_RE.exec(body ?? "");
|
|
25651
|
-
if (shaped) refusals.push(invisibleTrailer(sha, shaped[0].trim()));
|
|
25652
|
-
continue;
|
|
25653
|
-
}
|
|
25654
|
-
const { kinds, reason, unknown } = parseScope(value);
|
|
25655
|
-
if (unknown.length > 0) refusals.push(unknownScope(sha, unknown));
|
|
25656
|
-
else override = { sha, reason, kinds };
|
|
25657
|
-
}
|
|
25658
|
-
return { override, refusals };
|
|
25682
|
+
function readOverride2(base, cwd) {
|
|
25683
|
+
return readOverride(base, cwd);
|
|
25659
25684
|
}
|
|
25660
25685
|
function changedFilesSince(base, cwd) {
|
|
25661
|
-
const raw =
|
|
25686
|
+
const raw = git2(["diff", "--name-status", "-M", base, "--"], cwd).trim();
|
|
25662
25687
|
const changed = raw ? raw.split("\n").map((line) => {
|
|
25663
25688
|
const parts = line.split(" ");
|
|
25664
25689
|
const norm = (p) => p.replace(/\\/g, "/");
|
|
@@ -25666,12 +25691,12 @@ function changedFilesSince(base, cwd) {
|
|
|
25666
25691
|
if (parts.length > 2) row.from = norm(parts[1]);
|
|
25667
25692
|
return row;
|
|
25668
25693
|
}) : [];
|
|
25669
|
-
const untracked =
|
|
25694
|
+
const untracked = git2(["ls-files", "--others", "--exclude-standard", "-z"], cwd).split("\0").filter(Boolean).map((path2) => ({ status: "A", path: path2.replace(/\\/g, "/") }));
|
|
25670
25695
|
return [...changed, ...untracked];
|
|
25671
25696
|
}
|
|
25672
25697
|
function blobAt(base, path2, cwd) {
|
|
25673
25698
|
try {
|
|
25674
|
-
return
|
|
25699
|
+
return git2(["show", `${base}:${path2}`], cwd);
|
|
25675
25700
|
} catch {
|
|
25676
25701
|
return null;
|
|
25677
25702
|
}
|
|
@@ -25687,7 +25712,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
25687
25712
|
before: blobAt(base, path2, root),
|
|
25688
25713
|
after: readFileOrNull2((0, import_node_path22.join)(root, path2))
|
|
25689
25714
|
}));
|
|
25690
|
-
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ?
|
|
25715
|
+
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride2(base, root) : { override: null, refusals: [] };
|
|
25691
25716
|
const present = (path2) => exists((0, import_node_path22.join)(root, path2));
|
|
25692
25717
|
const removedByThisDiff = removedPaths(changed);
|
|
25693
25718
|
const staleFindings = [];
|
|
@@ -25720,7 +25745,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
25720
25745
|
const commandPolicy = evaluateTestCommandPolicy({
|
|
25721
25746
|
paths: changed.map((f) => f.path),
|
|
25722
25747
|
mandatory: policy.mandatory,
|
|
25723
|
-
regulated: policy.declared !== false
|
|
25748
|
+
regulated: policy.declared !== false,
|
|
25749
|
+
override: override && lookup.refusals.length === 0 ? { kinds: override.kinds } : null
|
|
25724
25750
|
});
|
|
25725
25751
|
const result = {
|
|
25726
25752
|
ok: findings.length === 0,
|
|
@@ -25742,7 +25768,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
25742
25768
|
}
|
|
25743
25769
|
|
|
25744
25770
|
// src/dist-drift.ts
|
|
25745
|
-
var
|
|
25771
|
+
var import_node_child_process16 = require("node:child_process");
|
|
25746
25772
|
var import_node_crypto7 = require("node:crypto");
|
|
25747
25773
|
var import_node_fs27 = require("node:fs");
|
|
25748
25774
|
var import_node_os13 = require("node:os");
|
|
@@ -25887,7 +25913,7 @@ function bomPathFor(root) {
|
|
|
25887
25913
|
}
|
|
25888
25914
|
}
|
|
25889
25915
|
function rebuildTo(packageRoot, outDir) {
|
|
25890
|
-
(0,
|
|
25916
|
+
(0, import_node_child_process16.execFileSync)(process.execPath, ["build.mjs"], {
|
|
25891
25917
|
cwd: packageRoot,
|
|
25892
25918
|
env: { ...process.env, MMI_DIST_OUTDIR: outDir },
|
|
25893
25919
|
windowsHide: true,
|
|
@@ -27604,9 +27630,9 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
27604
27630
|
|
|
27605
27631
|
// src/schedules-commands.ts
|
|
27606
27632
|
var import_promises4 = require("node:fs/promises");
|
|
27607
|
-
var
|
|
27633
|
+
var import_node_child_process17 = require("node:child_process");
|
|
27608
27634
|
var import_node_util7 = require("node:util");
|
|
27609
|
-
var execFileP5 = (0, import_node_util7.promisify)(
|
|
27635
|
+
var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process17.execFile);
|
|
27610
27636
|
var AWS_REGION = "eu-central-1";
|
|
27611
27637
|
var AWS_TIMEOUT_MS = 3e4;
|
|
27612
27638
|
var AWS_RETRY_DELAY_MS = 1500;
|
|
@@ -30932,6 +30958,9 @@ function buildPrMergeArgs(input) {
|
|
|
30932
30958
|
if (input.auto) args.push("--auto");
|
|
30933
30959
|
return args;
|
|
30934
30960
|
}
|
|
30961
|
+
function buildPrDisableAutoArgs(input) {
|
|
30962
|
+
return ["pr", "merge", input.number, ...input.repoArgs, "--disable-auto"];
|
|
30963
|
+
}
|
|
30935
30964
|
function basePolicyBlocksImmediateMerge(message) {
|
|
30936
30965
|
return /base branch policy prohibits|protected branch|required status check|merge queue/i.test(message);
|
|
30937
30966
|
}
|
|
@@ -31428,19 +31457,19 @@ function formatWorktreeRemovalFailureDetail(options) {
|
|
|
31428
31457
|
const detail = options.removeError.trim() || "git worktree remove exited nonzero";
|
|
31429
31458
|
return `${detail}; registered=${options.registered ? "yes" : "no"}; pathExists=${options.pathExists ? "yes" : "no"}; path=${options.wtPath}`;
|
|
31430
31459
|
}
|
|
31431
|
-
async function resolveMergeBaseTimestampMs(
|
|
31460
|
+
async function resolveMergeBaseTimestampMs(git3, baseRef, branch) {
|
|
31432
31461
|
try {
|
|
31433
|
-
const mergeBase = (await
|
|
31462
|
+
const mergeBase = (await git3(["merge-base", baseRef, branch])).trim();
|
|
31434
31463
|
if (!mergeBase) return 0;
|
|
31435
|
-
const sec = parseCommitTimestampSec(await
|
|
31464
|
+
const sec = parseCommitTimestampSec(await git3(["show", "-s", "--format=%ct", mergeBase]));
|
|
31436
31465
|
return sec ? sec * 1e3 : 0;
|
|
31437
31466
|
} catch {
|
|
31438
31467
|
return 0;
|
|
31439
31468
|
}
|
|
31440
31469
|
}
|
|
31441
|
-
async function verifyBranchHead(
|
|
31470
|
+
async function verifyBranchHead(git3, branch, expectedHeadOid) {
|
|
31442
31471
|
if (!expectedHeadOid) return { ok: false, reason: "branch-head-unverified" };
|
|
31443
|
-
const currentHead = (await
|
|
31472
|
+
const currentHead = (await git3(["rev-parse", `refs/heads/${branch}`]).catch(() => "") || "").trim();
|
|
31444
31473
|
if (!currentHead) return { ok: false, reason: "branch-head-unverified" };
|
|
31445
31474
|
if (currentHead !== expectedHeadOid) {
|
|
31446
31475
|
return { ok: false, reason: "unpushed-branch", error: `${currentHead} != ${expectedHeadOid}` };
|
|
@@ -31455,17 +31484,17 @@ async function teardownWorktreeStage(worktreePath) {
|
|
|
31455
31484
|
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
31456
31485
|
}
|
|
31457
31486
|
}
|
|
31458
|
-
async function removeWorktreeWithReconcile(wtPath,
|
|
31487
|
+
async function removeWorktreeWithReconcile(wtPath, git3, listWorktrees, pathExists) {
|
|
31459
31488
|
const attemptRemove = async () => {
|
|
31460
31489
|
try {
|
|
31461
|
-
await
|
|
31490
|
+
await git3(["worktree", "remove", "--force", wtPath]);
|
|
31462
31491
|
return void 0;
|
|
31463
31492
|
} catch (e) {
|
|
31464
31493
|
return formatGitCommandError(e);
|
|
31465
31494
|
}
|
|
31466
31495
|
};
|
|
31467
31496
|
const pruneAndVerify = async (reason) => {
|
|
31468
|
-
await
|
|
31497
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
31469
31498
|
let worktrees2;
|
|
31470
31499
|
try {
|
|
31471
31500
|
worktrees2 = await listWorktrees();
|
|
@@ -31495,7 +31524,7 @@ async function removeWorktreeWithReconcile(wtPath, git2, listWorktrees, pathExis
|
|
|
31495
31524
|
};
|
|
31496
31525
|
}
|
|
31497
31526
|
if (!isWorktreePathRegistered(worktrees, wtPath)) return pruneAndVerify("reconciled-unregistered");
|
|
31498
|
-
await
|
|
31527
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
31499
31528
|
try {
|
|
31500
31529
|
worktrees = await listWorktrees();
|
|
31501
31530
|
} catch (e) {
|
|
@@ -31582,8 +31611,8 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
31582
31611
|
return report;
|
|
31583
31612
|
}
|
|
31584
31613
|
const safeCwd = selectSafeWorktreeCwd([...afterWorktrees, ...beforeWorktrees], wtPath, { pathExists });
|
|
31585
|
-
const
|
|
31586
|
-
const headCheck = await verifyBranchHead(
|
|
31614
|
+
const git3 = (args) => safeCwd ? execGit(["-C", safeCwd, ...args]) : execGit(args);
|
|
31615
|
+
const headCheck = await verifyBranchHead(git3, branch, options.expectedHeadOid);
|
|
31587
31616
|
if (!headCheck.ok) {
|
|
31588
31617
|
report.localBranch = {
|
|
31589
31618
|
name: branch,
|
|
@@ -31646,8 +31675,8 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
31646
31675
|
}
|
|
31647
31676
|
const removal = await removeWorktreeWithReconcile(
|
|
31648
31677
|
wtPath,
|
|
31649
|
-
|
|
31650
|
-
async () => parseGitWorktreePorcelain(await
|
|
31678
|
+
git3,
|
|
31679
|
+
async () => parseGitWorktreePorcelain(await git3(["worktree", "list", "--porcelain"])),
|
|
31651
31680
|
pathExists
|
|
31652
31681
|
);
|
|
31653
31682
|
if (removal.status === "failed") {
|
|
@@ -31680,10 +31709,10 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
31680
31709
|
}
|
|
31681
31710
|
}
|
|
31682
31711
|
try {
|
|
31683
|
-
await
|
|
31712
|
+
await git3(["branch", "-D", branch]);
|
|
31684
31713
|
report.localBranch = { name: branch, status: "deleted" };
|
|
31685
31714
|
} catch (e) {
|
|
31686
|
-
const remaining = await
|
|
31715
|
+
const remaining = await git3(["branch", "--list", branch]).catch(() => "");
|
|
31687
31716
|
report.localBranch = remaining.trim() ? { name: branch, status: "failed", error: e instanceof Error ? e.message : String(e) } : { name: branch, status: "already-gone" };
|
|
31688
31717
|
}
|
|
31689
31718
|
return report;
|
|
@@ -31717,6 +31746,17 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
31717
31746
|
return lines;
|
|
31718
31747
|
}
|
|
31719
31748
|
|
|
31749
|
+
// src/utf8-receipt.ts
|
|
31750
|
+
var import_node_fs37 = require("node:fs");
|
|
31751
|
+
var import_node_path35 = require("node:path");
|
|
31752
|
+
function writeUtf8Receipt(path2, text) {
|
|
31753
|
+
(0, import_node_fs37.mkdirSync)((0, import_node_path35.dirname)(path2), { recursive: true });
|
|
31754
|
+
const body = text.endsWith("\n") ? text : `${text}
|
|
31755
|
+
`;
|
|
31756
|
+
(0, import_node_fs37.writeFileSync)(path2, body, "utf8");
|
|
31757
|
+
return Buffer.byteLength(body, "utf8");
|
|
31758
|
+
}
|
|
31759
|
+
|
|
31720
31760
|
// src/board-commands.ts
|
|
31721
31761
|
function refuseRateLimited(e, json) {
|
|
31722
31762
|
if (!isRateLimitedError(e)) return false;
|
|
@@ -31744,7 +31784,9 @@ function registerBoardCommands(program3) {
|
|
|
31744
31784
|
includeAllBodies: o.bodies,
|
|
31745
31785
|
allowPartial: o.allowPartial
|
|
31746
31786
|
}, o.direct ? {} : { snapshot: registryClientDeps(config) });
|
|
31747
|
-
|
|
31787
|
+
const output = o.json ? JSON.stringify(report) : renderBoardReport(report);
|
|
31788
|
+
if (!o.out) console.log(output);
|
|
31789
|
+
else console.log(`Wrote board read to ${o.out} (UTF-8, ${writeUtf8Receipt(o.out, output)} bytes)`);
|
|
31748
31790
|
} catch (e) {
|
|
31749
31791
|
return failGraceful(`board read failed: ${withDiscoverMissDetail(e.message)}`);
|
|
31750
31792
|
}
|
|
@@ -31753,7 +31795,7 @@ function registerBoardCommands(program3) {
|
|
|
31753
31795
|
return alreadyClaimed ? `Check ${ref}: claimed and In Progress, no live contest - claim would renew the lease (nothing written)` : `Check ${ref}: free - claim would proceed (nothing written)`;
|
|
31754
31796
|
}
|
|
31755
31797
|
const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
|
|
31756
|
-
board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n").action((o) => runBoardRead(o));
|
|
31798
|
+
board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--out <path>", "write the output to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802)").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nNever capture the JSON with a shell redirect on Windows: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n").action((o) => runBoardRead(o));
|
|
31757
31799
|
withExamples(mutating(
|
|
31758
31800
|
board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").addHelpText("after", "\nevery claim stamps a lane-identity marker comment on the issue (`<!-- mmi-claim: \u2026 -->`,\nsurface/session@host) so other agents can attribute the hold (#3727). The session is the\nhost-exported id when the surface provides one, otherwise a per-process `synth-` fallback \u2014\na claim is never anonymous (#5245). `board show`, doctor and unclaim read the latest marker.\n").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
|
|
31759
31801
|
(_opts, args) => ({ command: "board claim", issues: args[0] ?? [] })
|
|
@@ -32416,7 +32458,7 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
32416
32458
|
}
|
|
32417
32459
|
|
|
32418
32460
|
// src/issue-commands.ts
|
|
32419
|
-
var
|
|
32461
|
+
var import_node_fs38 = require("node:fs");
|
|
32420
32462
|
var import_node_crypto11 = require("node:crypto");
|
|
32421
32463
|
|
|
32422
32464
|
// src/learning-closure-rate.ts
|
|
@@ -32603,7 +32645,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
32603
32645
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
32604
32646
|
const patch = {};
|
|
32605
32647
|
let bodyChanged = false;
|
|
32606
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
32648
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs38.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
32607
32649
|
if (options.titleFile !== void 0) {
|
|
32608
32650
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
32609
32651
|
} else if (options.title !== void 0) {
|
|
@@ -33220,7 +33262,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
33220
33262
|
if (opts.batch) {
|
|
33221
33263
|
let specs;
|
|
33222
33264
|
try {
|
|
33223
|
-
const raw = (0,
|
|
33265
|
+
const raw = (0, import_node_fs38.readFileSync)(opts.batch, "utf8");
|
|
33224
33266
|
specs = JSON.parse(raw);
|
|
33225
33267
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
33226
33268
|
} catch (e) {
|
|
@@ -33295,8 +33337,8 @@ ${lines}`, {
|
|
|
33295
33337
|
}
|
|
33296
33338
|
|
|
33297
33339
|
// src/train-commands.ts
|
|
33298
|
-
var
|
|
33299
|
-
var
|
|
33340
|
+
var import_node_fs39 = require("node:fs");
|
|
33341
|
+
var import_node_path36 = require("node:path");
|
|
33300
33342
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
33301
33343
|
function resolveReleaseBumpIntent(raw) {
|
|
33302
33344
|
const intent = typeof raw === "string" ? raw.trim() : "";
|
|
@@ -33307,7 +33349,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
33307
33349
|
}
|
|
33308
33350
|
function readRepoVersion() {
|
|
33309
33351
|
try {
|
|
33310
|
-
return JSON.parse((0,
|
|
33352
|
+
return JSON.parse((0, import_node_fs39.readFileSync)((0, import_node_path36.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
33311
33353
|
} catch {
|
|
33312
33354
|
return void 0;
|
|
33313
33355
|
}
|
|
@@ -33464,9 +33506,9 @@ function registerDeployCommands(program3) {
|
|
|
33464
33506
|
}
|
|
33465
33507
|
|
|
33466
33508
|
// src/discovery-commands.ts
|
|
33467
|
-
var
|
|
33509
|
+
var import_node_fs40 = require("node:fs");
|
|
33468
33510
|
var import_node_os17 = require("node:os");
|
|
33469
|
-
var
|
|
33511
|
+
var import_node_path37 = require("node:path");
|
|
33470
33512
|
var GC_GH_TIMEOUT_MS2 = 2e4;
|
|
33471
33513
|
async function collectStatus() {
|
|
33472
33514
|
const repo = await resolveRepo();
|
|
@@ -33656,8 +33698,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
33656
33698
|
}
|
|
33657
33699
|
const home = (0, import_node_os17.homedir)();
|
|
33658
33700
|
const plugin = onboardPluginGate({
|
|
33659
|
-
readKnown: () => readFileSyncSafe((0,
|
|
33660
|
-
readSettings: () => readFileSyncSafe((0,
|
|
33701
|
+
readKnown: () => readFileSyncSafe((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs40.readFileSync),
|
|
33702
|
+
readSettings: () => readFileSyncSafe((0, import_node_path37.join)(home, ".claude", "settings.json"), import_node_fs40.readFileSync)
|
|
33661
33703
|
});
|
|
33662
33704
|
return { track, board, registry: registry2, secrets, plugin, doors: opts.doors ?? [], nextCommand };
|
|
33663
33705
|
}
|
|
@@ -34420,8 +34462,8 @@ function registerPrLifecycleCommands(program3) {
|
|
|
34420
34462
|
}
|
|
34421
34463
|
|
|
34422
34464
|
// src/post-merge-recon.ts
|
|
34423
|
-
var
|
|
34424
|
-
var
|
|
34465
|
+
var import_node_fs41 = require("node:fs");
|
|
34466
|
+
var import_node_path38 = require("node:path");
|
|
34425
34467
|
|
|
34426
34468
|
// src/cross-repo-filing-issue.ts
|
|
34427
34469
|
function crossRepoFilingRetryCommand(prRepo, prNumber) {
|
|
@@ -34587,16 +34629,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
34587
34629
|
}
|
|
34588
34630
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
34589
34631
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
34590
|
-
(0,
|
|
34591
|
-
(0,
|
|
34632
|
+
(0, import_node_fs41.mkdirSync)((0, import_node_path38.dirname)(path2), { recursive: true });
|
|
34633
|
+
(0, import_node_fs41.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
34592
34634
|
`, "utf8");
|
|
34593
34635
|
return path2;
|
|
34594
34636
|
}
|
|
34595
34637
|
function clearPostMergeReconRecovery(cwd, repo, pr2) {
|
|
34596
34638
|
const path2 = postMergeReconStatePath(cwd, repo, pr2);
|
|
34597
|
-
if (!(0,
|
|
34639
|
+
if (!(0, import_node_fs41.existsSync)(path2)) return;
|
|
34598
34640
|
try {
|
|
34599
|
-
(0,
|
|
34641
|
+
(0, import_node_fs41.unlinkSync)(path2);
|
|
34600
34642
|
} catch {
|
|
34601
34643
|
}
|
|
34602
34644
|
}
|
|
@@ -37713,10 +37755,10 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
37713
37755
|
}
|
|
37714
37756
|
|
|
37715
37757
|
// src/doctor-io.ts
|
|
37716
|
-
var
|
|
37758
|
+
var import_node_fs42 = require("node:fs");
|
|
37717
37759
|
var import_node_os18 = require("node:os");
|
|
37718
|
-
var
|
|
37719
|
-
var
|
|
37760
|
+
var import_node_path39 = require("node:path");
|
|
37761
|
+
var import_node_child_process18 = require("node:child_process");
|
|
37720
37762
|
var import_node_util8 = require("node:util");
|
|
37721
37763
|
|
|
37722
37764
|
// src/discard-sink.ts
|
|
@@ -37725,27 +37767,27 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
|
|
|
37725
37767
|
}
|
|
37726
37768
|
|
|
37727
37769
|
// src/doctor-io.ts
|
|
37728
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
37770
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process18.execFile);
|
|
37729
37771
|
function execFileCapture(file, args, opts = {}) {
|
|
37730
37772
|
const sink = nodeDiscardSinkPath();
|
|
37731
|
-
const inFd = (0,
|
|
37732
|
-
const errFd = (0,
|
|
37773
|
+
const inFd = (0, import_node_fs42.openSync)(sink, "r");
|
|
37774
|
+
const errFd = (0, import_node_fs42.openSync)(sink, "w");
|
|
37733
37775
|
try {
|
|
37734
|
-
return (0,
|
|
37776
|
+
return (0, import_node_child_process18.execFileSync)(file, args, {
|
|
37735
37777
|
...opts,
|
|
37736
37778
|
encoding: "utf8",
|
|
37737
37779
|
stdio: [inFd, "pipe", errFd]
|
|
37738
37780
|
});
|
|
37739
37781
|
} finally {
|
|
37740
|
-
(0,
|
|
37741
|
-
(0,
|
|
37782
|
+
(0, import_node_fs42.closeSync)(inFd);
|
|
37783
|
+
(0, import_node_fs42.closeSync)(errFd);
|
|
37742
37784
|
}
|
|
37743
37785
|
}
|
|
37744
37786
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
37745
37787
|
function installedClaudePluginVersion() {
|
|
37746
37788
|
try {
|
|
37747
37789
|
const file = JSON.parse(
|
|
37748
|
-
(0,
|
|
37790
|
+
(0, import_node_fs42.readFileSync)((0, import_node_path39.join)((0, import_node_os18.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
37749
37791
|
);
|
|
37750
37792
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
37751
37793
|
if (versions.length === 0) return void 0;
|
|
@@ -37756,7 +37798,7 @@ function installedClaudePluginVersion() {
|
|
|
37756
37798
|
}
|
|
37757
37799
|
function manifestVersion(path2) {
|
|
37758
37800
|
try {
|
|
37759
|
-
const manifest = JSON.parse((0,
|
|
37801
|
+
const manifest = JSON.parse((0, import_node_fs42.readFileSync)(path2, "utf8"));
|
|
37760
37802
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
37761
37803
|
} catch {
|
|
37762
37804
|
return void 0;
|
|
@@ -37765,12 +37807,12 @@ function manifestVersion(path2) {
|
|
|
37765
37807
|
function readHermesPluginEvidence(env = process.env) {
|
|
37766
37808
|
const host = hermesConfigRoot(env);
|
|
37767
37809
|
const root = hermesPluginRoot(env);
|
|
37768
|
-
const installRecordPresent = (0,
|
|
37769
|
-
const manifestPath = (0,
|
|
37810
|
+
const installRecordPresent = (0, import_node_fs42.existsSync)(root);
|
|
37811
|
+
const manifestPath = (0, import_node_path39.join)(root, "plugin.yaml");
|
|
37770
37812
|
let installedVersion;
|
|
37771
37813
|
let manifest = "missing";
|
|
37772
37814
|
try {
|
|
37773
|
-
const text = (0,
|
|
37815
|
+
const text = (0, import_node_fs42.readFileSync)(manifestPath, "utf8");
|
|
37774
37816
|
let version;
|
|
37775
37817
|
try {
|
|
37776
37818
|
const parsed = JSON.parse(text).version;
|
|
@@ -37784,20 +37826,20 @@ function readHermesPluginEvidence(env = process.env) {
|
|
|
37784
37826
|
if (version) {
|
|
37785
37827
|
installedVersion = version;
|
|
37786
37828
|
manifest = "valid";
|
|
37787
|
-
} else if ((0,
|
|
37829
|
+
} else if ((0, import_node_fs42.existsSync)(manifestPath)) manifest = "invalid";
|
|
37788
37830
|
} catch {
|
|
37789
|
-
if ((0,
|
|
37831
|
+
if ((0, import_node_fs42.existsSync)(manifestPath)) manifest = "invalid";
|
|
37790
37832
|
}
|
|
37791
37833
|
let skills = false;
|
|
37792
37834
|
try {
|
|
37793
|
-
skills = (0,
|
|
37835
|
+
skills = (0, import_node_fs42.existsSync)((0, import_node_path39.join)(root, "skills")) && (0, import_node_fs42.statSync)((0, import_node_path39.join)(root, "skills")).isDirectory();
|
|
37794
37836
|
} catch {
|
|
37795
37837
|
}
|
|
37796
37838
|
return {
|
|
37797
|
-
hostPresent: (0,
|
|
37839
|
+
hostPresent: (0, import_node_fs42.existsSync)(host),
|
|
37798
37840
|
installRecordPresent,
|
|
37799
37841
|
manifest,
|
|
37800
|
-
payloadPresent: (0,
|
|
37842
|
+
payloadPresent: (0, import_node_fs42.existsSync)((0, import_node_path39.join)(root, "__init__.py")) && skills && manifest === "valid",
|
|
37801
37843
|
...installedVersion ? { installedVersion } : {}
|
|
37802
37844
|
};
|
|
37803
37845
|
}
|
|
@@ -37805,7 +37847,7 @@ function installedSurfacePluginVersion(surface) {
|
|
|
37805
37847
|
const token = surfaceToken(surface);
|
|
37806
37848
|
if (token === "kilo") {
|
|
37807
37849
|
try {
|
|
37808
|
-
const stamp = (0,
|
|
37850
|
+
const stamp = (0, import_node_fs42.readFileSync)((0, import_node_path39.join)((0, import_node_os18.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
37809
37851
|
return stamp || void 0;
|
|
37810
37852
|
} catch {
|
|
37811
37853
|
return void 0;
|
|
@@ -37813,13 +37855,13 @@ function installedSurfacePluginVersion(surface) {
|
|
|
37813
37855
|
}
|
|
37814
37856
|
if (token === "hermes") return readHermesPluginEvidence().installedVersion;
|
|
37815
37857
|
if (token === "cursor") {
|
|
37816
|
-
return manifestVersion((0,
|
|
37858
|
+
return manifestVersion((0, import_node_path39.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
37817
37859
|
}
|
|
37818
37860
|
if (token === "jervcode") {
|
|
37819
37861
|
return installedJervCodePackageVersion();
|
|
37820
37862
|
}
|
|
37821
37863
|
if (token === "kimi") {
|
|
37822
|
-
return manifestVersion((0,
|
|
37864
|
+
return manifestVersion((0, import_node_path39.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
37823
37865
|
}
|
|
37824
37866
|
if (token === "claude") return installedClaudePluginVersion();
|
|
37825
37867
|
if (token !== "codex") return void 0;
|
|
@@ -37853,13 +37895,13 @@ function worktreeRootSync() {
|
|
|
37853
37895
|
}
|
|
37854
37896
|
var gitignorePath = () => {
|
|
37855
37897
|
const root = worktreeRootSync();
|
|
37856
|
-
return root === null ? null : (0,
|
|
37898
|
+
return root === null ? null : (0, import_node_path39.join)(root, ".gitignore");
|
|
37857
37899
|
};
|
|
37858
37900
|
function readGitignore() {
|
|
37859
37901
|
const path2 = gitignorePath();
|
|
37860
37902
|
if (path2 === null) return null;
|
|
37861
37903
|
try {
|
|
37862
|
-
return (0,
|
|
37904
|
+
return (0, import_node_fs42.readFileSync)(path2, "utf8");
|
|
37863
37905
|
} catch {
|
|
37864
37906
|
return null;
|
|
37865
37907
|
}
|
|
@@ -37868,14 +37910,14 @@ function writeGitignore(content) {
|
|
|
37868
37910
|
const path2 = gitignorePath();
|
|
37869
37911
|
if (path2 === null) return false;
|
|
37870
37912
|
try {
|
|
37871
|
-
(0,
|
|
37913
|
+
(0, import_node_fs42.writeFileSync)(path2, content, "utf8");
|
|
37872
37914
|
return true;
|
|
37873
37915
|
} catch {
|
|
37874
37916
|
return false;
|
|
37875
37917
|
}
|
|
37876
37918
|
}
|
|
37877
37919
|
function lineEndingState(root) {
|
|
37878
|
-
const attributesPresent = (0,
|
|
37920
|
+
const attributesPresent = (0, import_node_fs42.existsSync)((0, import_node_path39.join)(root, ".gitattributes"));
|
|
37879
37921
|
try {
|
|
37880
37922
|
const output = execFileCapture("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
|
|
37881
37923
|
windowsHide: true
|
|
@@ -37942,8 +37984,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
37942
37984
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
37943
37985
|
try {
|
|
37944
37986
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
37945
|
-
if (!hostsPath || !(0,
|
|
37946
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
37987
|
+
if (!hostsPath || !(0, import_node_fs43.existsSync)(hostsPath)) return void 0;
|
|
37988
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs43.readFileSync)(hostsPath, "utf8")));
|
|
37947
37989
|
} catch {
|
|
37948
37990
|
return void 0;
|
|
37949
37991
|
}
|
|
@@ -37951,7 +37993,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
37951
37993
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
37952
37994
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
37953
37995
|
function envHealLockPath(home) {
|
|
37954
|
-
return (0,
|
|
37996
|
+
return (0, import_node_path40.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
37955
37997
|
}
|
|
37956
37998
|
async function withEnvHealLock(what, run) {
|
|
37957
37999
|
try {
|
|
@@ -38088,7 +38130,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38088
38130
|
);
|
|
38089
38131
|
const result = applyPluginCachePlan(
|
|
38090
38132
|
plan,
|
|
38091
|
-
(p) => (0,
|
|
38133
|
+
(p) => (0, import_node_fs43.rmSync)(p, { recursive: true }),
|
|
38092
38134
|
stagingApplyFsGuard(configRoot)
|
|
38093
38135
|
);
|
|
38094
38136
|
return {
|
|
@@ -38126,7 +38168,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38126
38168
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
38127
38169
|
// get a permanent — demanding an artifact it never asked for.
|
|
38128
38170
|
docsIndexState: (root) => {
|
|
38129
|
-
if (!(0,
|
|
38171
|
+
if (!(0, import_node_fs43.existsSync)((0, import_node_path40.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
38130
38172
|
const real = createDocsIndexDeps(root);
|
|
38131
38173
|
let docs2;
|
|
38132
38174
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -38135,7 +38177,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38135
38177
|
},
|
|
38136
38178
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
38137
38179
|
healDocsIndex: (root) => {
|
|
38138
|
-
if (!(0,
|
|
38180
|
+
if (!(0, import_node_fs43.existsSync)((0, import_node_path40.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
38139
38181
|
const real = createDocsIndexDeps(root);
|
|
38140
38182
|
let docs2;
|
|
38141
38183
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -38456,19 +38498,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
38456
38498
|
});
|
|
38457
38499
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
38458
38500
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
38459
|
-
const path2 = (0,
|
|
38460
|
-
const current = (0,
|
|
38501
|
+
const path2 = (0, import_node_path40.join)(process.cwd(), ".gitignore");
|
|
38502
|
+
const current = (0, import_node_fs43.existsSync)(path2) ? (0, import_node_fs43.readFileSync)(path2, "utf8") : null;
|
|
38461
38503
|
const plan = planManagedGitignore(current);
|
|
38462
38504
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
38463
38505
|
if (opts.json) {
|
|
38464
|
-
if (opts.write && plan.changed) (0,
|
|
38506
|
+
if (opts.write && plan.changed) (0, import_node_fs43.writeFileSync)(path2, plan.content, "utf8");
|
|
38465
38507
|
console.log(JSON.stringify(plan, null, 2));
|
|
38466
38508
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
38467
38509
|
return;
|
|
38468
38510
|
}
|
|
38469
38511
|
if (opts.write) {
|
|
38470
38512
|
if (plan.changed) {
|
|
38471
|
-
(0,
|
|
38513
|
+
(0, import_node_fs43.writeFileSync)(path2, plan.content, "utf8");
|
|
38472
38514
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
38473
38515
|
} else {
|
|
38474
38516
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -38654,7 +38696,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
38654
38696
|
try {
|
|
38655
38697
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
38656
38698
|
if (o.repo) args.push("--repo", o.repo);
|
|
38657
|
-
spawnDetachedSelf(args, { spawn:
|
|
38699
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process19.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
38658
38700
|
} catch {
|
|
38659
38701
|
}
|
|
38660
38702
|
}
|
|
@@ -39044,7 +39086,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
39044
39086
|
if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
|
|
39045
39087
|
if (o.secretsFile) {
|
|
39046
39088
|
try {
|
|
39047
|
-
vars.push(`secrets=${(0,
|
|
39089
|
+
vars.push(`secrets=${(0, import_node_fs43.readFileSync)(o.secretsFile, "utf8")}`);
|
|
39048
39090
|
} catch (e) {
|
|
39049
39091
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
39050
39092
|
}
|
|
@@ -39869,11 +39911,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
39869
39911
|
}
|
|
39870
39912
|
});
|
|
39871
39913
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
39872
|
-
const wfDir = (0,
|
|
39873
|
-
if (!(0,
|
|
39874
|
-
return (0,
|
|
39914
|
+
const wfDir = (0, import_node_path40.join)(cwd, ".github", "workflows");
|
|
39915
|
+
if (!(0, import_node_fs43.existsSync)(wfDir)) return [];
|
|
39916
|
+
return (0, import_node_fs43.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
39875
39917
|
try {
|
|
39876
|
-
return workflowReportsPrChecks((0,
|
|
39918
|
+
return workflowReportsPrChecks((0, import_node_fs43.readFileSync)((0, import_node_path40.join)(wfDir, name), "utf8"));
|
|
39877
39919
|
} catch {
|
|
39878
39920
|
return true;
|
|
39879
39921
|
}
|
|
@@ -39939,16 +39981,16 @@ function ciAuditDeps() {
|
|
|
39939
39981
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
39940
39982
|
readSeedFile: (path2) => {
|
|
39941
39983
|
if (!root) return null;
|
|
39942
|
-
const fullPath = (0,
|
|
39943
|
-
return (0,
|
|
39984
|
+
const fullPath = (0, import_node_path40.join)(root, path2);
|
|
39985
|
+
return (0, import_node_fs43.existsSync)(fullPath) ? (0, import_node_fs43.readFileSync)(fullPath, "utf8") : null;
|
|
39944
39986
|
}
|
|
39945
39987
|
};
|
|
39946
39988
|
}
|
|
39947
39989
|
function hubRoot() {
|
|
39948
|
-
const fromPkg = (0,
|
|
39990
|
+
const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
|
|
39949
39991
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
39950
|
-
if ((0,
|
|
39951
|
-
if ((0,
|
|
39992
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(fromPkg, marker))) return fromPkg;
|
|
39993
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(process.cwd(), marker))) return process.cwd();
|
|
39952
39994
|
return null;
|
|
39953
39995
|
}
|
|
39954
39996
|
async function waitLoopCorePool(label) {
|
|
@@ -40220,9 +40262,18 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
40220
40262
|
else printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
|
|
40221
40263
|
if (result.status === "failed") process.exitCode = 1;
|
|
40222
40264
|
});
|
|
40223
|
-
jsonParity(pr.command("merge <number>").description("merge a PR (squash by default); archives gitignored tmp/** before worktree teardown; on no-ci repos run pr ci-policy / checks-wait first (#1432, #5679)").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888)").option("--gc", "acknowledge deleting unarchived gitignored tmp/** evidence newer than the branch base (#5679)").option("--squash-body-file <path>", "squash commit body (overrides GitHub COMMIT_MESSAGES); use when a pushed commit mentions close/fix/resolve + #N that must not close (#5723)").option("--force", "acknowledge and merge past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal")).action(async (number, o) => {
|
|
40265
|
+
jsonParity(pr.command("merge <number>").description("merge a PR (squash by default); archives gitignored tmp/** before worktree teardown; on no-ci repos run pr ci-policy / checks-wait first (#1432, #5679)").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").addOption(new Option("--disable-auto", "disable a queued auto-merge without merging").conflicts(["auto", "wait", "squash", "merge", "rebase", "preserveWorktree", "gc", "squashBodyFile", "force"])).option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888)").option("--gc", "acknowledge deleting unarchived gitignored tmp/** evidence newer than the branch base (#5679)").option("--squash-body-file <path>", "squash commit body (overrides GitHub COMMIT_MESSAGES); use when a pushed commit mentions close/fix/resolve + #N that must not close (#5723)").option("--force", "acknowledge and merge past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal")).action(async (number, o) => {
|
|
40224
40266
|
const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
|
|
40225
40267
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
40268
|
+
if (o.disableAuto) {
|
|
40269
|
+
await execFileP2("gh", buildPrDisableAutoArgs({ number, repoArgs }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e) => {
|
|
40270
|
+
const note = timeoutKillNote(e, GH_MUTATION_TIMEOUT_MS);
|
|
40271
|
+
if (note) throw new Error(`gh pr merge ${number} --disable-auto: ${note}`);
|
|
40272
|
+
throw e;
|
|
40273
|
+
});
|
|
40274
|
+
console.log(JSON.stringify({ mergeStatus: "auto-merge-disabled", pr: number, ...o.repo ? { repo: o.repo } : {} }));
|
|
40275
|
+
return;
|
|
40276
|
+
}
|
|
40226
40277
|
const repoForPostCleanup = await resolveRepo(o.repo) ?? o.repo;
|
|
40227
40278
|
const prMeta = await execFileP2("gh", ["pr", "view", number, ...repoArgs, "--json", "headRefName,baseRefName,headRefOid,state", "--jq", "{head: .headRefName, base: .baseRefName, oid: .headRefOid, state: .state}"], { timeout: GC_GH_TIMEOUT_MS }).then((r) => JSON.parse(r.stdout)).catch(async (e) => {
|
|
40228
40279
|
if (!isGitHubRateLimitError(e) || !repoForPostCleanup) throw e;
|
|
@@ -40249,7 +40300,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40249
40300
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
40250
40301
|
closingGuardInput,
|
|
40251
40302
|
method === "--squash",
|
|
40252
|
-
o.squashBodyFile ? (0,
|
|
40303
|
+
o.squashBodyFile ? (0, import_node_fs43.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
40253
40304
|
);
|
|
40254
40305
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
40255
40306
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -40263,7 +40314,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40263
40314
|
return;
|
|
40264
40315
|
}
|
|
40265
40316
|
if (closingGuardVerdict.message) console.warn(closingGuardVerdict.message);
|
|
40266
|
-
if (prMeta.state !== "MERGED") {
|
|
40317
|
+
if (prMeta.state !== "MERGED" && !o.preserveWorktree) {
|
|
40267
40318
|
if (!repoForPostCleanup) throw new Error("pr merge: repository is unreadable; cannot prove remote branch preservation");
|
|
40268
40319
|
const repoSettings = await defaultGitHubClient().rest("GET", `repos/${repoForPostCleanup}`);
|
|
40269
40320
|
assertRemoteBranchPreservedOnMerge(repoForPostCleanup, repoSettings.delete_branch_on_merge);
|
|
@@ -40338,7 +40389,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40338
40389
|
}
|
|
40339
40390
|
if (!repoForPostCleanup) throw e;
|
|
40340
40391
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
40341
|
-
const commitMessage = bodyFile ? (0,
|
|
40392
|
+
const commitMessage = bodyFile ? (0, import_node_fs43.readFileSync)(bodyFile, "utf8") : void 0;
|
|
40342
40393
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
40343
40394
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
40344
40395
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -40427,7 +40478,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40427
40478
|
preserveWorktree: o.preserveWorktree,
|
|
40428
40479
|
gcAcknowledged: o.gc,
|
|
40429
40480
|
expectedHeadOid: headRefOid,
|
|
40430
|
-
pathExists: (p) => (0,
|
|
40481
|
+
pathExists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
40431
40482
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout
|
|
40432
40483
|
});
|
|
40433
40484
|
} catch (e) {
|
|
@@ -40715,7 +40766,7 @@ for (const commandName of ["rcand", "release"]) {
|
|
|
40715
40766
|
{ flags: "--ack <shas>", description: "comma-separated dev shas a human verified are in the candidate, overriding the hotfix-coverage guard for a conflicted port whose -x trailer was lost (#958)" },
|
|
40716
40767
|
{ flags: "--dev", description: "full-track repos release development -> main directly, skipping rc (refuses if rc carries content not in development; no-op on direct-track repos) (#1062)" },
|
|
40717
40768
|
{ flags: "--abort", description: "with --apply, delete only a proven unpublished failed release candidate on any registered deploy model and restore local main for a same-version recut (#3944/#5650/#5657)" },
|
|
40718
|
-
{ flags: "--retry-publish <run-id>", description: "with --apply, retry
|
|
40769
|
+
{ flags: "--retry-publish <run-id>", description: "with --apply, retry one proven failed or cancelled Hub publish release run (#3949/#5797)" }
|
|
40719
40770
|
];
|
|
40720
40771
|
for (const f of RELEASE_ONLY_FLAGS) {
|
|
40721
40772
|
if (commandName === "release") {
|
|
@@ -41014,8 +41065,7 @@ ${r.repo}: applied=[${r.applied.join("; ")}] skipped=[${r.skipped.join("; ")}]${
|
|
|
41014
41065
|
console.log("\nDry-run \u2014 re-run with --apply to patch merge settings and activate product rulesets (master-admin).");
|
|
41015
41066
|
}
|
|
41016
41067
|
}
|
|
41017
|
-
|
|
41018
|
-
if (!audit.ok || applyFailed) process.exitCode = 1;
|
|
41068
|
+
if (ciReconcileExitFailed({ apply: o.apply === true, audit, applyResults })) process.exitCode = 1;
|
|
41019
41069
|
});
|
|
41020
41070
|
registerBootstrapCommands(program2);
|
|
41021
41071
|
var access = program2.command("access").description("org access audit (read-only)");
|
|
@@ -41064,12 +41114,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
41064
41114
|
targets = resolution.targets;
|
|
41065
41115
|
}
|
|
41066
41116
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
41067
|
-
const fileMatrix = (0,
|
|
41117
|
+
const fileMatrix = (0, import_node_fs43.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs43.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
41068
41118
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
41069
41119
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
41070
|
-
const fileContracts = (0,
|
|
41120
|
+
const fileContracts = (0, import_node_fs43.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs43.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
41071
41121
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
41072
|
-
const sanctioned = (0,
|
|
41122
|
+
const sanctioned = (0, import_node_fs43.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs43.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
41073
41123
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
41074
41124
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
41075
41125
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -41101,16 +41151,16 @@ function directoryBytes(path2) {
|
|
|
41101
41151
|
let total = 0;
|
|
41102
41152
|
let entries;
|
|
41103
41153
|
try {
|
|
41104
|
-
entries = (0,
|
|
41154
|
+
entries = (0, import_node_fs43.readdirSync)(path2, { withFileTypes: true });
|
|
41105
41155
|
} catch {
|
|
41106
41156
|
return 0;
|
|
41107
41157
|
}
|
|
41108
41158
|
for (const entry of entries) {
|
|
41109
|
-
const child2 = (0,
|
|
41159
|
+
const child2 = (0, import_node_path40.join)(path2, entry.name);
|
|
41110
41160
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
41111
41161
|
else {
|
|
41112
41162
|
try {
|
|
41113
|
-
total += (0,
|
|
41163
|
+
total += (0, import_node_fs43.statSync)(child2).size;
|
|
41114
41164
|
} catch {
|
|
41115
41165
|
}
|
|
41116
41166
|
}
|
|
@@ -41118,25 +41168,25 @@ function directoryBytes(path2) {
|
|
|
41118
41168
|
return total;
|
|
41119
41169
|
}
|
|
41120
41170
|
function listDirEntries(dir) {
|
|
41121
|
-
return (0,
|
|
41171
|
+
return (0, import_node_fs43.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
41122
41172
|
}
|
|
41123
41173
|
function readInstalledPluginRefs(configRoot) {
|
|
41124
41174
|
const p = installedPluginsPathForConfig(configRoot);
|
|
41125
|
-
if (!(0,
|
|
41175
|
+
if (!(0, import_node_fs43.existsSync)(p)) return [];
|
|
41126
41176
|
try {
|
|
41127
|
-
return installedPluginPaths((0,
|
|
41177
|
+
return installedPluginPaths((0, import_node_fs43.readFileSync)(p, "utf8"));
|
|
41128
41178
|
} catch {
|
|
41129
41179
|
return null;
|
|
41130
41180
|
}
|
|
41131
41181
|
}
|
|
41132
41182
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
41133
41183
|
return {
|
|
41134
|
-
exists: (p) => (0,
|
|
41135
|
-
listVersionDirs: (root) => (0,
|
|
41184
|
+
exists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
41185
|
+
listVersionDirs: (root) => (0, import_node_fs43.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
41136
41186
|
dirBytes,
|
|
41137
|
-
listStagingDirs: (root) => (0,
|
|
41187
|
+
listStagingDirs: (root) => (0, import_node_fs43.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
41138
41188
|
try {
|
|
41139
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
41189
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path40.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs43.statSync)(p).mtimeMs) };
|
|
41140
41190
|
} catch {
|
|
41141
41191
|
return { name: d.name, mtimeMs: Date.now() };
|
|
41142
41192
|
}
|
|
@@ -41150,10 +41200,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
41150
41200
|
return {
|
|
41151
41201
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
41152
41202
|
mtimeMs: (name) => {
|
|
41153
|
-
const p = (0,
|
|
41154
|
-
if (!(0,
|
|
41203
|
+
const p = (0, import_node_path40.join)(stagingRoot, name);
|
|
41204
|
+
if (!(0, import_node_fs43.existsSync)(p)) return null;
|
|
41155
41205
|
try {
|
|
41156
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
41206
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs43.statSync)(q).mtimeMs);
|
|
41157
41207
|
} catch {
|
|
41158
41208
|
return null;
|
|
41159
41209
|
}
|
|
@@ -41179,7 +41229,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
41179
41229
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
41180
41230
|
);
|
|
41181
41231
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
41182
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
41232
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs43.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
41183
41233
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
41184
41234
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
41185
41235
|
else console.log(renderPluginCachePlan(plan, result));
|
package/package.json
CHANGED