@mutmutco/cli 4.1.9 → 4.1.10
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 +559 -96
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5860,7 +5860,7 @@ var program = new Command();
|
|
|
5860
5860
|
|
|
5861
5861
|
// src/index.ts
|
|
5862
5862
|
var import_promises8 = require("node:fs/promises");
|
|
5863
|
-
var
|
|
5863
|
+
var import_node_fs48 = require("node:fs");
|
|
5864
5864
|
var import_node_child_process21 = require("node:child_process");
|
|
5865
5865
|
init_cli_shared();
|
|
5866
5866
|
|
|
@@ -6902,7 +6902,7 @@ function commandLadderHint() {
|
|
|
6902
6902
|
}
|
|
6903
6903
|
|
|
6904
6904
|
// src/index.ts
|
|
6905
|
-
var
|
|
6905
|
+
var import_node_path43 = require("node:path");
|
|
6906
6906
|
|
|
6907
6907
|
// src/merge-ci-policy.ts
|
|
6908
6908
|
function resolveMergeCiPolicy(input) {
|
|
@@ -13152,10 +13152,10 @@ var rollout_plan_default = {
|
|
|
13152
13152
|
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)."
|
|
13153
13153
|
},
|
|
13154
13154
|
baseline: {
|
|
13155
|
-
version: "4.1.
|
|
13156
|
-
tag: "v4.1.
|
|
13157
|
-
commit: "
|
|
13158
|
-
npm: "@mutmutco/cli@4.1.
|
|
13155
|
+
version: "4.1.10",
|
|
13156
|
+
tag: "v4.1.10",
|
|
13157
|
+
commit: "2a913c6e917e",
|
|
13158
|
+
npm: "@mutmutco/cli@4.1.10"
|
|
13159
13159
|
},
|
|
13160
13160
|
exitCriterion: "fleet-n-of-n",
|
|
13161
13161
|
hubOnlyShortcut: "forbidden",
|
|
@@ -13172,14 +13172,14 @@ var rollout_plan_default = {
|
|
|
13172
13172
|
repo: "mutmutco/mmi-hub",
|
|
13173
13173
|
role: "canary",
|
|
13174
13174
|
schedule: "train",
|
|
13175
|
-
v3Target: "v4.1.
|
|
13175
|
+
v3Target: "v4.1.10"
|
|
13176
13176
|
}
|
|
13177
13177
|
],
|
|
13178
13178
|
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.",
|
|
13179
13179
|
rollback: {
|
|
13180
13180
|
independent: true,
|
|
13181
|
-
mechanism: "npm dist-tag latest -> 4.1.
|
|
13182
|
-
v3Target: "v4.1.
|
|
13181
|
+
mechanism: "npm dist-tag latest -> 4.1.10 and redeploy the Hub Lambda from tag v4.1.10 (2a913c6e917e); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
13182
|
+
v3Target: "v4.1.10 (@mutmutco/cli@4.1.10, tag commit 2a913c6e917e \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
13183
13183
|
}
|
|
13184
13184
|
},
|
|
13185
13185
|
{
|
|
@@ -16909,6 +16909,7 @@ async function preflight(deps, ctx, stage, meta) {
|
|
|
16909
16909
|
throw new Error(`${ctx.repo} is not Hub-deployed (deployModel=none) \u2014 the release train does not apply; use the project's own release path`);
|
|
16910
16910
|
}
|
|
16911
16911
|
await deps.runSelf(["secrets", "preflight", "--stage", stage, "--repo", ctx.repo]);
|
|
16912
|
+
await assertNpmMajorPreflightFromWorkflows(deps, ctx.repo);
|
|
16912
16913
|
await assertActionsJobsCanStart(deps, ctx.repo);
|
|
16913
16914
|
enforceGateBudget(deps, ctx.repo);
|
|
16914
16915
|
if (model === "hub-serverless") {
|
|
@@ -17161,10 +17162,49 @@ function resolveGreenGateNpmFromGateWorkflows(files) {
|
|
|
17161
17162
|
const pin = preferGateNodePin(files.flatMap((file) => parseGateNodeVersionPins(file.body)));
|
|
17162
17163
|
return pin ? bundledNpmForNodePin(pin) : void 0;
|
|
17163
17164
|
}
|
|
17165
|
+
function parsePublishWorkflowNpmPin(workflowBody) {
|
|
17166
|
+
const match = /(?:^|[^\w])npm@(\d+\.\d+\.\d+)/m.exec(workflowBody);
|
|
17167
|
+
return match?.[1];
|
|
17168
|
+
}
|
|
17169
|
+
function resolveExpectedCiNpmFromWorkflows(gateFiles, allFiles) {
|
|
17170
|
+
const fromGate = resolveGreenGateNpmFromGateWorkflows(gateFiles);
|
|
17171
|
+
if (fromGate) return fromGate;
|
|
17172
|
+
const files = allFiles ?? gateFiles;
|
|
17173
|
+
if (!files?.length) return void 0;
|
|
17174
|
+
const publish = files.find((f) => /(?:^|\/)publish\.ya?ml$/i.test(f.path));
|
|
17175
|
+
return publish ? parsePublishWorkflowNpmPin(publish.body) : void 0;
|
|
17176
|
+
}
|
|
17164
17177
|
function npmMajor(version) {
|
|
17165
17178
|
const match = /^(\d+)/.exec(version.trim());
|
|
17166
17179
|
return match ? Number(match[1]) : void 0;
|
|
17167
17180
|
}
|
|
17181
|
+
function assertNpmMajorPreflight(opts) {
|
|
17182
|
+
const expectedNpm = opts.expectedNpm?.trim();
|
|
17183
|
+
if (!expectedNpm) return;
|
|
17184
|
+
const localNpm = opts.localNpm.trim();
|
|
17185
|
+
const localMajor = npmMajor(localNpm);
|
|
17186
|
+
const expectedMajor = npmMajor(expectedNpm);
|
|
17187
|
+
if (localMajor === void 0 || expectedMajor === void 0) return;
|
|
17188
|
+
if (localMajor === expectedMajor) return;
|
|
17189
|
+
throw new Error(
|
|
17190
|
+
`${opts.repo}: Step 0a npm-major preflight failed \u2014 local npm ${localNpm} (major ${localMajor}) \u2260 CI npm ${expectedNpm} (major ${expectedMajor}). Align local npm before --apply: \`npm install -g npm@${expectedNpm}\`. This is an npm-major mismatch (#5666/#5616/#4578), not a lockfile or publish-surface problem. When gate.yml has no bundled npm pin, read the publish workflow npm pin; otherwise compare \`npm -v\` with the green gate log's toolchain-preflight line (#3446).`
|
|
17191
|
+
);
|
|
17192
|
+
}
|
|
17193
|
+
async function assertNpmMajorPreflightFromWorkflows(deps, repo) {
|
|
17194
|
+
const gateFiles = (deps.readGateWorkflows ?? readLocalGateWorkflows)();
|
|
17195
|
+
const allFiles = (deps.readWorkflows ?? readLocalWorkflows)();
|
|
17196
|
+
let localNpm;
|
|
17197
|
+
try {
|
|
17198
|
+
localNpm = clean2(await deps.run("npm", ["-v"]));
|
|
17199
|
+
} catch {
|
|
17200
|
+
return;
|
|
17201
|
+
}
|
|
17202
|
+
assertNpmMajorPreflight({
|
|
17203
|
+
repo,
|
|
17204
|
+
localNpm,
|
|
17205
|
+
expectedNpm: resolveExpectedCiNpmFromWorkflows(gateFiles, allFiles)
|
|
17206
|
+
});
|
|
17207
|
+
}
|
|
17168
17208
|
function subprocessFailureDetail(e) {
|
|
17169
17209
|
if (!(e instanceof Error)) return String(e);
|
|
17170
17210
|
const err = e;
|
|
@@ -17211,7 +17251,10 @@ async function verifyPublishDryRun(deps, ctx, meta, deployModel) {
|
|
|
17211
17251
|
localNpm = clean2(await deps.run("npm", ["-v"]));
|
|
17212
17252
|
} catch {
|
|
17213
17253
|
}
|
|
17214
|
-
const gateNpm =
|
|
17254
|
+
const gateNpm = resolveExpectedCiNpmFromWorkflows(
|
|
17255
|
+
(deps.readGateWorkflows ?? readLocalGateWorkflows)(),
|
|
17256
|
+
(deps.readWorkflows ?? readLocalWorkflows)()
|
|
17257
|
+
);
|
|
17215
17258
|
throw classifyPublishDryRunFailure({
|
|
17216
17259
|
repo: ctx.repo,
|
|
17217
17260
|
publishDir,
|
|
@@ -33415,6 +33458,396 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
33415
33458
|
}
|
|
33416
33459
|
}
|
|
33417
33460
|
|
|
33461
|
+
// src/worktree-merge-cleanup.ts
|
|
33462
|
+
var import_node_fs42 = require("node:fs");
|
|
33463
|
+
init_cli_shared();
|
|
33464
|
+
|
|
33465
|
+
// src/worktree-evidence-archive.ts
|
|
33466
|
+
var import_node_fs41 = require("node:fs");
|
|
33467
|
+
var import_node_path38 = require("node:path");
|
|
33468
|
+
var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
|
|
33469
|
+
function sanitizeArchiveSegment(value, max = 80) {
|
|
33470
|
+
const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
33471
|
+
return (scrubbed || "unknown").slice(0, max);
|
|
33472
|
+
}
|
|
33473
|
+
function resolveArtifactRunScope(env = process.env) {
|
|
33474
|
+
for (const key of JERV_ARTIFACT_RUN_SCOPE_ENV_VARS) {
|
|
33475
|
+
const value = env[key]?.trim();
|
|
33476
|
+
if (value) return sanitizeArchiveSegment(value);
|
|
33477
|
+
}
|
|
33478
|
+
return "no-run";
|
|
33479
|
+
}
|
|
33480
|
+
function defaultIsDirectory(path2) {
|
|
33481
|
+
try {
|
|
33482
|
+
return (0, import_node_fs41.lstatSync)(path2).isDirectory();
|
|
33483
|
+
} catch {
|
|
33484
|
+
return false;
|
|
33485
|
+
}
|
|
33486
|
+
}
|
|
33487
|
+
function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
33488
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
33489
|
+
const isDirectory = deps.isDirectory ?? defaultIsDirectory;
|
|
33490
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
|
|
33491
|
+
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
33492
|
+
(0, import_node_fs41.mkdirSync)(path2, { recursive: true });
|
|
33493
|
+
});
|
|
33494
|
+
const env = deps.env ?? process.env;
|
|
33495
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
33496
|
+
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
33497
|
+
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
33498
|
+
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
33499
|
+
const source = (0, import_node_path38.join)(args.worktreePath, ".jerv");
|
|
33500
|
+
if (!exists(source)) return { status: "absent" };
|
|
33501
|
+
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
33502
|
+
const runScope = resolveArtifactRunScope(env);
|
|
33503
|
+
const branchSlug = sanitizeArchiveSegment(args.branch.replace(/[/\\]+/g, "-"), 40);
|
|
33504
|
+
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
33505
|
+
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
33506
|
+
try {
|
|
33507
|
+
mkdirp((0, import_node_path38.dirname)(dest));
|
|
33508
|
+
copyDir(source, dest);
|
|
33509
|
+
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
33510
|
+
return { status: "archived", path: dest, runScope };
|
|
33511
|
+
} catch (e) {
|
|
33512
|
+
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
33513
|
+
}
|
|
33514
|
+
}
|
|
33515
|
+
function defaultStat(path2) {
|
|
33516
|
+
const st = (0, import_node_fs41.statSync)(path2);
|
|
33517
|
+
return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
|
|
33518
|
+
}
|
|
33519
|
+
function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
33520
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
33521
|
+
const stat4 = deps.stat ?? defaultStat;
|
|
33522
|
+
const readdir2 = deps.readdir ?? import_node_fs41.readdirSync;
|
|
33523
|
+
const tmpRoot = (0, import_node_path38.join)(worktreePath, "tmp");
|
|
33524
|
+
if (!exists(tmpRoot)) return [];
|
|
33525
|
+
const entries = [];
|
|
33526
|
+
const walk2 = (dir) => {
|
|
33527
|
+
let names;
|
|
33528
|
+
try {
|
|
33529
|
+
names = readdir2(dir);
|
|
33530
|
+
} catch {
|
|
33531
|
+
return;
|
|
33532
|
+
}
|
|
33533
|
+
for (const name of names) {
|
|
33534
|
+
const full = (0, import_node_path38.join)(dir, name);
|
|
33535
|
+
let st;
|
|
33536
|
+
try {
|
|
33537
|
+
st = stat4(full);
|
|
33538
|
+
} catch {
|
|
33539
|
+
continue;
|
|
33540
|
+
}
|
|
33541
|
+
const relPath = (0, import_node_path38.relative)(worktreePath, full).replace(/\\/g, "/");
|
|
33542
|
+
if (st.isDirectory()) {
|
|
33543
|
+
if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
|
|
33544
|
+
walk2(full);
|
|
33545
|
+
continue;
|
|
33546
|
+
}
|
|
33547
|
+
if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: st.size, mtimeMs: st.mtimeMs });
|
|
33548
|
+
}
|
|
33549
|
+
};
|
|
33550
|
+
walk2(tmpRoot);
|
|
33551
|
+
entries.sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
33552
|
+
return entries;
|
|
33553
|
+
}
|
|
33554
|
+
function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
33555
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
33556
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
|
|
33557
|
+
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
33558
|
+
(0, import_node_fs41.mkdirSync)(path2, { recursive: true });
|
|
33559
|
+
});
|
|
33560
|
+
const env = deps.env ?? process.env;
|
|
33561
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
33562
|
+
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
33563
|
+
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
33564
|
+
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
33565
|
+
const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
|
|
33566
|
+
const source = (0, import_node_path38.join)(args.worktreePath, "tmp");
|
|
33567
|
+
if (!scanned.length) return { status: "absent" };
|
|
33568
|
+
if (!exists(source)) return { status: "absent" };
|
|
33569
|
+
const runScope = resolveArtifactRunScope(env);
|
|
33570
|
+
const branchSlug = sanitizeArchiveSegment(args.branch.replace(/[/\\]+/g, "-"), 40);
|
|
33571
|
+
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
33572
|
+
const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
|
|
33573
|
+
try {
|
|
33574
|
+
mkdirp((0, import_node_path38.dirname)(dest));
|
|
33575
|
+
copyDir(source, dest);
|
|
33576
|
+
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
33577
|
+
const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
|
|
33578
|
+
return { status: "archived", path: dest, runScope, entries: scanned.length, bytes };
|
|
33579
|
+
} catch (e) {
|
|
33580
|
+
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
33581
|
+
}
|
|
33582
|
+
}
|
|
33583
|
+
function archiveWorktreeArtifacts(args, deps = {}) {
|
|
33584
|
+
return {
|
|
33585
|
+
jerv: archiveWorktreeJervArtifacts(args, deps),
|
|
33586
|
+
tmp: archiveWorktreeTmpArtifacts(args, deps)
|
|
33587
|
+
};
|
|
33588
|
+
}
|
|
33589
|
+
function evaluateTmpEvidenceRemovalGuard(input) {
|
|
33590
|
+
if (!input.tmpEvidence.length) return { blocked: false };
|
|
33591
|
+
if (input.tmpArchive.status === "archived") return { blocked: false };
|
|
33592
|
+
if (input.tmpArchive.status === "failed") {
|
|
33593
|
+
return {
|
|
33594
|
+
blocked: true,
|
|
33595
|
+
reason: `tmp evidence archive failed \u2014 ${input.tmpArchive.error}`,
|
|
33596
|
+
wouldDelete: input.tmpEvidence.map((e) => e.relPath)
|
|
33597
|
+
};
|
|
33598
|
+
}
|
|
33599
|
+
if (input.gcAcknowledged) return { blocked: false, wouldDelete: input.tmpEvidence.map((e) => e.relPath) };
|
|
33600
|
+
const sample = input.tmpEvidence.slice(0, 8).map((e) => e.relPath);
|
|
33601
|
+
const more = input.tmpEvidence.length > sample.length ? ` (+${input.tmpEvidence.length - sample.length} more)` : "";
|
|
33602
|
+
return {
|
|
33603
|
+
blocked: true,
|
|
33604
|
+
reason: `worktree tmp evidence would be destroyed (${input.tmpEvidence.length} path(s) newer than the branch base) \u2014 re-run with --gc to acknowledge deletion, or copy tmp/ out first`,
|
|
33605
|
+
wouldDelete: sample.concat(more ? [`${input.tmpEvidence.length} total under tmp/`] : [])
|
|
33606
|
+
};
|
|
33607
|
+
}
|
|
33608
|
+
function parseCommitTimestampSec(stdout) {
|
|
33609
|
+
const trimmed = stdout.trim();
|
|
33610
|
+
if (!/^\d+$/.test(trimmed)) return void 0;
|
|
33611
|
+
const sec = Number(trimmed);
|
|
33612
|
+
return Number.isFinite(sec) && sec > 0 ? sec : void 0;
|
|
33613
|
+
}
|
|
33614
|
+
function formatArtifactsArchiveReceipt(archive) {
|
|
33615
|
+
const jervArchived = archive.jerv.status === "archived";
|
|
33616
|
+
const tmpArchived = archive.tmp.status === "archived";
|
|
33617
|
+
if (jervArchived || tmpArchived) {
|
|
33618
|
+
const path2 = archive.tmp.status === "archived" ? archive.tmp.path : archive.jerv.status === "archived" ? archive.jerv.path : void 0;
|
|
33619
|
+
return {
|
|
33620
|
+
status: "archived",
|
|
33621
|
+
jerv: archive.jerv,
|
|
33622
|
+
tmp: archive.tmp,
|
|
33623
|
+
path: path2
|
|
33624
|
+
};
|
|
33625
|
+
}
|
|
33626
|
+
if (archive.jerv.status === "absent" && archive.tmp.status === "absent") {
|
|
33627
|
+
return { status: "absent", jerv: archive.jerv, tmp: archive.tmp };
|
|
33628
|
+
}
|
|
33629
|
+
if (archive.tmp.status === "failed") {
|
|
33630
|
+
return { status: "blocked", jerv: archive.jerv, tmp: archive.tmp, error: archive.tmp.error };
|
|
33631
|
+
}
|
|
33632
|
+
if (archive.jerv.status === "failed") {
|
|
33633
|
+
return { status: "blocked", jerv: archive.jerv, tmp: archive.tmp, error: archive.jerv.error };
|
|
33634
|
+
}
|
|
33635
|
+
return { status: "partial", jerv: archive.jerv, tmp: archive.tmp };
|
|
33636
|
+
}
|
|
33637
|
+
|
|
33638
|
+
// src/worktree-merge-cleanup.ts
|
|
33639
|
+
function normPath2(p) {
|
|
33640
|
+
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
33641
|
+
}
|
|
33642
|
+
function samePath(a, b) {
|
|
33643
|
+
const na = normPath2(a);
|
|
33644
|
+
const nb = normPath2(b);
|
|
33645
|
+
return process.platform === "win32" ? na.toLowerCase() === nb.toLowerCase() : na === nb;
|
|
33646
|
+
}
|
|
33647
|
+
function selectPrMergeCleanupWorktree(branch, before, after, startingPath) {
|
|
33648
|
+
if (!branch) return void 0;
|
|
33649
|
+
const current = after.find((w) => w.branch === branch)?.path;
|
|
33650
|
+
if (current) return current;
|
|
33651
|
+
const previous = before.find((w) => w.branch === branch)?.path;
|
|
33652
|
+
if (previous) return previous;
|
|
33653
|
+
if (startingPath && before.some((w) => w.branch === branch && samePath(w.path, startingPath))) return startingPath;
|
|
33654
|
+
return void 0;
|
|
33655
|
+
}
|
|
33656
|
+
function selectSafeWorktreeCwd(worktrees, targetPath, options) {
|
|
33657
|
+
if (!targetPath) return void 0;
|
|
33658
|
+
const exists = options?.pathExists ?? (() => true);
|
|
33659
|
+
return worktrees.find((w) => !samePath(w.path, targetPath) && exists(w.path))?.path;
|
|
33660
|
+
}
|
|
33661
|
+
async function resolveMergeBaseTimestampMs(git3, baseRef, branch) {
|
|
33662
|
+
try {
|
|
33663
|
+
const mergeBase = (await git3(["merge-base", baseRef, branch])).trim();
|
|
33664
|
+
if (!mergeBase) return 0;
|
|
33665
|
+
const sec = parseCommitTimestampSec(await git3(["show", "-s", "--format=%ct", mergeBase]));
|
|
33666
|
+
return sec ? sec * 1e3 : 0;
|
|
33667
|
+
} catch {
|
|
33668
|
+
return 0;
|
|
33669
|
+
}
|
|
33670
|
+
}
|
|
33671
|
+
async function verifyBranchHead(git3, branch, expectedHeadOid) {
|
|
33672
|
+
if (!expectedHeadOid) return { ok: false, reason: "branch-head-unverified" };
|
|
33673
|
+
const currentHead = (await git3(["rev-parse", `refs/heads/${branch}`]).catch(() => "") || "").trim();
|
|
33674
|
+
if (!currentHead) return { ok: false, reason: "branch-head-unverified" };
|
|
33675
|
+
if (currentHead !== expectedHeadOid) {
|
|
33676
|
+
return { ok: false, reason: "unpushed-branch", error: `${currentHead} != ${expectedHeadOid}` };
|
|
33677
|
+
}
|
|
33678
|
+
return { ok: true };
|
|
33679
|
+
}
|
|
33680
|
+
async function teardownWorktreeStage(worktreePath) {
|
|
33681
|
+
try {
|
|
33682
|
+
const result = await stopStage({ cwd: worktreePath, requiredIdentityCwd: worktreePath, globalStatePath: false });
|
|
33683
|
+
return { status: result.ok ? "stopped" : "failed", ...result.ok ? {} : { error: result.message } };
|
|
33684
|
+
} catch (e) {
|
|
33685
|
+
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
33686
|
+
}
|
|
33687
|
+
}
|
|
33688
|
+
async function removeWorktree(wtPath, git3) {
|
|
33689
|
+
try {
|
|
33690
|
+
await git3(["worktree", "remove", "--force", wtPath]);
|
|
33691
|
+
await git3(["worktree", "prune"]).catch(() => "");
|
|
33692
|
+
return { status: "removed" };
|
|
33693
|
+
} catch (e) {
|
|
33694
|
+
return { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
33695
|
+
}
|
|
33696
|
+
}
|
|
33697
|
+
async function cleanupPrMergeLocalBranch(branch, options) {
|
|
33698
|
+
const report = { branch };
|
|
33699
|
+
if (!branch) {
|
|
33700
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: "missing-branch" };
|
|
33701
|
+
return report;
|
|
33702
|
+
}
|
|
33703
|
+
if (isProtectedBranch(branch)) {
|
|
33704
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: "protected-branch" };
|
|
33705
|
+
return report;
|
|
33706
|
+
}
|
|
33707
|
+
const execGit = options.execGit ?? (async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
33708
|
+
const pathExists = options.pathExists ?? import_node_fs42.existsSync;
|
|
33709
|
+
let afterWorktrees = [];
|
|
33710
|
+
try {
|
|
33711
|
+
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
33712
|
+
} catch (e) {
|
|
33713
|
+
report.localBranch = {
|
|
33714
|
+
name: branch,
|
|
33715
|
+
status: "not-attempted",
|
|
33716
|
+
reason: "worktree-list-failed",
|
|
33717
|
+
error: e instanceof Error ? e.message : String(e)
|
|
33718
|
+
};
|
|
33719
|
+
return report;
|
|
33720
|
+
}
|
|
33721
|
+
const beforeWorktrees = options.beforeWorktrees ?? [];
|
|
33722
|
+
const wtPath = selectPrMergeCleanupWorktree(branch, beforeWorktrees, afterWorktrees, options.startingPath);
|
|
33723
|
+
if (options.preserveWorktree) {
|
|
33724
|
+
if (wtPath) report.worktree = { path: wtPath, status: "preserved", reason: "preserve-worktree" };
|
|
33725
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: "preserve-worktree" };
|
|
33726
|
+
return report;
|
|
33727
|
+
}
|
|
33728
|
+
const mainWorktreePath = beforeWorktrees[0]?.path ?? afterWorktrees[0]?.path;
|
|
33729
|
+
const mainWorktreeTarget = Boolean(wtPath && mainWorktreePath && samePath(wtPath, mainWorktreePath));
|
|
33730
|
+
if (!wtPath || mainWorktreeTarget) {
|
|
33731
|
+
if (wtPath && mainWorktreeTarget) {
|
|
33732
|
+
report.worktree = { path: wtPath, status: "not-attempted", reason: "main-worktree" };
|
|
33733
|
+
}
|
|
33734
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: wtPath ? "main-worktree" : "no-worktree" };
|
|
33735
|
+
return report;
|
|
33736
|
+
}
|
|
33737
|
+
const safeCwd = selectSafeWorktreeCwd([...afterWorktrees, ...beforeWorktrees], wtPath, { pathExists });
|
|
33738
|
+
const git3 = (args) => safeCwd ? execGit(["-C", safeCwd, ...args]) : execGit(args);
|
|
33739
|
+
const headCheck = await verifyBranchHead(git3, branch, options.expectedHeadOid);
|
|
33740
|
+
if (!headCheck.ok) {
|
|
33741
|
+
report.localBranch = {
|
|
33742
|
+
name: branch,
|
|
33743
|
+
status: "not-attempted",
|
|
33744
|
+
reason: headCheck.reason,
|
|
33745
|
+
...headCheck.error ? { error: headCheck.error } : {}
|
|
33746
|
+
};
|
|
33747
|
+
return report;
|
|
33748
|
+
}
|
|
33749
|
+
const porcelain = await execGit(["-C", wtPath, "status", "--porcelain"]).catch(() => void 0);
|
|
33750
|
+
if (porcelain?.trim()) {
|
|
33751
|
+
report.worktree = { path: wtPath, status: "refused", reason: "dirty-worktree" };
|
|
33752
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: "dirty-worktree" };
|
|
33753
|
+
return report;
|
|
33754
|
+
}
|
|
33755
|
+
const newerThanMs = await resolveMergeBaseTimestampMs(
|
|
33756
|
+
(args) => execGit(["-C", wtPath, ...args]),
|
|
33757
|
+
options.baseRef,
|
|
33758
|
+
branch
|
|
33759
|
+
);
|
|
33760
|
+
const tmpEvidence = scanWorktreeTmpEvidence(wtPath, newerThanMs, { exists: pathExists });
|
|
33761
|
+
const stageTeardown = await teardownWorktreeStage(wtPath);
|
|
33762
|
+
const artifactsArchiveRaw = archiveWorktreeArtifacts({
|
|
33763
|
+
worktreePath: wtPath,
|
|
33764
|
+
primaryRoot: options.primaryRoot,
|
|
33765
|
+
branch,
|
|
33766
|
+
newerThanMs
|
|
33767
|
+
});
|
|
33768
|
+
const artifactsArchive = formatArtifactsArchiveReceipt(artifactsArchiveRaw);
|
|
33769
|
+
const tmpGuard = evaluateTmpEvidenceRemovalGuard({
|
|
33770
|
+
tmpEvidence,
|
|
33771
|
+
tmpArchive: artifactsArchiveRaw.tmp,
|
|
33772
|
+
gcAcknowledged: Boolean(options.gcAcknowledged)
|
|
33773
|
+
});
|
|
33774
|
+
if (tmpGuard.blocked) {
|
|
33775
|
+
report.worktree = {
|
|
33776
|
+
path: wtPath,
|
|
33777
|
+
status: "refused",
|
|
33778
|
+
reason: "tmp-evidence",
|
|
33779
|
+
error: tmpGuard.reason,
|
|
33780
|
+
artifactsArchive,
|
|
33781
|
+
stageTeardown,
|
|
33782
|
+
tmpEvidenceCount: tmpEvidence.length
|
|
33783
|
+
};
|
|
33784
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: "tmp-evidence" };
|
|
33785
|
+
return report;
|
|
33786
|
+
}
|
|
33787
|
+
if (artifactsArchive.status === "blocked") {
|
|
33788
|
+
report.worktree = {
|
|
33789
|
+
path: wtPath,
|
|
33790
|
+
status: "refused",
|
|
33791
|
+
reason: "archive-failed",
|
|
33792
|
+
error: artifactsArchive.error,
|
|
33793
|
+
artifactsArchive,
|
|
33794
|
+
stageTeardown,
|
|
33795
|
+
tmpEvidenceCount: tmpEvidence.length
|
|
33796
|
+
};
|
|
33797
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: "archive-failed" };
|
|
33798
|
+
return report;
|
|
33799
|
+
}
|
|
33800
|
+
const removal = await removeWorktree(wtPath, git3);
|
|
33801
|
+
if (removal.status === "failed") {
|
|
33802
|
+
report.worktree = {
|
|
33803
|
+
path: wtPath,
|
|
33804
|
+
status: "failed",
|
|
33805
|
+
error: removal.error,
|
|
33806
|
+
artifactsArchive,
|
|
33807
|
+
stageTeardown,
|
|
33808
|
+
tmpEvidenceCount: tmpEvidence.length
|
|
33809
|
+
};
|
|
33810
|
+
report.localBranch = { name: branch, status: "not-attempted", reason: "worktree-removal-failed" };
|
|
33811
|
+
return report;
|
|
33812
|
+
}
|
|
33813
|
+
report.worktree = {
|
|
33814
|
+
path: wtPath,
|
|
33815
|
+
status: "removed",
|
|
33816
|
+
artifactsArchive,
|
|
33817
|
+
stageTeardown,
|
|
33818
|
+
tmpEvidenceCount: tmpEvidence.length
|
|
33819
|
+
};
|
|
33820
|
+
try {
|
|
33821
|
+
await git3(["branch", "-D", branch]);
|
|
33822
|
+
report.localBranch = { name: branch, status: "deleted" };
|
|
33823
|
+
} catch (e) {
|
|
33824
|
+
const remaining = await git3(["branch", "--list", branch]).catch(() => "");
|
|
33825
|
+
report.localBranch = remaining.trim() ? { name: branch, status: "failed", error: e instanceof Error ? e.message : String(e) } : { name: branch, status: "already-gone" };
|
|
33826
|
+
}
|
|
33827
|
+
return report;
|
|
33828
|
+
}
|
|
33829
|
+
function renderPrMergeCleanupLines(cleanup) {
|
|
33830
|
+
const wt = cleanup?.worktree;
|
|
33831
|
+
if (!wt?.path) return [];
|
|
33832
|
+
const lines = [];
|
|
33833
|
+
if (wt.status === "removed") {
|
|
33834
|
+
lines.push(`pr merge: removed worktree ${wt.path} \u2014 that directory is gone; cd elsewhere before your next command`);
|
|
33835
|
+
} else if (wt.status === "refused") {
|
|
33836
|
+
lines.push(`pr merge: worktree ${wt.path} kept \u2014 ${wt.reason ?? "refused"}${wt.error ? `: ${wt.error}` : ""}`);
|
|
33837
|
+
if (wt.tmpEvidenceCount) lines.push(`pr merge: ${wt.tmpEvidenceCount} tmp path(s) newer than branch base would have been destroyed`);
|
|
33838
|
+
} else if (wt.status === "failed") {
|
|
33839
|
+
lines.push(`pr merge: worktree ${wt.path} removal failed${wt.error ? ` \u2014 ${wt.error}` : ""}`);
|
|
33840
|
+
} else if (wt.status === "preserved") {
|
|
33841
|
+
lines.push(`pr merge: preserved worktree ${wt.path} (--preserve-worktree)`);
|
|
33842
|
+
}
|
|
33843
|
+
if (wt.artifactsArchive?.status === "archived" && wt.artifactsArchive.path) {
|
|
33844
|
+
lines.push(`pr merge: archived worktree evidence to ${wt.artifactsArchive.path}`);
|
|
33845
|
+
} else if (wt.artifactsArchive?.status === "blocked" && wt.artifactsArchive.error) {
|
|
33846
|
+
lines.push(`pr merge: evidence archive failed \u2014 ${wt.artifactsArchive.error}`);
|
|
33847
|
+
}
|
|
33848
|
+
return lines;
|
|
33849
|
+
}
|
|
33850
|
+
|
|
33418
33851
|
// src/board-commands.ts
|
|
33419
33852
|
init_cli_shared();
|
|
33420
33853
|
init_clean_exit();
|
|
@@ -34119,7 +34552,7 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
34119
34552
|
}
|
|
34120
34553
|
|
|
34121
34554
|
// src/issue-commands.ts
|
|
34122
|
-
var
|
|
34555
|
+
var import_node_fs43 = require("node:fs");
|
|
34123
34556
|
var import_node_crypto16 = require("node:crypto");
|
|
34124
34557
|
init_cli_shared();
|
|
34125
34558
|
init_clean_exit();
|
|
@@ -34312,7 +34745,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
34312
34745
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
34313
34746
|
const patch = {};
|
|
34314
34747
|
let bodyChanged = false;
|
|
34315
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
34748
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs43.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
34316
34749
|
if (options.titleFile !== void 0) {
|
|
34317
34750
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
34318
34751
|
} else if (options.title !== void 0) {
|
|
@@ -34929,7 +35362,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
34929
35362
|
if (opts.batch) {
|
|
34930
35363
|
let specs;
|
|
34931
35364
|
try {
|
|
34932
|
-
const raw = (0,
|
|
35365
|
+
const raw = (0, import_node_fs43.readFileSync)(opts.batch, "utf8");
|
|
34933
35366
|
specs = JSON.parse(raw);
|
|
34934
35367
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
34935
35368
|
} catch (e) {
|
|
@@ -35004,8 +35437,8 @@ ${lines}`, {
|
|
|
35004
35437
|
}
|
|
35005
35438
|
|
|
35006
35439
|
// src/train-commands.ts
|
|
35007
|
-
var
|
|
35008
|
-
var
|
|
35440
|
+
var import_node_fs44 = require("node:fs");
|
|
35441
|
+
var import_node_path39 = require("node:path");
|
|
35009
35442
|
init_cli_shared();
|
|
35010
35443
|
init_clean_exit();
|
|
35011
35444
|
init_client_version();
|
|
@@ -35020,7 +35453,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
35020
35453
|
}
|
|
35021
35454
|
function readRepoVersion() {
|
|
35022
35455
|
try {
|
|
35023
|
-
return JSON.parse((0,
|
|
35456
|
+
return JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path39.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
35024
35457
|
} catch {
|
|
35025
35458
|
return void 0;
|
|
35026
35459
|
}
|
|
@@ -35184,9 +35617,9 @@ function registerDeployCommands(program3) {
|
|
|
35184
35617
|
init_cli_shared();
|
|
35185
35618
|
init_github_client();
|
|
35186
35619
|
init_cli_shared();
|
|
35187
|
-
var
|
|
35620
|
+
var import_node_fs45 = require("node:fs");
|
|
35188
35621
|
var import_node_os19 = require("node:os");
|
|
35189
|
-
var
|
|
35622
|
+
var import_node_path40 = require("node:path");
|
|
35190
35623
|
init_marketplace_autoupdate();
|
|
35191
35624
|
var GC_GH_TIMEOUT_MS2 = 2e4;
|
|
35192
35625
|
async function collectStatus() {
|
|
@@ -35389,8 +35822,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
35389
35822
|
}
|
|
35390
35823
|
const home = (0, import_node_os19.homedir)();
|
|
35391
35824
|
const plugin = onboardPluginGate({
|
|
35392
|
-
readKnown: () => readFileSyncSafe((0,
|
|
35393
|
-
readSettings: () => readFileSyncSafe((0,
|
|
35825
|
+
readKnown: () => readFileSyncSafe((0, import_node_path40.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs45.readFileSync),
|
|
35826
|
+
readSettings: () => readFileSyncSafe((0, import_node_path40.join)(home, ".claude", "settings.json"), import_node_fs45.readFileSync)
|
|
35394
35827
|
});
|
|
35395
35828
|
return { track, board, registry: registry2, secrets, plugin, estateCli, doors: opts.doors ?? [], nextCommand };
|
|
35396
35829
|
}
|
|
@@ -36153,8 +36586,8 @@ function registerPrLifecycleCommands(program3) {
|
|
|
36153
36586
|
}
|
|
36154
36587
|
|
|
36155
36588
|
// src/post-merge-recon.ts
|
|
36156
|
-
var
|
|
36157
|
-
var
|
|
36589
|
+
var import_node_fs46 = require("node:fs");
|
|
36590
|
+
var import_node_path41 = require("node:path");
|
|
36158
36591
|
|
|
36159
36592
|
// src/cross-repo-filing-issue.ts
|
|
36160
36593
|
init_github_client();
|
|
@@ -36321,16 +36754,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
36321
36754
|
}
|
|
36322
36755
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
36323
36756
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
36324
|
-
(0,
|
|
36325
|
-
(0,
|
|
36757
|
+
(0, import_node_fs46.mkdirSync)((0, import_node_path41.dirname)(path2), { recursive: true });
|
|
36758
|
+
(0, import_node_fs46.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
36326
36759
|
`, "utf8");
|
|
36327
36760
|
return path2;
|
|
36328
36761
|
}
|
|
36329
36762
|
function clearPostMergeReconRecovery(cwd, repo, pr2) {
|
|
36330
36763
|
const path2 = postMergeReconStatePath(cwd, repo, pr2);
|
|
36331
|
-
if (!(0,
|
|
36764
|
+
if (!(0, import_node_fs46.existsSync)(path2)) return;
|
|
36332
36765
|
try {
|
|
36333
|
-
(0,
|
|
36766
|
+
(0, import_node_fs46.unlinkSync)(path2);
|
|
36334
36767
|
} catch {
|
|
36335
36768
|
}
|
|
36336
36769
|
}
|
|
@@ -39457,17 +39890,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
39457
39890
|
}
|
|
39458
39891
|
function ghHostsConfigPath(env, platform2) {
|
|
39459
39892
|
const sep4 = platform2 === "win32" ? "\\" : "/";
|
|
39460
|
-
const
|
|
39893
|
+
const join37 = (...parts) => parts.join(sep4);
|
|
39461
39894
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
39462
|
-
if (explicit) return
|
|
39895
|
+
if (explicit) return join37(explicit, "hosts.yml");
|
|
39463
39896
|
if (platform2 === "win32") {
|
|
39464
39897
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
39465
|
-
return appData ?
|
|
39898
|
+
return appData ? join37(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
39466
39899
|
}
|
|
39467
39900
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
39468
|
-
if (xdg) return
|
|
39901
|
+
if (xdg) return join37(xdg, "gh", "hosts.yml");
|
|
39469
39902
|
const home = env.HOME?.trim();
|
|
39470
|
-
return home ?
|
|
39903
|
+
return home ? join37(home, ".config", "gh", "hosts.yml") : void 0;
|
|
39471
39904
|
}
|
|
39472
39905
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
39473
39906
|
let hostIndent = null;
|
|
@@ -39517,9 +39950,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
39517
39950
|
}
|
|
39518
39951
|
|
|
39519
39952
|
// src/doctor-io.ts
|
|
39520
|
-
var
|
|
39953
|
+
var import_node_fs47 = require("node:fs");
|
|
39521
39954
|
var import_node_os20 = require("node:os");
|
|
39522
|
-
var
|
|
39955
|
+
var import_node_path42 = require("node:path");
|
|
39523
39956
|
var import_node_child_process20 = require("node:child_process");
|
|
39524
39957
|
var import_node_util8 = require("node:util");
|
|
39525
39958
|
init_version_lag();
|
|
@@ -39534,8 +39967,8 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
|
|
|
39534
39967
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process20.execFile);
|
|
39535
39968
|
function execFileCapture(file, args, opts = {}) {
|
|
39536
39969
|
const sink = nodeDiscardSinkPath();
|
|
39537
|
-
const inFd = (0,
|
|
39538
|
-
const errFd = (0,
|
|
39970
|
+
const inFd = (0, import_node_fs47.openSync)(sink, "r");
|
|
39971
|
+
const errFd = (0, import_node_fs47.openSync)(sink, "w");
|
|
39539
39972
|
try {
|
|
39540
39973
|
return (0, import_node_child_process20.execFileSync)(file, args, {
|
|
39541
39974
|
...opts,
|
|
@@ -39543,15 +39976,15 @@ function execFileCapture(file, args, opts = {}) {
|
|
|
39543
39976
|
stdio: [inFd, "pipe", errFd]
|
|
39544
39977
|
});
|
|
39545
39978
|
} finally {
|
|
39546
|
-
(0,
|
|
39547
|
-
(0,
|
|
39979
|
+
(0, import_node_fs47.closeSync)(inFd);
|
|
39980
|
+
(0, import_node_fs47.closeSync)(errFd);
|
|
39548
39981
|
}
|
|
39549
39982
|
}
|
|
39550
39983
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
39551
39984
|
function installedClaudePluginVersion() {
|
|
39552
39985
|
try {
|
|
39553
39986
|
const file = JSON.parse(
|
|
39554
|
-
(0,
|
|
39987
|
+
(0, import_node_fs47.readFileSync)((0, import_node_path42.join)((0, import_node_os20.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
39555
39988
|
);
|
|
39556
39989
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
39557
39990
|
if (versions.length === 0) return void 0;
|
|
@@ -39562,7 +39995,7 @@ function installedClaudePluginVersion() {
|
|
|
39562
39995
|
}
|
|
39563
39996
|
function manifestVersion(path2) {
|
|
39564
39997
|
try {
|
|
39565
|
-
const manifest = JSON.parse((0,
|
|
39998
|
+
const manifest = JSON.parse((0, import_node_fs47.readFileSync)(path2, "utf8"));
|
|
39566
39999
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
39567
40000
|
} catch {
|
|
39568
40001
|
return void 0;
|
|
@@ -39571,12 +40004,12 @@ function manifestVersion(path2) {
|
|
|
39571
40004
|
function readHermesPluginEvidence(env = process.env) {
|
|
39572
40005
|
const host = hermesConfigRoot(env);
|
|
39573
40006
|
const root = hermesPluginRoot(env);
|
|
39574
|
-
const installRecordPresent = (0,
|
|
39575
|
-
const manifestPath = (0,
|
|
40007
|
+
const installRecordPresent = (0, import_node_fs47.existsSync)(root);
|
|
40008
|
+
const manifestPath = (0, import_node_path42.join)(root, "plugin.yaml");
|
|
39576
40009
|
let installedVersion;
|
|
39577
40010
|
let manifest = "missing";
|
|
39578
40011
|
try {
|
|
39579
|
-
const text = (0,
|
|
40012
|
+
const text = (0, import_node_fs47.readFileSync)(manifestPath, "utf8");
|
|
39580
40013
|
let version;
|
|
39581
40014
|
try {
|
|
39582
40015
|
const parsed = JSON.parse(text).version;
|
|
@@ -39590,20 +40023,20 @@ function readHermesPluginEvidence(env = process.env) {
|
|
|
39590
40023
|
if (version) {
|
|
39591
40024
|
installedVersion = version;
|
|
39592
40025
|
manifest = "valid";
|
|
39593
|
-
} else if ((0,
|
|
40026
|
+
} else if ((0, import_node_fs47.existsSync)(manifestPath)) manifest = "invalid";
|
|
39594
40027
|
} catch {
|
|
39595
|
-
if ((0,
|
|
40028
|
+
if ((0, import_node_fs47.existsSync)(manifestPath)) manifest = "invalid";
|
|
39596
40029
|
}
|
|
39597
40030
|
let skills = false;
|
|
39598
40031
|
try {
|
|
39599
|
-
skills = (0,
|
|
40032
|
+
skills = (0, import_node_fs47.existsSync)((0, import_node_path42.join)(root, "skills")) && (0, import_node_fs47.statSync)((0, import_node_path42.join)(root, "skills")).isDirectory();
|
|
39600
40033
|
} catch {
|
|
39601
40034
|
}
|
|
39602
40035
|
return {
|
|
39603
|
-
hostPresent: (0,
|
|
40036
|
+
hostPresent: (0, import_node_fs47.existsSync)(host),
|
|
39604
40037
|
installRecordPresent,
|
|
39605
40038
|
manifest,
|
|
39606
|
-
payloadPresent: (0,
|
|
40039
|
+
payloadPresent: (0, import_node_fs47.existsSync)((0, import_node_path42.join)(root, "__init__.py")) && skills && manifest === "valid",
|
|
39607
40040
|
...installedVersion ? { installedVersion } : {}
|
|
39608
40041
|
};
|
|
39609
40042
|
}
|
|
@@ -39611,7 +40044,7 @@ function installedSurfacePluginVersion(surface) {
|
|
|
39611
40044
|
const token = surfaceToken(surface);
|
|
39612
40045
|
if (token === "kilo") {
|
|
39613
40046
|
try {
|
|
39614
|
-
const stamp = (0,
|
|
40047
|
+
const stamp = (0, import_node_fs47.readFileSync)((0, import_node_path42.join)((0, import_node_os20.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
39615
40048
|
return stamp || void 0;
|
|
39616
40049
|
} catch {
|
|
39617
40050
|
return void 0;
|
|
@@ -39619,13 +40052,13 @@ function installedSurfacePluginVersion(surface) {
|
|
|
39619
40052
|
}
|
|
39620
40053
|
if (token === "hermes") return readHermesPluginEvidence().installedVersion;
|
|
39621
40054
|
if (token === "cursor") {
|
|
39622
|
-
return manifestVersion((0,
|
|
40055
|
+
return manifestVersion((0, import_node_path42.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
39623
40056
|
}
|
|
39624
40057
|
if (token === "jervcode") {
|
|
39625
40058
|
return installedJervCodePackageVersion();
|
|
39626
40059
|
}
|
|
39627
40060
|
if (token === "kimi") {
|
|
39628
|
-
return manifestVersion((0,
|
|
40061
|
+
return manifestVersion((0, import_node_path42.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
39629
40062
|
}
|
|
39630
40063
|
if (token === "claude") return installedClaudePluginVersion();
|
|
39631
40064
|
if (token !== "codex") return void 0;
|
|
@@ -39659,13 +40092,13 @@ function worktreeRootSync() {
|
|
|
39659
40092
|
}
|
|
39660
40093
|
var gitignorePath = () => {
|
|
39661
40094
|
const root = worktreeRootSync();
|
|
39662
|
-
return root === null ? null : (0,
|
|
40095
|
+
return root === null ? null : (0, import_node_path42.join)(root, ".gitignore");
|
|
39663
40096
|
};
|
|
39664
40097
|
function readGitignore() {
|
|
39665
40098
|
const path2 = gitignorePath();
|
|
39666
40099
|
if (path2 === null) return null;
|
|
39667
40100
|
try {
|
|
39668
|
-
return (0,
|
|
40101
|
+
return (0, import_node_fs47.readFileSync)(path2, "utf8");
|
|
39669
40102
|
} catch {
|
|
39670
40103
|
return null;
|
|
39671
40104
|
}
|
|
@@ -39674,14 +40107,14 @@ function writeGitignore(content) {
|
|
|
39674
40107
|
const path2 = gitignorePath();
|
|
39675
40108
|
if (path2 === null) return false;
|
|
39676
40109
|
try {
|
|
39677
|
-
(0,
|
|
40110
|
+
(0, import_node_fs47.writeFileSync)(path2, content, "utf8");
|
|
39678
40111
|
return true;
|
|
39679
40112
|
} catch {
|
|
39680
40113
|
return false;
|
|
39681
40114
|
}
|
|
39682
40115
|
}
|
|
39683
40116
|
function lineEndingState(root) {
|
|
39684
|
-
const attributesPresent = (0,
|
|
40117
|
+
const attributesPresent = (0, import_node_fs47.existsSync)((0, import_node_path42.join)(root, ".gitattributes"));
|
|
39685
40118
|
try {
|
|
39686
40119
|
const output = execFileCapture("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
|
|
39687
40120
|
windowsHide: true
|
|
@@ -39748,8 +40181,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
39748
40181
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
39749
40182
|
try {
|
|
39750
40183
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
39751
|
-
if (!hostsPath || !(0,
|
|
39752
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
40184
|
+
if (!hostsPath || !(0, import_node_fs48.existsSync)(hostsPath)) return void 0;
|
|
40185
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs48.readFileSync)(hostsPath, "utf8")));
|
|
39753
40186
|
} catch {
|
|
39754
40187
|
return void 0;
|
|
39755
40188
|
}
|
|
@@ -39757,7 +40190,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
39757
40190
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
39758
40191
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
39759
40192
|
function envHealLockPath(home) {
|
|
39760
|
-
return (0,
|
|
40193
|
+
return (0, import_node_path43.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
39761
40194
|
}
|
|
39762
40195
|
async function withEnvHealLock(what, run) {
|
|
39763
40196
|
try {
|
|
@@ -39894,7 +40327,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39894
40327
|
);
|
|
39895
40328
|
const result = applyPluginCachePlan(
|
|
39896
40329
|
plan,
|
|
39897
|
-
(p) => (0,
|
|
40330
|
+
(p) => (0, import_node_fs48.rmSync)(p, { recursive: true }),
|
|
39898
40331
|
stagingApplyFsGuard(configRoot)
|
|
39899
40332
|
);
|
|
39900
40333
|
return {
|
|
@@ -39932,7 +40365,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39932
40365
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
39933
40366
|
// get a permanent — demanding an artifact it never asked for.
|
|
39934
40367
|
docsIndexState: (root) => {
|
|
39935
|
-
if (!(0,
|
|
40368
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path43.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
39936
40369
|
const real = createDocsIndexDeps(root);
|
|
39937
40370
|
let docs2;
|
|
39938
40371
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -39941,7 +40374,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39941
40374
|
},
|
|
39942
40375
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
39943
40376
|
healDocsIndex: (root) => {
|
|
39944
|
-
if (!(0,
|
|
40377
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path43.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
39945
40378
|
const real = createDocsIndexDeps(root);
|
|
39946
40379
|
let docs2;
|
|
39947
40380
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -40033,7 +40466,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
40033
40466
|
repoIndexCloudState: async (root) => {
|
|
40034
40467
|
let localV4 = { state: "absent" };
|
|
40035
40468
|
try {
|
|
40036
|
-
const parsed = JSON.parse((0,
|
|
40469
|
+
const parsed = JSON.parse((0, import_node_fs48.readFileSync)(repoIndexV4StorePath(root), "utf8"));
|
|
40037
40470
|
const state = parsed.status?.state;
|
|
40038
40471
|
if (parsed.schemaVersion === 4 && (state === "ready" || state === "degraded")) {
|
|
40039
40472
|
const chunks = parsed.manifest?.chunks?.length ?? 0;
|
|
@@ -40326,19 +40759,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
40326
40759
|
});
|
|
40327
40760
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
40328
40761
|
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) => {
|
|
40329
|
-
const path2 = (0,
|
|
40330
|
-
const current = (0,
|
|
40762
|
+
const path2 = (0, import_node_path43.join)(process.cwd(), ".gitignore");
|
|
40763
|
+
const current = (0, import_node_fs48.existsSync)(path2) ? (0, import_node_fs48.readFileSync)(path2, "utf8") : null;
|
|
40331
40764
|
const plan = planManagedGitignore(current);
|
|
40332
40765
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
40333
40766
|
if (opts.json) {
|
|
40334
|
-
if (opts.write && plan.changed) (0,
|
|
40767
|
+
if (opts.write && plan.changed) (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
40335
40768
|
console.log(JSON.stringify(plan, null, 2));
|
|
40336
40769
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
40337
40770
|
return;
|
|
40338
40771
|
}
|
|
40339
40772
|
if (opts.write) {
|
|
40340
40773
|
if (plan.changed) {
|
|
40341
|
-
(0,
|
|
40774
|
+
(0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
40342
40775
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
40343
40776
|
} else {
|
|
40344
40777
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -41217,7 +41650,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
41217
41650
|
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`);
|
|
41218
41651
|
if (o.secretsFile) {
|
|
41219
41652
|
try {
|
|
41220
|
-
vars.push(`secrets=${(0,
|
|
41653
|
+
vars.push(`secrets=${(0, import_node_fs48.readFileSync)(o.secretsFile, "utf8")}`);
|
|
41221
41654
|
} catch (e) {
|
|
41222
41655
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
41223
41656
|
}
|
|
@@ -42042,11 +42475,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
42042
42475
|
}
|
|
42043
42476
|
});
|
|
42044
42477
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
42045
|
-
const wfDir = (0,
|
|
42046
|
-
if (!(0,
|
|
42047
|
-
return (0,
|
|
42478
|
+
const wfDir = (0, import_node_path43.join)(cwd, ".github", "workflows");
|
|
42479
|
+
if (!(0, import_node_fs48.existsSync)(wfDir)) return [];
|
|
42480
|
+
return (0, import_node_fs48.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
42048
42481
|
try {
|
|
42049
|
-
return workflowReportsPrChecks((0,
|
|
42482
|
+
return workflowReportsPrChecks((0, import_node_fs48.readFileSync)((0, import_node_path43.join)(wfDir, name), "utf8"));
|
|
42050
42483
|
} catch {
|
|
42051
42484
|
return true;
|
|
42052
42485
|
}
|
|
@@ -42098,16 +42531,16 @@ function ciAuditDeps() {
|
|
|
42098
42531
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
42099
42532
|
readSeedFile: (path2) => {
|
|
42100
42533
|
if (!root) return null;
|
|
42101
|
-
const fullPath = (0,
|
|
42102
|
-
return (0,
|
|
42534
|
+
const fullPath = (0, import_node_path43.join)(root, path2);
|
|
42535
|
+
return (0, import_node_fs48.existsSync)(fullPath) ? (0, import_node_fs48.readFileSync)(fullPath, "utf8") : null;
|
|
42103
42536
|
}
|
|
42104
42537
|
};
|
|
42105
42538
|
}
|
|
42106
42539
|
function hubRoot() {
|
|
42107
|
-
const fromPkg = (0,
|
|
42540
|
+
const fromPkg = (0, import_node_path43.join)(__dirname, "..", "..");
|
|
42108
42541
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
42109
|
-
if ((0,
|
|
42110
|
-
if ((0,
|
|
42542
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path43.join)(fromPkg, marker))) return fromPkg;
|
|
42543
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path43.join)(process.cwd(), marker))) return process.cwd();
|
|
42111
42544
|
return null;
|
|
42112
42545
|
}
|
|
42113
42546
|
async function waitLoopCorePool(label) {
|
|
@@ -42370,18 +42803,19 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
42370
42803
|
else printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
|
|
42371
42804
|
if (result.status === "failed") process.exitCode = 1;
|
|
42372
42805
|
});
|
|
42373
|
-
jsonParity(pr.command("merge <number>").description("merge a PR (squash by default);
|
|
42806
|
+
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("--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) => {
|
|
42374
42807
|
const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
|
|
42375
42808
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
42376
42809
|
const repoForPostCleanup = await resolveRepo(o.repo) ?? o.repo;
|
|
42377
|
-
const prMeta = await execFileP2("gh", ["pr", "view", number, ...repoArgs, "--json", "headRefName,baseRefName", "--jq", "{head: .headRefName, base: .baseRefName}"], { timeout: GC_GH_TIMEOUT_MS }).then((r) => JSON.parse(r.stdout)).catch(async (e) => {
|
|
42810
|
+
const prMeta = await execFileP2("gh", ["pr", "view", number, ...repoArgs, "--json", "headRefName,baseRefName,headRefOid", "--jq", "{head: .headRefName, base: .baseRefName, oid: .headRefOid}"], { timeout: GC_GH_TIMEOUT_MS }).then((r) => JSON.parse(r.stdout)).catch(async (e) => {
|
|
42378
42811
|
if (!isGitHubRateLimitError(e) || !repoForPostCleanup) throw e;
|
|
42379
42812
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 reading PR #${number} via REST instead (#4588).`);
|
|
42380
42813
|
const snapshot = await fetchRestPrSnapshot(number, repoForPostCleanup);
|
|
42381
|
-
return { head: snapshot.headRef, base: snapshot.baseRef };
|
|
42814
|
+
return { head: snapshot.headRef, base: snapshot.baseRef, oid: snapshot.headSha };
|
|
42382
42815
|
});
|
|
42383
42816
|
const headRef = prMeta.head;
|
|
42384
42817
|
const baseRef = prMeta.base;
|
|
42818
|
+
const headRefOid = (prMeta.oid ?? "").trim() || void 0;
|
|
42385
42819
|
const devDeployDeps = repoForPostCleanup ? registryClientDeps(await loadConfig()) : void 0;
|
|
42386
42820
|
const devDeployPlan = repoForPostCleanup && devDeployDeps ? await planDevDeployOnDevelopmentMerge(repoForPostCleanup, baseRef, devDeployDeps).catch(() => ({ applicable: false, reason: "unreadable" })) : { applicable: false, reason: "unreadable" };
|
|
42387
42821
|
const closingGuardInput = await withAlreadyClosedCommitTargets(
|
|
@@ -42401,6 +42835,8 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42401
42835
|
if (closingGuardVerdict.message) console.warn(closingGuardVerdict.message);
|
|
42402
42836
|
const startingPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
42403
42837
|
const housekeeping = assertPrMergeHousekeepingClean(startingPath || process.cwd(), "pr merge", { force: o.force });
|
|
42838
|
+
const beforeWorktreesRead = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => ({ state: "ok", stdout: r.stdout })).catch((e) => ({ state: "failed", error: e.message || "git worktree list failed" }));
|
|
42839
|
+
const beforeWorktrees = beforeWorktreesRead.state === "ok" ? parseGitWorktreePorcelain(beforeWorktreesRead.stdout) : [];
|
|
42404
42840
|
const ciHeadRef = repoForPostCleanup ? await prHeadRefForCiProbe(number, repoForPostCleanup) : void 0;
|
|
42405
42841
|
const ciPolicy = await resolveMergeCiPolicyForCheckout(o.repo, ciHeadRef);
|
|
42406
42842
|
if (o.wait) {
|
|
@@ -42462,7 +42898,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42462
42898
|
}
|
|
42463
42899
|
if (!repoForPostCleanup) throw e;
|
|
42464
42900
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
42465
|
-
const commitMessage = bodyFile ? (0,
|
|
42901
|
+
const commitMessage = bodyFile ? (0, import_node_fs48.readFileSync)(bodyFile, "utf8") : void 0;
|
|
42466
42902
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
42467
42903
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
42468
42904
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -42540,6 +42976,30 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42540
42976
|
return;
|
|
42541
42977
|
}
|
|
42542
42978
|
const remoteBranch = { branch: headRef, existedBefore: remoteBefore, attempted: false, reason: remoteNotAttemptedReason };
|
|
42979
|
+
const primaryRoot = beforeWorktrees[0]?.path ?? (startingPath || process.cwd());
|
|
42980
|
+
let localCleanup;
|
|
42981
|
+
try {
|
|
42982
|
+
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
42983
|
+
beforeWorktrees,
|
|
42984
|
+
startingPath,
|
|
42985
|
+
baseRef,
|
|
42986
|
+
primaryRoot,
|
|
42987
|
+
preserveWorktree: o.preserveWorktree,
|
|
42988
|
+
gcAcknowledged: o.gc,
|
|
42989
|
+
expectedHeadOid: headRefOid,
|
|
42990
|
+
pathExists: (p) => (0, import_node_fs48.existsSync)(p),
|
|
42991
|
+
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout
|
|
42992
|
+
});
|
|
42993
|
+
} catch (e) {
|
|
42994
|
+
localCleanup = {
|
|
42995
|
+
branch: headRef,
|
|
42996
|
+
localBranch: {
|
|
42997
|
+
name: headRef,
|
|
42998
|
+
status: "failed",
|
|
42999
|
+
error: e instanceof Error ? e.message : String(e)
|
|
43000
|
+
}
|
|
43001
|
+
};
|
|
43002
|
+
}
|
|
42543
43003
|
const boardAdvance = await advanceClosedIssuesToDone2(number, repoForPostCleanup);
|
|
42544
43004
|
const crossRepoFilingIssue = repoForPostCleanup ? await reconcileCrossRepoFilingIssue(defaultGitHubClient(), repoForPostCleanup, Number(number)) : { status: "failed", error: "could not resolve the PR repo for cross-repo filing-issue reconciliation" };
|
|
42545
43005
|
const recovery = repoForPostCleanup ? buildPostMergeReconRecovery({
|
|
@@ -42583,6 +43043,8 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42583
43043
|
...methodField ? { method: methodField } : {},
|
|
42584
43044
|
remoteBranch,
|
|
42585
43045
|
housekeeping,
|
|
43046
|
+
...localCleanup?.worktree ? { worktree: localCleanup.worktree } : {},
|
|
43047
|
+
...localCleanup?.localBranch ? { localBranch: localCleanup.localBranch } : {},
|
|
42586
43048
|
// `boardAdvance` keeps its published array shape; `boardAdvanceStatus` is the field a caller reads to
|
|
42587
43049
|
// tell "merged clean" from "merged, but the board did not follow" without guessing from the exit code.
|
|
42588
43050
|
...boardAdvance.entries.length ? { boardAdvance: boardAdvance.entries } : {},
|
|
@@ -42591,6 +43053,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42591
43053
|
...recovery ? { postMergeReconRecovery: recovery } : {},
|
|
42592
43054
|
...devDeploy ? { devDeploy: devDeploy.ok ? { dispatched: true } : { dispatched: false, manual: devDeployManual } } : {}
|
|
42593
43055
|
}));
|
|
43056
|
+
for (const line of renderPrMergeCleanupLines(localCleanup)) console.warn(line);
|
|
42594
43057
|
if (devDeploy && !devDeploy.ok) {
|
|
42595
43058
|
console.error(`pr merge: ${devDeployManual} (${devDeploy.detail})`);
|
|
42596
43059
|
}
|
|
@@ -43158,12 +43621,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
43158
43621
|
targets = resolution.targets;
|
|
43159
43622
|
}
|
|
43160
43623
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
43161
|
-
const fileMatrix = (0,
|
|
43624
|
+
const fileMatrix = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
43162
43625
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
43163
43626
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
43164
|
-
const fileContracts = (0,
|
|
43627
|
+
const fileContracts = (0, import_node_fs48.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs48.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
43165
43628
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
43166
|
-
const sanctioned = (0,
|
|
43629
|
+
const sanctioned = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
43167
43630
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
43168
43631
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
43169
43632
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -43195,16 +43658,16 @@ function directoryBytes(path2) {
|
|
|
43195
43658
|
let total = 0;
|
|
43196
43659
|
let entries;
|
|
43197
43660
|
try {
|
|
43198
|
-
entries = (0,
|
|
43661
|
+
entries = (0, import_node_fs48.readdirSync)(path2, { withFileTypes: true });
|
|
43199
43662
|
} catch {
|
|
43200
43663
|
return 0;
|
|
43201
43664
|
}
|
|
43202
43665
|
for (const entry of entries) {
|
|
43203
|
-
const child2 = (0,
|
|
43666
|
+
const child2 = (0, import_node_path43.join)(path2, entry.name);
|
|
43204
43667
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
43205
43668
|
else {
|
|
43206
43669
|
try {
|
|
43207
|
-
total += (0,
|
|
43670
|
+
total += (0, import_node_fs48.statSync)(child2).size;
|
|
43208
43671
|
} catch {
|
|
43209
43672
|
}
|
|
43210
43673
|
}
|
|
@@ -43212,25 +43675,25 @@ function directoryBytes(path2) {
|
|
|
43212
43675
|
return total;
|
|
43213
43676
|
}
|
|
43214
43677
|
function listDirEntries(dir) {
|
|
43215
|
-
return (0,
|
|
43678
|
+
return (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
43216
43679
|
}
|
|
43217
43680
|
function readInstalledPluginRefs(configRoot) {
|
|
43218
43681
|
const p = installedPluginsPathForConfig(configRoot);
|
|
43219
|
-
if (!(0,
|
|
43682
|
+
if (!(0, import_node_fs48.existsSync)(p)) return [];
|
|
43220
43683
|
try {
|
|
43221
|
-
return installedPluginPaths((0,
|
|
43684
|
+
return installedPluginPaths((0, import_node_fs48.readFileSync)(p, "utf8"));
|
|
43222
43685
|
} catch {
|
|
43223
43686
|
return null;
|
|
43224
43687
|
}
|
|
43225
43688
|
}
|
|
43226
43689
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
43227
43690
|
return {
|
|
43228
|
-
exists: (p) => (0,
|
|
43229
|
-
listVersionDirs: (root) => (0,
|
|
43691
|
+
exists: (p) => (0, import_node_fs48.existsSync)(p),
|
|
43692
|
+
listVersionDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
43230
43693
|
dirBytes,
|
|
43231
|
-
listStagingDirs: (root) => (0,
|
|
43694
|
+
listStagingDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
43232
43695
|
try {
|
|
43233
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
43696
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path43.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs48.statSync)(p).mtimeMs) };
|
|
43234
43697
|
} catch {
|
|
43235
43698
|
return { name: d.name, mtimeMs: Date.now() };
|
|
43236
43699
|
}
|
|
@@ -43244,10 +43707,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
43244
43707
|
return {
|
|
43245
43708
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
43246
43709
|
mtimeMs: (name) => {
|
|
43247
|
-
const p = (0,
|
|
43248
|
-
if (!(0,
|
|
43710
|
+
const p = (0, import_node_path43.join)(stagingRoot, name);
|
|
43711
|
+
if (!(0, import_node_fs48.existsSync)(p)) return null;
|
|
43249
43712
|
try {
|
|
43250
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
43713
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs48.statSync)(q).mtimeMs);
|
|
43251
43714
|
} catch {
|
|
43252
43715
|
return null;
|
|
43253
43716
|
}
|
|
@@ -43273,7 +43736,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
43273
43736
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
43274
43737
|
);
|
|
43275
43738
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
43276
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
43739
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs48.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
43277
43740
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
43278
43741
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
43279
43742
|
else console.log(renderPluginCachePlan(plan, result));
|
package/package.json
CHANGED