@mutmutco/cli 4.2.0 → 4.2.2
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 +345 -240
- 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");
|
|
@@ -4522,10 +4522,29 @@ function targetFlagValueFromArgv(flag, argv) {
|
|
|
4522
4522
|
if (!next || next.startsWith("-")) return void 0;
|
|
4523
4523
|
return next;
|
|
4524
4524
|
}
|
|
4525
|
+
function survivingFlagArgs(argv, rejectedFlag, ownFlags) {
|
|
4526
|
+
const out = [];
|
|
4527
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
4528
|
+
const token = argv[i];
|
|
4529
|
+
if (!token.startsWith("--")) continue;
|
|
4530
|
+
const [name, inlineValue] = token.includes("=") ? [token.slice(0, token.indexOf("=")), token.slice(token.indexOf("=") + 1)] : [token, void 0];
|
|
4531
|
+
const values = [];
|
|
4532
|
+
if (inlineValue === void 0) {
|
|
4533
|
+
while (argv[i + 1] !== void 0 && !argv[i + 1].startsWith("-")) {
|
|
4534
|
+
values.push(argv[i + 1]);
|
|
4535
|
+
i += 1;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
if (name === rejectedFlag || !ownFlags.includes(name)) continue;
|
|
4539
|
+
out.push(inlineValue !== void 0 ? token : name, ...values);
|
|
4540
|
+
}
|
|
4541
|
+
return out;
|
|
4542
|
+
}
|
|
4525
4543
|
function formatPositionalTarget(commandPath3, argName, opts = {}) {
|
|
4526
4544
|
const concrete = opts.flag && opts.argv ? targetFlagValueFromArgv(opts.flag, opts.argv) : void 0;
|
|
4527
|
-
|
|
4528
|
-
|
|
4545
|
+
const target = concrete ?? `<${argName}>`;
|
|
4546
|
+
const kept = opts.flag && opts.argv && opts.ownFlags ? survivingFlagArgs(opts.argv, opts.flag, opts.ownFlags) : [];
|
|
4547
|
+
return [`mmi-cli ${commandPath3}`, target, ...kept].join(" ");
|
|
4529
4548
|
}
|
|
4530
4549
|
var FLAG_SYNONYMS = /* @__PURE__ */ new Map([["--outcome", "--reason"]]);
|
|
4531
4550
|
function synonymFlagFor(flag, ownFlags) {
|
|
@@ -4545,6 +4564,27 @@ var GUIDES = /* @__PURE__ */ new Map([
|
|
|
4545
4564
|
expected: ["learning report", "oracle issue create", "oracle issue list"],
|
|
4546
4565
|
did_you_mean: "learning report"
|
|
4547
4566
|
}
|
|
4567
|
+
],
|
|
4568
|
+
// #5821: `list` is the verb `oracle issue list` and `devops pr list` teach, so callers reach for
|
|
4569
|
+
// `oracle board list` next. Distance ranks the OTHER houses' `list` leaves above the board's own
|
|
4570
|
+
// reader, so the receipt pointed at `devops pr list` — a different house, for a board query.
|
|
4571
|
+
[
|
|
4572
|
+
"oracle board:list",
|
|
4573
|
+
{
|
|
4574
|
+
hint: "no `oracle board list` \u2014 the bounded board query is `oracle board read` (add `--json` for the machine-readable partition)",
|
|
4575
|
+
expected: ["oracle board read"],
|
|
4576
|
+
did_you_mean: "oracle board read"
|
|
4577
|
+
}
|
|
4578
|
+
],
|
|
4579
|
+
// #5823: `label` names a capability, not a command. The mutation door is `oracle issue edit`
|
|
4580
|
+
// (`--add-label` / `--remove-label`); distance cannot bridge noun→verb, so name it here.
|
|
4581
|
+
[
|
|
4582
|
+
"oracle issue:label",
|
|
4583
|
+
{
|
|
4584
|
+
hint: "no `oracle issue label` \u2014 labels are edited through `oracle issue edit --add-label <name>` / `--remove-label <name>`",
|
|
4585
|
+
expected: ["oracle issue edit"],
|
|
4586
|
+
did_you_mean: "oracle issue edit"
|
|
4587
|
+
}
|
|
4548
4588
|
]
|
|
4549
4589
|
]);
|
|
4550
4590
|
function unknownCommandDomainGuide(parentPath, token) {
|
|
@@ -5538,7 +5578,7 @@ function commandLadderHint() {
|
|
|
5538
5578
|
}
|
|
5539
5579
|
|
|
5540
5580
|
// src/index.ts
|
|
5541
|
-
var
|
|
5581
|
+
var import_node_path40 = require("node:path");
|
|
5542
5582
|
|
|
5543
5583
|
// src/merge-ci-policy.ts
|
|
5544
5584
|
function resolveMergeCiPolicy(input) {
|
|
@@ -11762,10 +11802,10 @@ var rollout_plan_default = {
|
|
|
11762
11802
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
11763
11803
|
},
|
|
11764
11804
|
baseline: {
|
|
11765
|
-
version: "4.2.
|
|
11766
|
-
tag: "v4.2.
|
|
11767
|
-
commit: "
|
|
11768
|
-
npm: "@mutmutco/cli@4.2.
|
|
11805
|
+
version: "4.2.2",
|
|
11806
|
+
tag: "v4.2.2",
|
|
11807
|
+
commit: "c347a86b94cc",
|
|
11808
|
+
npm: "@mutmutco/cli@4.2.2"
|
|
11769
11809
|
},
|
|
11770
11810
|
exitCriterion: "fleet-n-of-n",
|
|
11771
11811
|
hubOnlyShortcut: "forbidden",
|
|
@@ -11782,14 +11822,14 @@ var rollout_plan_default = {
|
|
|
11782
11822
|
repo: "mutmutco/mmi-hub",
|
|
11783
11823
|
role: "canary",
|
|
11784
11824
|
schedule: "train",
|
|
11785
|
-
v3Target: "v4.2.
|
|
11825
|
+
v3Target: "v4.2.2"
|
|
11786
11826
|
}
|
|
11787
11827
|
],
|
|
11788
11828
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
11789
11829
|
rollback: {
|
|
11790
11830
|
independent: true,
|
|
11791
|
-
mechanism: "npm dist-tag latest -> 4.2.
|
|
11792
|
-
v3Target: "v4.2.
|
|
11831
|
+
mechanism: "npm dist-tag latest -> 4.2.2 and redeploy the Hub Lambda from tag v4.2.2 (c347a86b94cc); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
11832
|
+
v3Target: "v4.2.2 (@mutmutco/cli@4.2.2, tag commit c347a86b94cc \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
11793
11833
|
}
|
|
11794
11834
|
},
|
|
11795
11835
|
{
|
|
@@ -14739,10 +14779,16 @@ async function correlateWorkflowRun(deps, args) {
|
|
|
14739
14779
|
async function watchTenantRun(deps, runId, repo = HUB_REPO3) {
|
|
14740
14780
|
if (runId == null) return "pending";
|
|
14741
14781
|
let watchSaidSuccess = true;
|
|
14782
|
+
const startedAt = Date.now();
|
|
14783
|
+
deps.warn?.(`watching workflow run ${runId} (${repo})`);
|
|
14784
|
+
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;
|
|
14785
|
+
heartbeat?.unref();
|
|
14742
14786
|
try {
|
|
14743
14787
|
await deps.run("gh", ["run", "watch", String(runId), "--repo", repo, "--exit-status"]);
|
|
14744
14788
|
} catch {
|
|
14745
14789
|
watchSaidSuccess = false;
|
|
14790
|
+
} finally {
|
|
14791
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
14746
14792
|
}
|
|
14747
14793
|
const sleep2 = resolveSleep(deps);
|
|
14748
14794
|
let confirmError;
|
|
@@ -16338,8 +16384,9 @@ async function runReleasePublishRetry(deps, runId, options = {}) {
|
|
|
16338
16384
|
}
|
|
16339
16385
|
};
|
|
16340
16386
|
const run = await readRun();
|
|
16341
|
-
|
|
16342
|
-
|
|
16387
|
+
const retryableConclusion = run.conclusion === "failure" || run.conclusion === "cancelled";
|
|
16388
|
+
if (run.databaseId !== runId || run.workflowName !== "publish" || run.event !== "release" || run.status !== "completed" || !retryableConclusion) {
|
|
16389
|
+
throw new Error(`run ${runId} is not a completed failed or cancelled Hub publish release run; nothing was written`);
|
|
16343
16390
|
}
|
|
16344
16391
|
const tag = run.headBranch ?? "";
|
|
16345
16392
|
const tagSha = run.headSha ?? "";
|
|
@@ -16350,13 +16397,16 @@ async function runReleasePublishRetry(deps, runId, options = {}) {
|
|
|
16350
16397
|
throw new Error(`run ${runId} does not match origin ${tag}; nothing was written`);
|
|
16351
16398
|
}
|
|
16352
16399
|
await verifyPublishedRelease(deps, ctx.repo, tag, "main", tagSha);
|
|
16353
|
-
await deps.run("gh", [
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16400
|
+
await deps.run("gh", [
|
|
16401
|
+
"run",
|
|
16402
|
+
"rerun",
|
|
16403
|
+
String(runId),
|
|
16404
|
+
"--repo",
|
|
16405
|
+
ctx.repo,
|
|
16406
|
+
...run.conclusion === "failure" ? ["--failed"] : []
|
|
16407
|
+
]);
|
|
16408
|
+
if (options.watch && await watchTenantRun(deps, runId, ctx.repo) !== "success") {
|
|
16409
|
+
throw new Error(`publish run ${runId} did not finish successfully after retry`);
|
|
16360
16410
|
}
|
|
16361
16411
|
return {
|
|
16362
16412
|
command: "release-retry-publish",
|
|
@@ -16366,7 +16416,7 @@ async function runReleasePublishRetry(deps, runId, options = {}) {
|
|
|
16366
16416
|
runId,
|
|
16367
16417
|
runUrl: run.url ?? `https://github.com/${ctx.repo}/actions/runs/${runId}`,
|
|
16368
16418
|
status: options.watch ? "success" : "pending",
|
|
16369
|
-
note: options.watch ? `publish run ${runId} retried and passed` : `publish run ${runId}
|
|
16419
|
+
note: options.watch ? `publish run ${runId} retried and passed` : `publish run ${runId} queued for retry`
|
|
16370
16420
|
};
|
|
16371
16421
|
}
|
|
16372
16422
|
async function runTrainApplyPipeline(mode, input) {
|
|
@@ -17707,6 +17757,12 @@ async function finalizeCiReconcile(repo, deps, result, before, pendingReason) {
|
|
|
17707
17757
|
return result;
|
|
17708
17758
|
}
|
|
17709
17759
|
}
|
|
17760
|
+
function ciReconcileExitFailed(input) {
|
|
17761
|
+
if (!input.apply || input.applyResults.length === 0) {
|
|
17762
|
+
return !input.audit.ok || input.applyResults.some((result) => result.errors.length > 0 || result.postApply?.state === "failed");
|
|
17763
|
+
}
|
|
17764
|
+
return input.applyResults.some((result) => result.errors.length > 0 || result.postApply == null || result.postApply.state !== "clean" && result.postApply.state !== "repaired");
|
|
17765
|
+
}
|
|
17710
17766
|
async function authoritativeSafetyHold(authority, deps, repo, baseBranch) {
|
|
17711
17767
|
if (authority.coversReleaseBranches) {
|
|
17712
17768
|
const prOnly = authority.contexts.filter((context) => TRAIN_PR_ONLY_CONTEXTS.has(context));
|
|
@@ -20933,11 +20989,11 @@ function findBoardItem(items, selector, board) {
|
|
|
20933
20989
|
}
|
|
20934
20990
|
async function resolveCurrentRepo(options, deps) {
|
|
20935
20991
|
if (options.repo) return options.repo;
|
|
20936
|
-
const
|
|
20992
|
+
const git3 = deps.git ?? defaultGit;
|
|
20937
20993
|
let gitFailure;
|
|
20938
20994
|
let originUrl = "";
|
|
20939
20995
|
try {
|
|
20940
|
-
originUrl = await
|
|
20996
|
+
originUrl = await git3(["remote", "get-url", "origin"]);
|
|
20941
20997
|
} catch (e) {
|
|
20942
20998
|
const read = gitReadState(e);
|
|
20943
20999
|
if (read.state === "failed") gitFailure = read.detail;
|
|
@@ -23492,8 +23548,11 @@ var defaultDeps = {
|
|
|
23492
23548
|
},
|
|
23493
23549
|
// Re-exec the SAME command line in the fresh CLI with inherited stdio (spawn, not execFile, so
|
|
23494
23550
|
// output streams live) and hand its exit code back — the stale parent exits with it (#5762).
|
|
23495
|
-
|
|
23496
|
-
|
|
23551
|
+
// #5813: the caller supplies that command line. `process.argv` is NOT it — the house shim splices
|
|
23552
|
+
// the house root out in place, so re-reading it here re-ran the removed flat alias (`release …`
|
|
23553
|
+
// instead of `devops release …`) and the converged child refused with exit 2.
|
|
23554
|
+
reexec: (argv) => new Promise((resolve6) => {
|
|
23555
|
+
const args = [...argv];
|
|
23497
23556
|
const child2 = isWin2 ? (0, import_node_child_process10.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", windowsHide: true }) : (0, import_node_child_process10.spawn)("mmi-cli", args, { stdio: "inherit", windowsHide: true });
|
|
23498
23557
|
child2.on("exit", (code) => resolve6(code ?? 1));
|
|
23499
23558
|
child2.on("error", () => resolve6(1));
|
|
@@ -23515,7 +23574,7 @@ async function selfConvergeTrainCli(input) {
|
|
|
23515
23574
|
error: `self-converge did not take effect: mmi-cli on PATH still resolves to ${installed ?? "unknown"}, expected >= ${input.releasedVersion}`
|
|
23516
23575
|
};
|
|
23517
23576
|
}
|
|
23518
|
-
const reexecCode = await deps.reexec();
|
|
23577
|
+
const reexecCode = await deps.reexec(input.argv);
|
|
23519
23578
|
return { kind: "converged", reexecCode };
|
|
23520
23579
|
} catch (e) {
|
|
23521
23580
|
return { kind: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
@@ -23804,31 +23863,31 @@ var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
|
23804
23863
|
function checkHotfixCoverage(options = {}) {
|
|
23805
23864
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
23806
23865
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
23807
|
-
const
|
|
23866
|
+
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
23867
|
const revList = (range) => {
|
|
23809
|
-
const out =
|
|
23868
|
+
const out = git3(["rev-list", "--no-merges", range]).trim();
|
|
23810
23869
|
return out ? out.split("\n") : [];
|
|
23811
23870
|
};
|
|
23812
23871
|
const isAncestor = (sha, ref) => {
|
|
23813
23872
|
try {
|
|
23814
|
-
|
|
23873
|
+
git3(["merge-base", "--is-ancestor", sha, ref]);
|
|
23815
23874
|
return true;
|
|
23816
23875
|
} catch {
|
|
23817
23876
|
return false;
|
|
23818
23877
|
}
|
|
23819
23878
|
};
|
|
23820
23879
|
const patchId = (sha) => {
|
|
23821
|
-
const diff =
|
|
23880
|
+
const diff = git3(["show", "--no-color", "--pretty=format:", sha]);
|
|
23822
23881
|
if (!diff.trim()) return null;
|
|
23823
|
-
const out =
|
|
23882
|
+
const out = git3(["patch-id", "--stable"], { input: diff }).trim();
|
|
23824
23883
|
return out ? out.split(" ")[0] : null;
|
|
23825
23884
|
};
|
|
23826
23885
|
const changedPaths = (sha) => {
|
|
23827
|
-
const out =
|
|
23886
|
+
const out = git3(["show", "--no-color", "--name-only", "--pretty=format:", sha]).trim();
|
|
23828
23887
|
return out ? out.split("\n") : [];
|
|
23829
23888
|
};
|
|
23830
23889
|
const cherrySources = (sha) => {
|
|
23831
|
-
const message =
|
|
23890
|
+
const message = git3(["log", "-1", "--format=%B", sha]);
|
|
23832
23891
|
return [...message.matchAll(CHERRY_TRAILER)].map((m) => m[1]);
|
|
23833
23892
|
};
|
|
23834
23893
|
const mainOnly = revList(`${rcRef}..${mainRef}`);
|
|
@@ -23843,7 +23902,7 @@ function checkHotfixCoverage(options = {}) {
|
|
|
23843
23902
|
}
|
|
23844
23903
|
};
|
|
23845
23904
|
const commits = mainOnly.map((sha) => {
|
|
23846
|
-
const subject =
|
|
23905
|
+
const subject = git3(["log", "-1", "--format=%s", sha]).trim();
|
|
23847
23906
|
const paths = changedPaths(sha);
|
|
23848
23907
|
if (paths.length > 0 && paths.every((p) => manifestPaths.includes(p))) {
|
|
23849
23908
|
return { sha, subject, coverage: "exempt-distribution" };
|
|
@@ -23872,21 +23931,21 @@ function checkHotfixCoverage(options = {}) {
|
|
|
23872
23931
|
}
|
|
23873
23932
|
function checkHotfixCarries(options) {
|
|
23874
23933
|
const { cwd = process.cwd(), branch, baseRef, targets } = options;
|
|
23875
|
-
const
|
|
23934
|
+
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
23935
|
const isAncestor = (sha, ref) => {
|
|
23877
23936
|
try {
|
|
23878
|
-
|
|
23937
|
+
git3(["merge-base", "--is-ancestor", sha, ref]);
|
|
23879
23938
|
return true;
|
|
23880
23939
|
} catch {
|
|
23881
23940
|
return false;
|
|
23882
23941
|
}
|
|
23883
23942
|
};
|
|
23884
23943
|
const revList = (range) => {
|
|
23885
|
-
const out =
|
|
23944
|
+
const out = git3(["rev-list", "--no-merges", range]).trim();
|
|
23886
23945
|
return out ? out.split("\n") : [];
|
|
23887
23946
|
};
|
|
23888
23947
|
const cherrySources = (sha) => {
|
|
23889
|
-
const message =
|
|
23948
|
+
const message = git3(["log", "-1", "--format=%B", sha]);
|
|
23890
23949
|
return [...message.matchAll(CHERRY_TRAILER)].map((m) => m[1]);
|
|
23891
23950
|
};
|
|
23892
23951
|
const carried = /* @__PURE__ */ new Set();
|
|
@@ -25074,12 +25133,25 @@ function runSpawnPolicy(root) {
|
|
|
25074
25133
|
}
|
|
25075
25134
|
|
|
25076
25135
|
// src/test-policy-core.ts
|
|
25077
|
-
var
|
|
25136
|
+
var import_node_child_process15 = require("node:child_process");
|
|
25078
25137
|
var import_node_fs25 = require("node:fs");
|
|
25079
25138
|
var import_node_path22 = require("node:path");
|
|
25080
25139
|
|
|
25081
25140
|
// ../scripts/test-command-policy-core.mjs
|
|
25141
|
+
var import_node_child_process14 = require("node:child_process");
|
|
25082
25142
|
var TEST_COMMAND_CLASS = "test";
|
|
25143
|
+
var TRAILER_KEY = "Test-Policy-Override";
|
|
25144
|
+
var OVERRIDE_RE = /^Test-Policy-Override:\s*(.+)$/im;
|
|
25145
|
+
var REC = "";
|
|
25146
|
+
var FLD = "";
|
|
25147
|
+
var WAIVABLE_KINDS = [
|
|
25148
|
+
"mandatory-zone-untested",
|
|
25149
|
+
"unrequested-test-file",
|
|
25150
|
+
"protected-removed",
|
|
25151
|
+
"stale-protected-entry",
|
|
25152
|
+
"stale-satisfied-by"
|
|
25153
|
+
];
|
|
25154
|
+
var TEST_WORK_KIND = "unrequested-test-file";
|
|
25083
25155
|
function translateGlob(glob) {
|
|
25084
25156
|
let out = "";
|
|
25085
25157
|
for (let i = 0; i < glob.length; i += 1) {
|
|
@@ -25122,7 +25194,7 @@ function matchedMandatoryGlobs(paths, mandatory) {
|
|
|
25122
25194
|
return list.some((path2) => re.test(path2));
|
|
25123
25195
|
});
|
|
25124
25196
|
}
|
|
25125
|
-
function evaluateTestCommandPolicy({ paths, mandatory, regulated = true } = {}) {
|
|
25197
|
+
function evaluateTestCommandPolicy({ paths, mandatory, regulated = true, override = null } = {}) {
|
|
25126
25198
|
const configuredMandatoryCount = mandatoryGlobList(mandatory).length;
|
|
25127
25199
|
if (!regulated) {
|
|
25128
25200
|
return {
|
|
@@ -25135,7 +25207,8 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true } = {})
|
|
|
25135
25207
|
};
|
|
25136
25208
|
}
|
|
25137
25209
|
const matched = matchedMandatoryGlobs(paths, mandatory);
|
|
25138
|
-
const
|
|
25210
|
+
const overrideAuthorizes = Array.isArray(override?.kinds) && override.kinds.includes(TEST_WORK_KIND);
|
|
25211
|
+
const testCommandsAllowed = matched.length > 0 || overrideAuthorizes;
|
|
25139
25212
|
return {
|
|
25140
25213
|
configuredMandatoryCount,
|
|
25141
25214
|
matchedMandatoryGlobs: matched,
|
|
@@ -25148,22 +25221,78 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true } = {})
|
|
|
25148
25221
|
}
|
|
25149
25222
|
};
|
|
25150
25223
|
}
|
|
25224
|
+
function git(args, cwd) {
|
|
25225
|
+
return (0, import_node_child_process14.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
25226
|
+
}
|
|
25227
|
+
function parseScope(value) {
|
|
25228
|
+
const scoped = /^\[([^\]]*)\]\s*([\s\S]*)$/.exec(value);
|
|
25229
|
+
if (!scoped) return { kinds: [...WAIVABLE_KINDS], reason: value, unknown: [] };
|
|
25230
|
+
const named = scoped[1].split(",").map((k) => k.trim()).filter(Boolean);
|
|
25231
|
+
return {
|
|
25232
|
+
kinds: named,
|
|
25233
|
+
reason: scoped[2].trim(),
|
|
25234
|
+
unknown: named.filter((k) => !WAIVABLE_KINDS.includes(k))
|
|
25235
|
+
};
|
|
25236
|
+
}
|
|
25237
|
+
function invisibleTrailer(sha, line) {
|
|
25238
|
+
return {
|
|
25239
|
+
kind: "malformed-override-trailer",
|
|
25240
|
+
paths: [],
|
|
25241
|
+
detail: `OVERRIDE TRAILER GIT CANNOT SEE \u2014 commit ${sha.slice(0, 8)} carries
|
|
25242
|
+
${line}
|
|
25243
|
+
but \`git log --format='%(trailers:key=${TRAILER_KEY})'\` reports nothing for it, so the waiver would
|
|
25244
|
+
exist only to the regex that granted it. The trailer was chosen over a flag because it can be
|
|
25245
|
+
AUDITED, and a reason the auditor cannot see is not a reason this gate accepts (#3628).
|
|
25246
|
+
Git reads trailers from the LAST paragraph only, and every line of that paragraph must be a
|
|
25247
|
+
trailer or an indented continuation \u2014 one bare line such as \`Closes #123\` disqualifies the whole
|
|
25248
|
+
block. Indent the continuation lines, and leave nothing but trailers in that paragraph.
|
|
25249
|
+
This is fixable forward: push a LATER commit on this branch carrying a well-formed trailer.
|
|
25250
|
+
The nearest waiver wins, and it supersedes this one \u2014 no force-push, no re-filed branch.`
|
|
25251
|
+
};
|
|
25252
|
+
}
|
|
25253
|
+
function unknownScope(sha, unknown) {
|
|
25254
|
+
return {
|
|
25255
|
+
kind: "malformed-override-trailer",
|
|
25256
|
+
paths: [],
|
|
25257
|
+
detail: `UNKNOWN OVERRIDE SCOPE \u2014 commit ${sha.slice(0, 8)} scopes its waiver to ${unknown.join(", ")}, which this
|
|
25258
|
+
gate cannot report.
|
|
25259
|
+
Waivable kinds: ${WAIVABLE_KINDS.join(", ")}.
|
|
25260
|
+
Refused rather than widened: treating a typo as "waive everything" is how a scoped waiver turns
|
|
25261
|
+
into a blanket exemption without anyone deciding it should.`
|
|
25262
|
+
};
|
|
25263
|
+
}
|
|
25264
|
+
function isShallowRepository(cwd) {
|
|
25265
|
+
try {
|
|
25266
|
+
return git(["rev-parse", "--is-shallow-repository"], cwd).trim() !== "false";
|
|
25267
|
+
} catch {
|
|
25268
|
+
return true;
|
|
25269
|
+
}
|
|
25270
|
+
}
|
|
25271
|
+
function readOverride(base, cwd) {
|
|
25272
|
+
const format = `%H${FLD}%(trailers:key=${TRAILER_KEY},valueonly,unfold)${FLD}%B${REC}`;
|
|
25273
|
+
const refusals = [];
|
|
25274
|
+
let override = null;
|
|
25275
|
+
for (const record of git(["log", `${base}..HEAD`, `--format=${format}`], cwd).split(REC)) {
|
|
25276
|
+
const [sha, trailer, body] = record.replace(/^\s+/, "").split(FLD);
|
|
25277
|
+
if (!sha) continue;
|
|
25278
|
+
if (override) break;
|
|
25279
|
+
const value = (trailer ?? "").trim();
|
|
25280
|
+
if (!value) {
|
|
25281
|
+
const shaped = OVERRIDE_RE.exec(body ?? "");
|
|
25282
|
+
if (shaped) refusals.push(invisibleTrailer(sha, shaped[0].trim()));
|
|
25283
|
+
continue;
|
|
25284
|
+
}
|
|
25285
|
+
const { kinds, reason, unknown } = parseScope(value);
|
|
25286
|
+
if (unknown.length > 0) refusals.push(unknownScope(sha, unknown));
|
|
25287
|
+
else override = { sha, reason, kinds };
|
|
25288
|
+
}
|
|
25289
|
+
return { override, refusals };
|
|
25290
|
+
}
|
|
25151
25291
|
|
|
25152
25292
|
// src/test-policy-core.ts
|
|
25153
25293
|
var POLICY_FILE = "test-policy.json";
|
|
25154
25294
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
25155
25295
|
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
25296
|
function translate(glob) {
|
|
25168
25297
|
let out = "";
|
|
25169
25298
|
for (let i = 0; i < glob.length; i++) {
|
|
@@ -25521,15 +25650,15 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
25521
25650
|
}
|
|
25522
25651
|
return findings;
|
|
25523
25652
|
}
|
|
25524
|
-
function
|
|
25525
|
-
return (0,
|
|
25653
|
+
function git2(args, cwd) {
|
|
25654
|
+
return (0, import_node_child_process15.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
25526
25655
|
}
|
|
25527
25656
|
var COAUTHOR_KEY = "Co-authored-by";
|
|
25528
25657
|
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
25529
25658
|
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
25530
25659
|
function parseTrailers(message, cwd) {
|
|
25531
25660
|
try {
|
|
25532
|
-
return (0,
|
|
25661
|
+
return (0, import_node_child_process15.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
25533
25662
|
windowsHide: true,
|
|
25534
25663
|
cwd,
|
|
25535
25664
|
input: message,
|
|
@@ -25566,19 +25695,12 @@ function resolveBase(cwd, explicit) {
|
|
|
25566
25695
|
const candidates = [explicit, process.env.TEST_POLICY_BASE, "origin/development", "origin/main"].filter(Boolean);
|
|
25567
25696
|
for (const ref of candidates) {
|
|
25568
25697
|
try {
|
|
25569
|
-
return
|
|
25698
|
+
return git2(["merge-base", "HEAD", ref], cwd).trim();
|
|
25570
25699
|
} catch {
|
|
25571
25700
|
}
|
|
25572
25701
|
}
|
|
25573
25702
|
return null;
|
|
25574
25703
|
}
|
|
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
25704
|
function untrustworthyRange(cwd, base) {
|
|
25583
25705
|
if (!base) {
|
|
25584
25706
|
return {
|
|
@@ -25600,65 +25722,11 @@ function untrustworthyRange(cwd, base) {
|
|
|
25600
25722
|
Fetch full history \u2014 actions/checkout with \`fetch-depth: 0\`, or \`git fetch --unshallow\`.`
|
|
25601
25723
|
};
|
|
25602
25724
|
}
|
|
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 };
|
|
25725
|
+
function readOverride2(base, cwd) {
|
|
25726
|
+
return readOverride(base, cwd);
|
|
25659
25727
|
}
|
|
25660
25728
|
function changedFilesSince(base, cwd) {
|
|
25661
|
-
const raw =
|
|
25729
|
+
const raw = git2(["diff", "--name-status", "-M", base, "--"], cwd).trim();
|
|
25662
25730
|
const changed = raw ? raw.split("\n").map((line) => {
|
|
25663
25731
|
const parts = line.split(" ");
|
|
25664
25732
|
const norm = (p) => p.replace(/\\/g, "/");
|
|
@@ -25666,12 +25734,12 @@ function changedFilesSince(base, cwd) {
|
|
|
25666
25734
|
if (parts.length > 2) row.from = norm(parts[1]);
|
|
25667
25735
|
return row;
|
|
25668
25736
|
}) : [];
|
|
25669
|
-
const untracked =
|
|
25737
|
+
const untracked = git2(["ls-files", "--others", "--exclude-standard", "-z"], cwd).split("\0").filter(Boolean).map((path2) => ({ status: "A", path: path2.replace(/\\/g, "/") }));
|
|
25670
25738
|
return [...changed, ...untracked];
|
|
25671
25739
|
}
|
|
25672
25740
|
function blobAt(base, path2, cwd) {
|
|
25673
25741
|
try {
|
|
25674
|
-
return
|
|
25742
|
+
return git2(["show", `${base}:${path2}`], cwd);
|
|
25675
25743
|
} catch {
|
|
25676
25744
|
return null;
|
|
25677
25745
|
}
|
|
@@ -25687,7 +25755,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
25687
25755
|
before: blobAt(base, path2, root),
|
|
25688
25756
|
after: readFileOrNull2((0, import_node_path22.join)(root, path2))
|
|
25689
25757
|
}));
|
|
25690
|
-
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ?
|
|
25758
|
+
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride2(base, root) : { override: null, refusals: [] };
|
|
25691
25759
|
const present = (path2) => exists((0, import_node_path22.join)(root, path2));
|
|
25692
25760
|
const removedByThisDiff = removedPaths(changed);
|
|
25693
25761
|
const staleFindings = [];
|
|
@@ -25720,7 +25788,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
25720
25788
|
const commandPolicy = evaluateTestCommandPolicy({
|
|
25721
25789
|
paths: changed.map((f) => f.path),
|
|
25722
25790
|
mandatory: policy.mandatory,
|
|
25723
|
-
regulated: policy.declared !== false
|
|
25791
|
+
regulated: policy.declared !== false,
|
|
25792
|
+
override: override && lookup.refusals.length === 0 ? { kinds: override.kinds } : null
|
|
25724
25793
|
});
|
|
25725
25794
|
const result = {
|
|
25726
25795
|
ok: findings.length === 0,
|
|
@@ -25742,7 +25811,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
25742
25811
|
}
|
|
25743
25812
|
|
|
25744
25813
|
// src/dist-drift.ts
|
|
25745
|
-
var
|
|
25814
|
+
var import_node_child_process16 = require("node:child_process");
|
|
25746
25815
|
var import_node_crypto7 = require("node:crypto");
|
|
25747
25816
|
var import_node_fs27 = require("node:fs");
|
|
25748
25817
|
var import_node_os13 = require("node:os");
|
|
@@ -25887,7 +25956,7 @@ function bomPathFor(root) {
|
|
|
25887
25956
|
}
|
|
25888
25957
|
}
|
|
25889
25958
|
function rebuildTo(packageRoot, outDir) {
|
|
25890
|
-
(0,
|
|
25959
|
+
(0, import_node_child_process16.execFileSync)(process.execPath, ["build.mjs"], {
|
|
25891
25960
|
cwd: packageRoot,
|
|
25892
25961
|
env: { ...process.env, MMI_DIST_OUTDIR: outDir },
|
|
25893
25962
|
windowsHide: true,
|
|
@@ -27604,9 +27673,9 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
27604
27673
|
|
|
27605
27674
|
// src/schedules-commands.ts
|
|
27606
27675
|
var import_promises4 = require("node:fs/promises");
|
|
27607
|
-
var
|
|
27676
|
+
var import_node_child_process17 = require("node:child_process");
|
|
27608
27677
|
var import_node_util7 = require("node:util");
|
|
27609
|
-
var execFileP5 = (0, import_node_util7.promisify)(
|
|
27678
|
+
var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process17.execFile);
|
|
27610
27679
|
var AWS_REGION = "eu-central-1";
|
|
27611
27680
|
var AWS_TIMEOUT_MS = 3e4;
|
|
27612
27681
|
var AWS_RETRY_DELAY_MS = 1500;
|
|
@@ -30932,6 +31001,9 @@ function buildPrMergeArgs(input) {
|
|
|
30932
31001
|
if (input.auto) args.push("--auto");
|
|
30933
31002
|
return args;
|
|
30934
31003
|
}
|
|
31004
|
+
function buildPrDisableAutoArgs(input) {
|
|
31005
|
+
return ["pr", "merge", input.number, ...input.repoArgs, "--disable-auto"];
|
|
31006
|
+
}
|
|
30935
31007
|
function basePolicyBlocksImmediateMerge(message) {
|
|
30936
31008
|
return /base branch policy prohibits|protected branch|required status check|merge queue/i.test(message);
|
|
30937
31009
|
}
|
|
@@ -31428,19 +31500,19 @@ function formatWorktreeRemovalFailureDetail(options) {
|
|
|
31428
31500
|
const detail = options.removeError.trim() || "git worktree remove exited nonzero";
|
|
31429
31501
|
return `${detail}; registered=${options.registered ? "yes" : "no"}; pathExists=${options.pathExists ? "yes" : "no"}; path=${options.wtPath}`;
|
|
31430
31502
|
}
|
|
31431
|
-
async function resolveMergeBaseTimestampMs(
|
|
31503
|
+
async function resolveMergeBaseTimestampMs(git3, baseRef, branch) {
|
|
31432
31504
|
try {
|
|
31433
|
-
const mergeBase = (await
|
|
31505
|
+
const mergeBase = (await git3(["merge-base", baseRef, branch])).trim();
|
|
31434
31506
|
if (!mergeBase) return 0;
|
|
31435
|
-
const sec = parseCommitTimestampSec(await
|
|
31507
|
+
const sec = parseCommitTimestampSec(await git3(["show", "-s", "--format=%ct", mergeBase]));
|
|
31436
31508
|
return sec ? sec * 1e3 : 0;
|
|
31437
31509
|
} catch {
|
|
31438
31510
|
return 0;
|
|
31439
31511
|
}
|
|
31440
31512
|
}
|
|
31441
|
-
async function verifyBranchHead(
|
|
31513
|
+
async function verifyBranchHead(git3, branch, expectedHeadOid) {
|
|
31442
31514
|
if (!expectedHeadOid) return { ok: false, reason: "branch-head-unverified" };
|
|
31443
|
-
const currentHead = (await
|
|
31515
|
+
const currentHead = (await git3(["rev-parse", `refs/heads/${branch}`]).catch(() => "") || "").trim();
|
|
31444
31516
|
if (!currentHead) return { ok: false, reason: "branch-head-unverified" };
|
|
31445
31517
|
if (currentHead !== expectedHeadOid) {
|
|
31446
31518
|
return { ok: false, reason: "unpushed-branch", error: `${currentHead} != ${expectedHeadOid}` };
|
|
@@ -31455,17 +31527,17 @@ async function teardownWorktreeStage(worktreePath) {
|
|
|
31455
31527
|
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
31456
31528
|
}
|
|
31457
31529
|
}
|
|
31458
|
-
async function removeWorktreeWithReconcile(wtPath,
|
|
31530
|
+
async function removeWorktreeWithReconcile(wtPath, git3, listWorktrees, pathExists) {
|
|
31459
31531
|
const attemptRemove = async () => {
|
|
31460
31532
|
try {
|
|
31461
|
-
await
|
|
31533
|
+
await git3(["worktree", "remove", "--force", wtPath]);
|
|
31462
31534
|
return void 0;
|
|
31463
31535
|
} catch (e) {
|
|
31464
31536
|
return formatGitCommandError(e);
|
|
31465
31537
|
}
|
|
31466
31538
|
};
|
|
31467
31539
|
const pruneAndVerify = async (reason) => {
|
|
31468
|
-
await
|
|
31540
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
31469
31541
|
let worktrees2;
|
|
31470
31542
|
try {
|
|
31471
31543
|
worktrees2 = await listWorktrees();
|
|
@@ -31495,7 +31567,7 @@ async function removeWorktreeWithReconcile(wtPath, git2, listWorktrees, pathExis
|
|
|
31495
31567
|
};
|
|
31496
31568
|
}
|
|
31497
31569
|
if (!isWorktreePathRegistered(worktrees, wtPath)) return pruneAndVerify("reconciled-unregistered");
|
|
31498
|
-
await
|
|
31570
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
31499
31571
|
try {
|
|
31500
31572
|
worktrees = await listWorktrees();
|
|
31501
31573
|
} catch (e) {
|
|
@@ -31582,8 +31654,8 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
31582
31654
|
return report;
|
|
31583
31655
|
}
|
|
31584
31656
|
const safeCwd = selectSafeWorktreeCwd([...afterWorktrees, ...beforeWorktrees], wtPath, { pathExists });
|
|
31585
|
-
const
|
|
31586
|
-
const headCheck = await verifyBranchHead(
|
|
31657
|
+
const git3 = (args) => safeCwd ? execGit(["-C", safeCwd, ...args]) : execGit(args);
|
|
31658
|
+
const headCheck = await verifyBranchHead(git3, branch, options.expectedHeadOid);
|
|
31587
31659
|
if (!headCheck.ok) {
|
|
31588
31660
|
report.localBranch = {
|
|
31589
31661
|
name: branch,
|
|
@@ -31646,8 +31718,8 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
31646
31718
|
}
|
|
31647
31719
|
const removal = await removeWorktreeWithReconcile(
|
|
31648
31720
|
wtPath,
|
|
31649
|
-
|
|
31650
|
-
async () => parseGitWorktreePorcelain(await
|
|
31721
|
+
git3,
|
|
31722
|
+
async () => parseGitWorktreePorcelain(await git3(["worktree", "list", "--porcelain"])),
|
|
31651
31723
|
pathExists
|
|
31652
31724
|
);
|
|
31653
31725
|
if (removal.status === "failed") {
|
|
@@ -31680,10 +31752,10 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
31680
31752
|
}
|
|
31681
31753
|
}
|
|
31682
31754
|
try {
|
|
31683
|
-
await
|
|
31755
|
+
await git3(["branch", "-D", branch]);
|
|
31684
31756
|
report.localBranch = { name: branch, status: "deleted" };
|
|
31685
31757
|
} catch (e) {
|
|
31686
|
-
const remaining = await
|
|
31758
|
+
const remaining = await git3(["branch", "--list", branch]).catch(() => "");
|
|
31687
31759
|
report.localBranch = remaining.trim() ? { name: branch, status: "failed", error: e instanceof Error ? e.message : String(e) } : { name: branch, status: "already-gone" };
|
|
31688
31760
|
}
|
|
31689
31761
|
return report;
|
|
@@ -31717,6 +31789,17 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
31717
31789
|
return lines;
|
|
31718
31790
|
}
|
|
31719
31791
|
|
|
31792
|
+
// src/utf8-receipt.ts
|
|
31793
|
+
var import_node_fs37 = require("node:fs");
|
|
31794
|
+
var import_node_path35 = require("node:path");
|
|
31795
|
+
function writeUtf8Receipt(path2, text) {
|
|
31796
|
+
(0, import_node_fs37.mkdirSync)((0, import_node_path35.dirname)(path2), { recursive: true });
|
|
31797
|
+
const body = text.endsWith("\n") ? text : `${text}
|
|
31798
|
+
`;
|
|
31799
|
+
(0, import_node_fs37.writeFileSync)(path2, body, "utf8");
|
|
31800
|
+
return Buffer.byteLength(body, "utf8");
|
|
31801
|
+
}
|
|
31802
|
+
|
|
31720
31803
|
// src/board-commands.ts
|
|
31721
31804
|
function refuseRateLimited(e, json) {
|
|
31722
31805
|
if (!isRateLimitedError(e)) return false;
|
|
@@ -31744,7 +31827,9 @@ function registerBoardCommands(program3) {
|
|
|
31744
31827
|
includeAllBodies: o.bodies,
|
|
31745
31828
|
allowPartial: o.allowPartial
|
|
31746
31829
|
}, o.direct ? {} : { snapshot: registryClientDeps(config) });
|
|
31747
|
-
|
|
31830
|
+
const output = o.json ? JSON.stringify(report) : renderBoardReport(report);
|
|
31831
|
+
if (!o.out) console.log(output);
|
|
31832
|
+
else console.log(`Wrote board read to ${o.out} (UTF-8, ${writeUtf8Receipt(o.out, output)} bytes)`);
|
|
31748
31833
|
} catch (e) {
|
|
31749
31834
|
return failGraceful(`board read failed: ${withDiscoverMissDetail(e.message)}`);
|
|
31750
31835
|
}
|
|
@@ -31753,7 +31838,7 @@ function registerBoardCommands(program3) {
|
|
|
31753
31838
|
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
31839
|
}
|
|
31755
31840
|
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));
|
|
31841
|
+
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
31842
|
withExamples(mutating(
|
|
31758
31843
|
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
31844
|
(_opts, args) => ({ command: "board claim", issues: args[0] ?? [] })
|
|
@@ -32416,7 +32501,7 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
32416
32501
|
}
|
|
32417
32502
|
|
|
32418
32503
|
// src/issue-commands.ts
|
|
32419
|
-
var
|
|
32504
|
+
var import_node_fs38 = require("node:fs");
|
|
32420
32505
|
var import_node_crypto11 = require("node:crypto");
|
|
32421
32506
|
|
|
32422
32507
|
// src/learning-closure-rate.ts
|
|
@@ -32603,7 +32688,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
32603
32688
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
32604
32689
|
const patch = {};
|
|
32605
32690
|
let bodyChanged = false;
|
|
32606
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
32691
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs38.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
32607
32692
|
if (options.titleFile !== void 0) {
|
|
32608
32693
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
32609
32694
|
} else if (options.title !== void 0) {
|
|
@@ -33220,7 +33305,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
33220
33305
|
if (opts.batch) {
|
|
33221
33306
|
let specs;
|
|
33222
33307
|
try {
|
|
33223
|
-
const raw = (0,
|
|
33308
|
+
const raw = (0, import_node_fs38.readFileSync)(opts.batch, "utf8");
|
|
33224
33309
|
specs = JSON.parse(raw);
|
|
33225
33310
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
33226
33311
|
} catch (e) {
|
|
@@ -33295,8 +33380,8 @@ ${lines}`, {
|
|
|
33295
33380
|
}
|
|
33296
33381
|
|
|
33297
33382
|
// src/train-commands.ts
|
|
33298
|
-
var
|
|
33299
|
-
var
|
|
33383
|
+
var import_node_fs39 = require("node:fs");
|
|
33384
|
+
var import_node_path36 = require("node:path");
|
|
33300
33385
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
33301
33386
|
function resolveReleaseBumpIntent(raw) {
|
|
33302
33387
|
const intent = typeof raw === "string" ? raw.trim() : "";
|
|
@@ -33307,7 +33392,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
33307
33392
|
}
|
|
33308
33393
|
function readRepoVersion() {
|
|
33309
33394
|
try {
|
|
33310
|
-
return JSON.parse((0,
|
|
33395
|
+
return JSON.parse((0, import_node_fs39.readFileSync)((0, import_node_path36.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
33311
33396
|
} catch {
|
|
33312
33397
|
return void 0;
|
|
33313
33398
|
}
|
|
@@ -33464,9 +33549,9 @@ function registerDeployCommands(program3) {
|
|
|
33464
33549
|
}
|
|
33465
33550
|
|
|
33466
33551
|
// src/discovery-commands.ts
|
|
33467
|
-
var
|
|
33552
|
+
var import_node_fs40 = require("node:fs");
|
|
33468
33553
|
var import_node_os17 = require("node:os");
|
|
33469
|
-
var
|
|
33554
|
+
var import_node_path37 = require("node:path");
|
|
33470
33555
|
var GC_GH_TIMEOUT_MS2 = 2e4;
|
|
33471
33556
|
async function collectStatus() {
|
|
33472
33557
|
const repo = await resolveRepo();
|
|
@@ -33656,8 +33741,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
33656
33741
|
}
|
|
33657
33742
|
const home = (0, import_node_os17.homedir)();
|
|
33658
33743
|
const plugin = onboardPluginGate({
|
|
33659
|
-
readKnown: () => readFileSyncSafe((0,
|
|
33660
|
-
readSettings: () => readFileSyncSafe((0,
|
|
33744
|
+
readKnown: () => readFileSyncSafe((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs40.readFileSync),
|
|
33745
|
+
readSettings: () => readFileSyncSafe((0, import_node_path37.join)(home, ".claude", "settings.json"), import_node_fs40.readFileSync)
|
|
33661
33746
|
});
|
|
33662
33747
|
return { track, board, registry: registry2, secrets, plugin, doors: opts.doors ?? [], nextCommand };
|
|
33663
33748
|
}
|
|
@@ -33865,12 +33950,18 @@ function catalogCommandPaths(manifest) {
|
|
|
33865
33950
|
function nearestExplainTargets(query, paths, limit = 5) {
|
|
33866
33951
|
const parentPath = query.includes(" ") ? query.slice(0, query.lastIndexOf(" ")) : void 0;
|
|
33867
33952
|
const leaf = query.includes(" ") ? query.slice(query.lastIndexOf(" ") + 1) : query;
|
|
33868
|
-
const guide = unknownCommandDomainGuide(parentPath, leaf);
|
|
33869
|
-
if (guide) return guide.expected.slice(0, limit);
|
|
33870
33953
|
const all = [...paths];
|
|
33871
33954
|
const leafOf = (p) => p.slice(p.lastIndexOf(" ") + 1);
|
|
33955
|
+
const siblings = parentPath ? all.filter((p) => p !== query && p.startsWith(`${parentPath} `) && !p.slice(parentPath.length + 1).includes(" ")).sort() : [];
|
|
33956
|
+
const withSiblings = (primary) => {
|
|
33957
|
+
const out2 = [...primary];
|
|
33958
|
+
for (const sibling of siblings) if (!out2.includes(sibling)) out2.push(sibling);
|
|
33959
|
+
return out2.slice(0, limit);
|
|
33960
|
+
};
|
|
33961
|
+
const guide = unknownCommandDomainGuide(parentPath, leaf);
|
|
33962
|
+
if (guide) return withSiblings(guide.expected);
|
|
33872
33963
|
const exactLeaf = all.filter((p) => p !== query && leafOf(p) === leaf).sort();
|
|
33873
|
-
if (exactLeaf.length) return exactLeaf
|
|
33964
|
+
if (exactLeaf.length) return withSiblings(exactLeaf);
|
|
33874
33965
|
const out = [];
|
|
33875
33966
|
const nearPath = didYouMean(query, all);
|
|
33876
33967
|
if (nearPath) out.push(nearPath);
|
|
@@ -33881,7 +33972,7 @@ function nearestExplainTargets(query, paths, limit = 5) {
|
|
|
33881
33972
|
if (out.length >= limit) break;
|
|
33882
33973
|
}
|
|
33883
33974
|
}
|
|
33884
|
-
return out
|
|
33975
|
+
return withSiblings(out);
|
|
33885
33976
|
}
|
|
33886
33977
|
function explainCommandManifest(manifest, command, opts = {}) {
|
|
33887
33978
|
const base = {
|
|
@@ -34420,8 +34511,8 @@ function registerPrLifecycleCommands(program3) {
|
|
|
34420
34511
|
}
|
|
34421
34512
|
|
|
34422
34513
|
// src/post-merge-recon.ts
|
|
34423
|
-
var
|
|
34424
|
-
var
|
|
34514
|
+
var import_node_fs41 = require("node:fs");
|
|
34515
|
+
var import_node_path38 = require("node:path");
|
|
34425
34516
|
|
|
34426
34517
|
// src/cross-repo-filing-issue.ts
|
|
34427
34518
|
function crossRepoFilingRetryCommand(prRepo, prNumber) {
|
|
@@ -34587,16 +34678,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
34587
34678
|
}
|
|
34588
34679
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
34589
34680
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
34590
|
-
(0,
|
|
34591
|
-
(0,
|
|
34681
|
+
(0, import_node_fs41.mkdirSync)((0, import_node_path38.dirname)(path2), { recursive: true });
|
|
34682
|
+
(0, import_node_fs41.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
34592
34683
|
`, "utf8");
|
|
34593
34684
|
return path2;
|
|
34594
34685
|
}
|
|
34595
34686
|
function clearPostMergeReconRecovery(cwd, repo, pr2) {
|
|
34596
34687
|
const path2 = postMergeReconStatePath(cwd, repo, pr2);
|
|
34597
|
-
if (!(0,
|
|
34688
|
+
if (!(0, import_node_fs41.existsSync)(path2)) return;
|
|
34598
34689
|
try {
|
|
34599
|
-
(0,
|
|
34690
|
+
(0, import_node_fs41.unlinkSync)(path2);
|
|
34600
34691
|
} catch {
|
|
34601
34692
|
}
|
|
34602
34693
|
}
|
|
@@ -37713,10 +37804,10 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
37713
37804
|
}
|
|
37714
37805
|
|
|
37715
37806
|
// src/doctor-io.ts
|
|
37716
|
-
var
|
|
37807
|
+
var import_node_fs42 = require("node:fs");
|
|
37717
37808
|
var import_node_os18 = require("node:os");
|
|
37718
|
-
var
|
|
37719
|
-
var
|
|
37809
|
+
var import_node_path39 = require("node:path");
|
|
37810
|
+
var import_node_child_process18 = require("node:child_process");
|
|
37720
37811
|
var import_node_util8 = require("node:util");
|
|
37721
37812
|
|
|
37722
37813
|
// src/discard-sink.ts
|
|
@@ -37725,27 +37816,27 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
|
|
|
37725
37816
|
}
|
|
37726
37817
|
|
|
37727
37818
|
// src/doctor-io.ts
|
|
37728
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
37819
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process18.execFile);
|
|
37729
37820
|
function execFileCapture(file, args, opts = {}) {
|
|
37730
37821
|
const sink = nodeDiscardSinkPath();
|
|
37731
|
-
const inFd = (0,
|
|
37732
|
-
const errFd = (0,
|
|
37822
|
+
const inFd = (0, import_node_fs42.openSync)(sink, "r");
|
|
37823
|
+
const errFd = (0, import_node_fs42.openSync)(sink, "w");
|
|
37733
37824
|
try {
|
|
37734
|
-
return (0,
|
|
37825
|
+
return (0, import_node_child_process18.execFileSync)(file, args, {
|
|
37735
37826
|
...opts,
|
|
37736
37827
|
encoding: "utf8",
|
|
37737
37828
|
stdio: [inFd, "pipe", errFd]
|
|
37738
37829
|
});
|
|
37739
37830
|
} finally {
|
|
37740
|
-
(0,
|
|
37741
|
-
(0,
|
|
37831
|
+
(0, import_node_fs42.closeSync)(inFd);
|
|
37832
|
+
(0, import_node_fs42.closeSync)(errFd);
|
|
37742
37833
|
}
|
|
37743
37834
|
}
|
|
37744
37835
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
37745
37836
|
function installedClaudePluginVersion() {
|
|
37746
37837
|
try {
|
|
37747
37838
|
const file = JSON.parse(
|
|
37748
|
-
(0,
|
|
37839
|
+
(0, import_node_fs42.readFileSync)((0, import_node_path39.join)((0, import_node_os18.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
37749
37840
|
);
|
|
37750
37841
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
37751
37842
|
if (versions.length === 0) return void 0;
|
|
@@ -37756,7 +37847,7 @@ function installedClaudePluginVersion() {
|
|
|
37756
37847
|
}
|
|
37757
37848
|
function manifestVersion(path2) {
|
|
37758
37849
|
try {
|
|
37759
|
-
const manifest = JSON.parse((0,
|
|
37850
|
+
const manifest = JSON.parse((0, import_node_fs42.readFileSync)(path2, "utf8"));
|
|
37760
37851
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
37761
37852
|
} catch {
|
|
37762
37853
|
return void 0;
|
|
@@ -37765,12 +37856,12 @@ function manifestVersion(path2) {
|
|
|
37765
37856
|
function readHermesPluginEvidence(env = process.env) {
|
|
37766
37857
|
const host = hermesConfigRoot(env);
|
|
37767
37858
|
const root = hermesPluginRoot(env);
|
|
37768
|
-
const installRecordPresent = (0,
|
|
37769
|
-
const manifestPath = (0,
|
|
37859
|
+
const installRecordPresent = (0, import_node_fs42.existsSync)(root);
|
|
37860
|
+
const manifestPath = (0, import_node_path39.join)(root, "plugin.yaml");
|
|
37770
37861
|
let installedVersion;
|
|
37771
37862
|
let manifest = "missing";
|
|
37772
37863
|
try {
|
|
37773
|
-
const text = (0,
|
|
37864
|
+
const text = (0, import_node_fs42.readFileSync)(manifestPath, "utf8");
|
|
37774
37865
|
let version;
|
|
37775
37866
|
try {
|
|
37776
37867
|
const parsed = JSON.parse(text).version;
|
|
@@ -37784,20 +37875,20 @@ function readHermesPluginEvidence(env = process.env) {
|
|
|
37784
37875
|
if (version) {
|
|
37785
37876
|
installedVersion = version;
|
|
37786
37877
|
manifest = "valid";
|
|
37787
|
-
} else if ((0,
|
|
37878
|
+
} else if ((0, import_node_fs42.existsSync)(manifestPath)) manifest = "invalid";
|
|
37788
37879
|
} catch {
|
|
37789
|
-
if ((0,
|
|
37880
|
+
if ((0, import_node_fs42.existsSync)(manifestPath)) manifest = "invalid";
|
|
37790
37881
|
}
|
|
37791
37882
|
let skills = false;
|
|
37792
37883
|
try {
|
|
37793
|
-
skills = (0,
|
|
37884
|
+
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
37885
|
} catch {
|
|
37795
37886
|
}
|
|
37796
37887
|
return {
|
|
37797
|
-
hostPresent: (0,
|
|
37888
|
+
hostPresent: (0, import_node_fs42.existsSync)(host),
|
|
37798
37889
|
installRecordPresent,
|
|
37799
37890
|
manifest,
|
|
37800
|
-
payloadPresent: (0,
|
|
37891
|
+
payloadPresent: (0, import_node_fs42.existsSync)((0, import_node_path39.join)(root, "__init__.py")) && skills && manifest === "valid",
|
|
37801
37892
|
...installedVersion ? { installedVersion } : {}
|
|
37802
37893
|
};
|
|
37803
37894
|
}
|
|
@@ -37805,7 +37896,7 @@ function installedSurfacePluginVersion(surface) {
|
|
|
37805
37896
|
const token = surfaceToken(surface);
|
|
37806
37897
|
if (token === "kilo") {
|
|
37807
37898
|
try {
|
|
37808
|
-
const stamp = (0,
|
|
37899
|
+
const stamp = (0, import_node_fs42.readFileSync)((0, import_node_path39.join)((0, import_node_os18.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
37809
37900
|
return stamp || void 0;
|
|
37810
37901
|
} catch {
|
|
37811
37902
|
return void 0;
|
|
@@ -37813,13 +37904,13 @@ function installedSurfacePluginVersion(surface) {
|
|
|
37813
37904
|
}
|
|
37814
37905
|
if (token === "hermes") return readHermesPluginEvidence().installedVersion;
|
|
37815
37906
|
if (token === "cursor") {
|
|
37816
|
-
return manifestVersion((0,
|
|
37907
|
+
return manifestVersion((0, import_node_path39.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
37817
37908
|
}
|
|
37818
37909
|
if (token === "jervcode") {
|
|
37819
37910
|
return installedJervCodePackageVersion();
|
|
37820
37911
|
}
|
|
37821
37912
|
if (token === "kimi") {
|
|
37822
|
-
return manifestVersion((0,
|
|
37913
|
+
return manifestVersion((0, import_node_path39.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
37823
37914
|
}
|
|
37824
37915
|
if (token === "claude") return installedClaudePluginVersion();
|
|
37825
37916
|
if (token !== "codex") return void 0;
|
|
@@ -37853,13 +37944,13 @@ function worktreeRootSync() {
|
|
|
37853
37944
|
}
|
|
37854
37945
|
var gitignorePath = () => {
|
|
37855
37946
|
const root = worktreeRootSync();
|
|
37856
|
-
return root === null ? null : (0,
|
|
37947
|
+
return root === null ? null : (0, import_node_path39.join)(root, ".gitignore");
|
|
37857
37948
|
};
|
|
37858
37949
|
function readGitignore() {
|
|
37859
37950
|
const path2 = gitignorePath();
|
|
37860
37951
|
if (path2 === null) return null;
|
|
37861
37952
|
try {
|
|
37862
|
-
return (0,
|
|
37953
|
+
return (0, import_node_fs42.readFileSync)(path2, "utf8");
|
|
37863
37954
|
} catch {
|
|
37864
37955
|
return null;
|
|
37865
37956
|
}
|
|
@@ -37868,14 +37959,14 @@ function writeGitignore(content) {
|
|
|
37868
37959
|
const path2 = gitignorePath();
|
|
37869
37960
|
if (path2 === null) return false;
|
|
37870
37961
|
try {
|
|
37871
|
-
(0,
|
|
37962
|
+
(0, import_node_fs42.writeFileSync)(path2, content, "utf8");
|
|
37872
37963
|
return true;
|
|
37873
37964
|
} catch {
|
|
37874
37965
|
return false;
|
|
37875
37966
|
}
|
|
37876
37967
|
}
|
|
37877
37968
|
function lineEndingState(root) {
|
|
37878
|
-
const attributesPresent = (0,
|
|
37969
|
+
const attributesPresent = (0, import_node_fs42.existsSync)((0, import_node_path39.join)(root, ".gitattributes"));
|
|
37879
37970
|
try {
|
|
37880
37971
|
const output = execFileCapture("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
|
|
37881
37972
|
windowsHide: true
|
|
@@ -37942,8 +38033,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
37942
38033
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
37943
38034
|
try {
|
|
37944
38035
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
37945
|
-
if (!hostsPath || !(0,
|
|
37946
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
38036
|
+
if (!hostsPath || !(0, import_node_fs43.existsSync)(hostsPath)) return void 0;
|
|
38037
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs43.readFileSync)(hostsPath, "utf8")));
|
|
37947
38038
|
} catch {
|
|
37948
38039
|
return void 0;
|
|
37949
38040
|
}
|
|
@@ -37951,7 +38042,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
37951
38042
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
37952
38043
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
37953
38044
|
function envHealLockPath(home) {
|
|
37954
|
-
return (0,
|
|
38045
|
+
return (0, import_node_path40.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
37955
38046
|
}
|
|
37956
38047
|
async function withEnvHealLock(what, run) {
|
|
37957
38048
|
try {
|
|
@@ -38088,7 +38179,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38088
38179
|
);
|
|
38089
38180
|
const result = applyPluginCachePlan(
|
|
38090
38181
|
plan,
|
|
38091
|
-
(p) => (0,
|
|
38182
|
+
(p) => (0, import_node_fs43.rmSync)(p, { recursive: true }),
|
|
38092
38183
|
stagingApplyFsGuard(configRoot)
|
|
38093
38184
|
);
|
|
38094
38185
|
return {
|
|
@@ -38126,7 +38217,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38126
38217
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
38127
38218
|
// get a permanent — demanding an artifact it never asked for.
|
|
38128
38219
|
docsIndexState: (root) => {
|
|
38129
|
-
if (!(0,
|
|
38220
|
+
if (!(0, import_node_fs43.existsSync)((0, import_node_path40.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
38130
38221
|
const real = createDocsIndexDeps(root);
|
|
38131
38222
|
let docs2;
|
|
38132
38223
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -38135,7 +38226,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
38135
38226
|
},
|
|
38136
38227
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
38137
38228
|
healDocsIndex: (root) => {
|
|
38138
|
-
if (!(0,
|
|
38229
|
+
if (!(0, import_node_fs43.existsSync)((0, import_node_path40.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
38139
38230
|
const real = createDocsIndexDeps(root);
|
|
38140
38231
|
let docs2;
|
|
38141
38232
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -38238,7 +38329,9 @@ async function requireFreshTrainCli(commandName) {
|
|
|
38238
38329
|
const converge = await selfConvergeTrainCli({
|
|
38239
38330
|
currentVersion: resolveClientVersion(),
|
|
38240
38331
|
releasedVersion: report.releasedVersion,
|
|
38241
|
-
commandName
|
|
38332
|
+
commandName,
|
|
38333
|
+
// The typed, house-prefixed argv — never the spliced process.argv (#5813).
|
|
38334
|
+
argv: INVOKED_ARGV
|
|
38242
38335
|
});
|
|
38243
38336
|
if (converge.kind === "failed") {
|
|
38244
38337
|
throw new Error(`${commandName}: ${converge.error}. ${staleTrainCliMessage(report, commandName)}`);
|
|
@@ -38333,7 +38426,10 @@ function positionalTargetForm(cmd, opts = {}) {
|
|
|
38333
38426
|
const args = cmd.registeredArguments ?? [];
|
|
38334
38427
|
const first = args[0];
|
|
38335
38428
|
if (!first) return void 0;
|
|
38336
|
-
return formatPositionalTarget(canonicalPathFor(commandPath2(cmd)) ?? commandPath2(cmd), first.name(),
|
|
38429
|
+
return formatPositionalTarget(canonicalPathFor(commandPath2(cmd)) ?? commandPath2(cmd), first.name(), {
|
|
38430
|
+
...opts,
|
|
38431
|
+
ownFlags: commandOwnLongFlags(cmd)
|
|
38432
|
+
});
|
|
38337
38433
|
}
|
|
38338
38434
|
function unknownCommandCandidates(parent, allPaths) {
|
|
38339
38435
|
if (!parent) return [...allPaths];
|
|
@@ -38428,6 +38524,7 @@ function envelopeAwareWriteErr(str) {
|
|
|
38428
38524
|
if (unknownFlagJsonHandled && argvWantsJson2()) return;
|
|
38429
38525
|
process.stderr.write(str);
|
|
38430
38526
|
}
|
|
38527
|
+
var INVOKED_ARGV = process.argv.slice(2);
|
|
38431
38528
|
var program2 = new Command();
|
|
38432
38529
|
program2.name("mmi-cli").description("MMI Future Hub CLI \u2014 the org control plane for agentic coding.").version(resolveClientVersion()).configureOutput({ writeErr: envelopeAwareWriteErr }).showHelpAfterError(PARSE_HINT_SENTINEL);
|
|
38433
38530
|
program2.addHelpText(
|
|
@@ -38456,19 +38553,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
38456
38553
|
});
|
|
38457
38554
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
38458
38555
|
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,
|
|
38556
|
+
const path2 = (0, import_node_path40.join)(process.cwd(), ".gitignore");
|
|
38557
|
+
const current = (0, import_node_fs43.existsSync)(path2) ? (0, import_node_fs43.readFileSync)(path2, "utf8") : null;
|
|
38461
38558
|
const plan = planManagedGitignore(current);
|
|
38462
38559
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
38463
38560
|
if (opts.json) {
|
|
38464
|
-
if (opts.write && plan.changed) (0,
|
|
38561
|
+
if (opts.write && plan.changed) (0, import_node_fs43.writeFileSync)(path2, plan.content, "utf8");
|
|
38465
38562
|
console.log(JSON.stringify(plan, null, 2));
|
|
38466
38563
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
38467
38564
|
return;
|
|
38468
38565
|
}
|
|
38469
38566
|
if (opts.write) {
|
|
38470
38567
|
if (plan.changed) {
|
|
38471
|
-
(0,
|
|
38568
|
+
(0, import_node_fs43.writeFileSync)(path2, plan.content, "utf8");
|
|
38472
38569
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
38473
38570
|
} else {
|
|
38474
38571
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -38654,7 +38751,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
38654
38751
|
try {
|
|
38655
38752
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
38656
38753
|
if (o.repo) args.push("--repo", o.repo);
|
|
38657
|
-
spawnDetachedSelf(args, { spawn:
|
|
38754
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process19.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
38658
38755
|
} catch {
|
|
38659
38756
|
}
|
|
38660
38757
|
}
|
|
@@ -39044,7 +39141,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
39044
39141
|
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
39142
|
if (o.secretsFile) {
|
|
39046
39143
|
try {
|
|
39047
|
-
vars.push(`secrets=${(0,
|
|
39144
|
+
vars.push(`secrets=${(0, import_node_fs43.readFileSync)(o.secretsFile, "utf8")}`);
|
|
39048
39145
|
} catch (e) {
|
|
39049
39146
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
39050
39147
|
}
|
|
@@ -39869,11 +39966,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
39869
39966
|
}
|
|
39870
39967
|
});
|
|
39871
39968
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
39872
|
-
const wfDir = (0,
|
|
39873
|
-
if (!(0,
|
|
39874
|
-
return (0,
|
|
39969
|
+
const wfDir = (0, import_node_path40.join)(cwd, ".github", "workflows");
|
|
39970
|
+
if (!(0, import_node_fs43.existsSync)(wfDir)) return [];
|
|
39971
|
+
return (0, import_node_fs43.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
39875
39972
|
try {
|
|
39876
|
-
return workflowReportsPrChecks((0,
|
|
39973
|
+
return workflowReportsPrChecks((0, import_node_fs43.readFileSync)((0, import_node_path40.join)(wfDir, name), "utf8"));
|
|
39877
39974
|
} catch {
|
|
39878
39975
|
return true;
|
|
39879
39976
|
}
|
|
@@ -39939,16 +40036,16 @@ function ciAuditDeps() {
|
|
|
39939
40036
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
39940
40037
|
readSeedFile: (path2) => {
|
|
39941
40038
|
if (!root) return null;
|
|
39942
|
-
const fullPath = (0,
|
|
39943
|
-
return (0,
|
|
40039
|
+
const fullPath = (0, import_node_path40.join)(root, path2);
|
|
40040
|
+
return (0, import_node_fs43.existsSync)(fullPath) ? (0, import_node_fs43.readFileSync)(fullPath, "utf8") : null;
|
|
39944
40041
|
}
|
|
39945
40042
|
};
|
|
39946
40043
|
}
|
|
39947
40044
|
function hubRoot() {
|
|
39948
|
-
const fromPkg = (0,
|
|
40045
|
+
const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
|
|
39949
40046
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
39950
|
-
if ((0,
|
|
39951
|
-
if ((0,
|
|
40047
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(fromPkg, marker))) return fromPkg;
|
|
40048
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(process.cwd(), marker))) return process.cwd();
|
|
39952
40049
|
return null;
|
|
39953
40050
|
}
|
|
39954
40051
|
async function waitLoopCorePool(label) {
|
|
@@ -40220,9 +40317,18 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
40220
40317
|
else printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
|
|
40221
40318
|
if (result.status === "failed") process.exitCode = 1;
|
|
40222
40319
|
});
|
|
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) => {
|
|
40320
|
+
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
40321
|
const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
|
|
40225
40322
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
40323
|
+
if (o.disableAuto) {
|
|
40324
|
+
await execFileP2("gh", buildPrDisableAutoArgs({ number, repoArgs }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e) => {
|
|
40325
|
+
const note = timeoutKillNote(e, GH_MUTATION_TIMEOUT_MS);
|
|
40326
|
+
if (note) throw new Error(`gh pr merge ${number} --disable-auto: ${note}`);
|
|
40327
|
+
throw e;
|
|
40328
|
+
});
|
|
40329
|
+
console.log(JSON.stringify({ mergeStatus: "auto-merge-disabled", pr: number, ...o.repo ? { repo: o.repo } : {} }));
|
|
40330
|
+
return;
|
|
40331
|
+
}
|
|
40226
40332
|
const repoForPostCleanup = await resolveRepo(o.repo) ?? o.repo;
|
|
40227
40333
|
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
40334
|
if (!isGitHubRateLimitError(e) || !repoForPostCleanup) throw e;
|
|
@@ -40249,7 +40355,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40249
40355
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
40250
40356
|
closingGuardInput,
|
|
40251
40357
|
method === "--squash",
|
|
40252
|
-
o.squashBodyFile ? (0,
|
|
40358
|
+
o.squashBodyFile ? (0, import_node_fs43.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
40253
40359
|
);
|
|
40254
40360
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
40255
40361
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -40263,7 +40369,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40263
40369
|
return;
|
|
40264
40370
|
}
|
|
40265
40371
|
if (closingGuardVerdict.message) console.warn(closingGuardVerdict.message);
|
|
40266
|
-
if (prMeta.state !== "MERGED") {
|
|
40372
|
+
if (prMeta.state !== "MERGED" && !o.preserveWorktree) {
|
|
40267
40373
|
if (!repoForPostCleanup) throw new Error("pr merge: repository is unreadable; cannot prove remote branch preservation");
|
|
40268
40374
|
const repoSettings = await defaultGitHubClient().rest("GET", `repos/${repoForPostCleanup}`);
|
|
40269
40375
|
assertRemoteBranchPreservedOnMerge(repoForPostCleanup, repoSettings.delete_branch_on_merge);
|
|
@@ -40338,7 +40444,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40338
40444
|
}
|
|
40339
40445
|
if (!repoForPostCleanup) throw e;
|
|
40340
40446
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
40341
|
-
const commitMessage = bodyFile ? (0,
|
|
40447
|
+
const commitMessage = bodyFile ? (0, import_node_fs43.readFileSync)(bodyFile, "utf8") : void 0;
|
|
40342
40448
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
40343
40449
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
40344
40450
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -40427,7 +40533,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40427
40533
|
preserveWorktree: o.preserveWorktree,
|
|
40428
40534
|
gcAcknowledged: o.gc,
|
|
40429
40535
|
expectedHeadOid: headRefOid,
|
|
40430
|
-
pathExists: (p) => (0,
|
|
40536
|
+
pathExists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
40431
40537
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout
|
|
40432
40538
|
});
|
|
40433
40539
|
} catch (e) {
|
|
@@ -40715,7 +40821,7 @@ for (const commandName of ["rcand", "release"]) {
|
|
|
40715
40821
|
{ 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
40822
|
{ 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
40823
|
{ 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
|
|
40824
|
+
{ flags: "--retry-publish <run-id>", description: "with --apply, retry one proven failed or cancelled Hub publish release run (#3949/#5797)" }
|
|
40719
40825
|
];
|
|
40720
40826
|
for (const f of RELEASE_ONLY_FLAGS) {
|
|
40721
40827
|
if (commandName === "release") {
|
|
@@ -41014,8 +41120,7 @@ ${r.repo}: applied=[${r.applied.join("; ")}] skipped=[${r.skipped.join("; ")}]${
|
|
|
41014
41120
|
console.log("\nDry-run \u2014 re-run with --apply to patch merge settings and activate product rulesets (master-admin).");
|
|
41015
41121
|
}
|
|
41016
41122
|
}
|
|
41017
|
-
|
|
41018
|
-
if (!audit.ok || applyFailed) process.exitCode = 1;
|
|
41123
|
+
if (ciReconcileExitFailed({ apply: o.apply === true, audit, applyResults })) process.exitCode = 1;
|
|
41019
41124
|
});
|
|
41020
41125
|
registerBootstrapCommands(program2);
|
|
41021
41126
|
var access = program2.command("access").description("org access audit (read-only)");
|
|
@@ -41064,12 +41169,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
41064
41169
|
targets = resolution.targets;
|
|
41065
41170
|
}
|
|
41066
41171
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
41067
|
-
const fileMatrix = (0,
|
|
41172
|
+
const fileMatrix = (0, import_node_fs43.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs43.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
41068
41173
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
41069
41174
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
41070
|
-
const fileContracts = (0,
|
|
41175
|
+
const fileContracts = (0, import_node_fs43.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs43.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
41071
41176
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
41072
|
-
const sanctioned = (0,
|
|
41177
|
+
const sanctioned = (0, import_node_fs43.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs43.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
41073
41178
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
41074
41179
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
41075
41180
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -41101,16 +41206,16 @@ function directoryBytes(path2) {
|
|
|
41101
41206
|
let total = 0;
|
|
41102
41207
|
let entries;
|
|
41103
41208
|
try {
|
|
41104
|
-
entries = (0,
|
|
41209
|
+
entries = (0, import_node_fs43.readdirSync)(path2, { withFileTypes: true });
|
|
41105
41210
|
} catch {
|
|
41106
41211
|
return 0;
|
|
41107
41212
|
}
|
|
41108
41213
|
for (const entry of entries) {
|
|
41109
|
-
const child2 = (0,
|
|
41214
|
+
const child2 = (0, import_node_path40.join)(path2, entry.name);
|
|
41110
41215
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
41111
41216
|
else {
|
|
41112
41217
|
try {
|
|
41113
|
-
total += (0,
|
|
41218
|
+
total += (0, import_node_fs43.statSync)(child2).size;
|
|
41114
41219
|
} catch {
|
|
41115
41220
|
}
|
|
41116
41221
|
}
|
|
@@ -41118,25 +41223,25 @@ function directoryBytes(path2) {
|
|
|
41118
41223
|
return total;
|
|
41119
41224
|
}
|
|
41120
41225
|
function listDirEntries(dir) {
|
|
41121
|
-
return (0,
|
|
41226
|
+
return (0, import_node_fs43.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
41122
41227
|
}
|
|
41123
41228
|
function readInstalledPluginRefs(configRoot) {
|
|
41124
41229
|
const p = installedPluginsPathForConfig(configRoot);
|
|
41125
|
-
if (!(0,
|
|
41230
|
+
if (!(0, import_node_fs43.existsSync)(p)) return [];
|
|
41126
41231
|
try {
|
|
41127
|
-
return installedPluginPaths((0,
|
|
41232
|
+
return installedPluginPaths((0, import_node_fs43.readFileSync)(p, "utf8"));
|
|
41128
41233
|
} catch {
|
|
41129
41234
|
return null;
|
|
41130
41235
|
}
|
|
41131
41236
|
}
|
|
41132
41237
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
41133
41238
|
return {
|
|
41134
|
-
exists: (p) => (0,
|
|
41135
|
-
listVersionDirs: (root) => (0,
|
|
41239
|
+
exists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
41240
|
+
listVersionDirs: (root) => (0, import_node_fs43.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
41136
41241
|
dirBytes,
|
|
41137
|
-
listStagingDirs: (root) => (0,
|
|
41242
|
+
listStagingDirs: (root) => (0, import_node_fs43.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
41138
41243
|
try {
|
|
41139
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
41244
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path40.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs43.statSync)(p).mtimeMs) };
|
|
41140
41245
|
} catch {
|
|
41141
41246
|
return { name: d.name, mtimeMs: Date.now() };
|
|
41142
41247
|
}
|
|
@@ -41150,10 +41255,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
41150
41255
|
return {
|
|
41151
41256
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
41152
41257
|
mtimeMs: (name) => {
|
|
41153
|
-
const p = (0,
|
|
41154
|
-
if (!(0,
|
|
41258
|
+
const p = (0, import_node_path40.join)(stagingRoot, name);
|
|
41259
|
+
if (!(0, import_node_fs43.existsSync)(p)) return null;
|
|
41155
41260
|
try {
|
|
41156
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
41261
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs43.statSync)(q).mtimeMs);
|
|
41157
41262
|
} catch {
|
|
41158
41263
|
return null;
|
|
41159
41264
|
}
|
|
@@ -41179,7 +41284,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
41179
41284
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
41180
41285
|
);
|
|
41181
41286
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
41182
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
41287
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs43.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
41183
41288
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
41184
41289
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
41185
41290
|
else console.log(renderPluginCachePlan(plan, result));
|