@mutmutco/cli 4.3.14 → 4.3.16
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/README.md +4 -2
- package/dist/main.cjs +407 -178
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3416,7 +3416,7 @@ function useColor() {
|
|
|
3416
3416
|
var program = new Command();
|
|
3417
3417
|
|
|
3418
3418
|
// src/command-composition.ts
|
|
3419
|
-
var
|
|
3419
|
+
var import_node_fs50 = require("node:fs");
|
|
3420
3420
|
|
|
3421
3421
|
// src/clean-exit.ts
|
|
3422
3422
|
var UNDICI_GLOBAL_DISPATCHER_SYMBOL = Object.getOwnPropertySymbols(globalThis).find(
|
|
@@ -5226,8 +5226,8 @@ function unknownCommandDomainGuide(parentPath, token) {
|
|
|
5226
5226
|
}
|
|
5227
5227
|
|
|
5228
5228
|
// src/command-composition.ts
|
|
5229
|
-
var
|
|
5230
|
-
var
|
|
5229
|
+
var import_node_os22 = require("node:os");
|
|
5230
|
+
var import_node_path47 = require("node:path");
|
|
5231
5231
|
|
|
5232
5232
|
// src/board-read.ts
|
|
5233
5233
|
var import_node_fs13 = require("node:fs");
|
|
@@ -7917,10 +7917,12 @@ function buildIssueArgs({ type, title, body, priority, repo, labels }) {
|
|
|
7917
7917
|
for (const label of labels ?? []) args.push("--label", label);
|
|
7918
7918
|
return args;
|
|
7919
7919
|
}
|
|
7920
|
+
var LEARNING_LABEL = "learning";
|
|
7921
|
+
var LEARNING_LABEL_COLOR = "d93f0b";
|
|
7920
7922
|
async function ensureLabelsExist(labels, repo, deps = {}) {
|
|
7921
7923
|
const run = deps.run ?? execFileP;
|
|
7922
7924
|
for (const label of new Set(labels)) {
|
|
7923
|
-
const args = ["label", "create", label, "--color", "ededed"];
|
|
7925
|
+
const args = ["label", "create", label, "--color", label === LEARNING_LABEL ? LEARNING_LABEL_COLOR : "ededed"];
|
|
7924
7926
|
if (repo) args.push("--repo", repo);
|
|
7925
7927
|
try {
|
|
7926
7928
|
await run("gh", args, { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
@@ -9334,7 +9336,7 @@ async function fetchSecretForUse(deps, { repo, key, slug }) {
|
|
|
9334
9336
|
// src/report.ts
|
|
9335
9337
|
var HUB_REPO = "mutmutco/MMI-Hub";
|
|
9336
9338
|
var REPORT_LABEL = "report";
|
|
9337
|
-
var
|
|
9339
|
+
var LEARNING_LABEL2 = "learning";
|
|
9338
9340
|
var REPORT_LOOP_KIND = "friction";
|
|
9339
9341
|
var REPORT_SURFACE_WAIVER_REASON = "Hub-only friction filing; attribution repo is footer-only; no honest single surface";
|
|
9340
9342
|
function preflightReportSurface() {
|
|
@@ -9516,7 +9518,7 @@ async function loadConfigForBoardSelector(selector, repoOption) {
|
|
|
9516
9518
|
|
|
9517
9519
|
// src/skill-lesson.ts
|
|
9518
9520
|
var SKILL_LESSON_LABEL = "skill-lesson";
|
|
9519
|
-
var SKILL_LESSON_FILE_LABELS = [SKILL_LESSON_LABEL,
|
|
9521
|
+
var SKILL_LESSON_FILE_LABELS = [SKILL_LESSON_LABEL, LEARNING_LABEL2];
|
|
9520
9522
|
var SKILL_LESSON_LOOP_KIND = "lesson";
|
|
9521
9523
|
var SKILL_NAMES = ["bootstrap", "epic", "hotfix", "rcand", "release", "secrets", "stage"];
|
|
9522
9524
|
function assertSkillName(name) {
|
|
@@ -9547,6 +9549,11 @@ ${buildSkillLessonBody(body, sourceRepo, pluginSha)}`;
|
|
|
9547
9549
|
function findDuplicateLesson(source, openLessons) {
|
|
9548
9550
|
return findDuplicateReport(source, openLessons);
|
|
9549
9551
|
}
|
|
9552
|
+
var SKILL_LESSON_DEPRECATION_NOTICE = "mmi-cli learning skill-lesson is deprecated \u2014 file the retro with the jerv_learning_note tool in-session (origin retro), or `jervcode learn --origin retro --surface skills` on a machine path. This verb still files.";
|
|
9553
|
+
function warnSkillLessonDeprecated(write = (line) => void process.stderr.write(line)) {
|
|
9554
|
+
write(`${SKILL_LESSON_DEPRECATION_NOTICE}
|
|
9555
|
+
`);
|
|
9556
|
+
}
|
|
9550
9557
|
|
|
9551
9558
|
// src/session-identity.ts
|
|
9552
9559
|
var import_node_crypto4 = require("node:crypto");
|
|
@@ -13699,7 +13706,7 @@ function createDocsIndexDeps(repoRoot2) {
|
|
|
13699
13706
|
}
|
|
13700
13707
|
|
|
13701
13708
|
// src/gate-budget.ts
|
|
13702
|
-
var BLESSED_RUN_WITH_BUDGET_SHA = "
|
|
13709
|
+
var BLESSED_RUN_WITH_BUDGET_SHA = "5ce541add569e54e7ca96de67a768577d3d604e4";
|
|
13703
13710
|
var BLESSED_RUNNER_GATE_SHA = "a6ee756d94fd95884451381e94408f0348eb11db";
|
|
13704
13711
|
var REMOTE_USE = /^mutmutco\/MMI-Hub\/\.github\/actions\/run-with-budget@(\S+)$/;
|
|
13705
13712
|
var LOCAL_USE = "./.github/actions/run-with-budget";
|
|
@@ -14037,6 +14044,8 @@ function gateSeedVars(cls, releaseTrack, runtime = "node", requiredCheckBranches
|
|
|
14037
14044
|
const runtimeVars = {
|
|
14038
14045
|
GATE_RUNTIME: runtime,
|
|
14039
14046
|
GATE_CMD: rt.cmd,
|
|
14047
|
+
// #6254: affected-only PR command; empty means "same as GATE_CMD" (withDerivedRepoVars fills it).
|
|
14048
|
+
GATE_AFFECTED_CMD: "",
|
|
14040
14049
|
GATE_INSTALL_CMD: rt.install,
|
|
14041
14050
|
GATE_WORKDIR: ".",
|
|
14042
14051
|
GATE_CACHE_DEP_PATH: "package-lock.json",
|
|
@@ -14084,6 +14093,7 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack, requiredCheckBranc
|
|
|
14084
14093
|
for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime, requiredCheckBranches))) {
|
|
14085
14094
|
out[key] ??= value;
|
|
14086
14095
|
}
|
|
14096
|
+
if (!out.GATE_AFFECTED_CMD.trim()) out.GATE_AFFECTED_CMD = out.GATE_CMD;
|
|
14087
14097
|
return out;
|
|
14088
14098
|
}
|
|
14089
14099
|
function gateConfigToVars(gate) {
|
|
@@ -14091,6 +14101,7 @@ function gateConfigToVars(gate) {
|
|
|
14091
14101
|
if (!gate || typeof gate !== "object") return out;
|
|
14092
14102
|
if (gate.runtime === "node" || gate.runtime === "python") out.GATE_RUNTIME = gate.runtime;
|
|
14093
14103
|
if (typeof gate.cmd === "string" && gate.cmd.trim()) out.GATE_CMD = gate.cmd;
|
|
14104
|
+
if (typeof gate.affectedCmd === "string" && gate.affectedCmd.trim()) out.GATE_AFFECTED_CMD = gate.affectedCmd;
|
|
14094
14105
|
if (typeof gate.workdir === "string" && gate.workdir.trim()) out.GATE_WORKDIR = gate.workdir;
|
|
14095
14106
|
if (typeof gate.cacheDepPath === "string" && gate.cacheDepPath.trim()) out.GATE_CACHE_DEP_PATH = gate.cacheDepPath;
|
|
14096
14107
|
if (typeof gate.pyVersion === "string" && gate.pyVersion.trim()) out.GATE_PY_VERSION = gate.pyVersion;
|
|
@@ -15476,10 +15487,10 @@ var rollout_plan_default = {
|
|
|
15476
15487
|
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)."
|
|
15477
15488
|
},
|
|
15478
15489
|
baseline: {
|
|
15479
|
-
version: "4.3.
|
|
15480
|
-
tag: "v4.3.
|
|
15481
|
-
commit: "
|
|
15482
|
-
npm: "@mutmutco/cli@4.3.
|
|
15490
|
+
version: "4.3.16",
|
|
15491
|
+
tag: "v4.3.16",
|
|
15492
|
+
commit: "5a4b8e29fd1a",
|
|
15493
|
+
npm: "@mutmutco/cli@4.3.16"
|
|
15483
15494
|
},
|
|
15484
15495
|
exitCriterion: "fleet-n-of-n",
|
|
15485
15496
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15496,14 +15507,14 @@ var rollout_plan_default = {
|
|
|
15496
15507
|
repo: "mutmutco/mmi-hub",
|
|
15497
15508
|
role: "canary",
|
|
15498
15509
|
schedule: "train",
|
|
15499
|
-
v3Target: "v4.3.
|
|
15510
|
+
v3Target: "v4.3.16"
|
|
15500
15511
|
}
|
|
15501
15512
|
],
|
|
15502
15513
|
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.",
|
|
15503
15514
|
rollback: {
|
|
15504
15515
|
independent: true,
|
|
15505
|
-
mechanism: "npm dist-tag latest -> 4.3.
|
|
15506
|
-
v3Target: "v4.3.
|
|
15516
|
+
mechanism: "npm dist-tag latest -> 4.3.16 and redeploy the Hub Lambda from tag v4.3.16 (5a4b8e29fd1a); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15517
|
+
v3Target: "v4.3.16 (@mutmutco/cli@4.3.16, tag commit 5a4b8e29fd1a \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15507
15518
|
}
|
|
15508
15519
|
},
|
|
15509
15520
|
{
|
|
@@ -28771,7 +28782,7 @@ function parseGateVar(raw) {
|
|
|
28771
28782
|
throw new Error('org project set: gate must be JSON, e.g. {"runtime":"python","cmd":"pytest","workdir":"app"}');
|
|
28772
28783
|
}
|
|
28773
28784
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
28774
|
-
throw new Error("org project set: gate must be a {runtime,cmd,workdir,cacheDepPath,pyVersion} object");
|
|
28785
|
+
throw new Error("org project set: gate must be a {runtime,cmd,affectedCmd,workdir,cacheDepPath,pyVersion} object");
|
|
28775
28786
|
}
|
|
28776
28787
|
const map = parsed;
|
|
28777
28788
|
const out = {};
|
|
@@ -28779,11 +28790,11 @@ function parseGateVar(raw) {
|
|
|
28779
28790
|
if (key === "runtime") {
|
|
28780
28791
|
if (value !== "node" && value !== "python") throw new Error('org project set: gate.runtime must be "node" or "python"');
|
|
28781
28792
|
out.runtime = value;
|
|
28782
|
-
} else if (key === "cmd" || key === "workdir" || key === "cacheDepPath" || key === "pyVersion") {
|
|
28793
|
+
} else if (key === "cmd" || key === "affectedCmd" || key === "workdir" || key === "cacheDepPath" || key === "pyVersion") {
|
|
28783
28794
|
if (typeof value !== "string" || !value.trim()) throw new Error(`org project set: gate.${key} must be a non-empty string`);
|
|
28784
28795
|
out[key] = value.trim();
|
|
28785
28796
|
} else {
|
|
28786
|
-
throw new Error(`org project set: gate key "${key}" \u2014 expected only runtime/cmd/workdir/cacheDepPath/pyVersion`);
|
|
28797
|
+
throw new Error(`org project set: gate key "${key}" \u2014 expected only runtime/cmd/affectedCmd/workdir/cacheDepPath/pyVersion`);
|
|
28787
28798
|
}
|
|
28788
28799
|
}
|
|
28789
28800
|
return out;
|
|
@@ -28884,7 +28895,7 @@ var SETTABLE_VAR_HINTS = {
|
|
|
28884
28895
|
requiredChecks: 'JSON array, e.g. ["gate"] or [] for no-ci',
|
|
28885
28896
|
requiredCheckBranches: "non-empty JSON branch-name array; exact product-ruleset scope (#5210)",
|
|
28886
28897
|
ciExemptReason: "non-empty string \u2014 declares this deployable repo has no CI surface (#4265)",
|
|
28887
|
-
gate: "JSON {runtime,cmd,workdir,cacheDepPath,pyVersion}"
|
|
28898
|
+
gate: "JSON {runtime,cmd,affectedCmd,workdir,cacheDepPath,pyVersion}"
|
|
28888
28899
|
};
|
|
28889
28900
|
function settableVarHelp() {
|
|
28890
28901
|
const keys = SETTABLE_VAR_KEYS.map((k) => {
|
|
@@ -33124,7 +33135,7 @@ ${lines2}`, {
|
|
|
33124
33135
|
var LEARNING_PICKUP_CMD = ["mmi-cli", "learning", "pickup"].join(" ");
|
|
33125
33136
|
var LEARNING_CLAIM_MARKER_LIVE_MS = 24 * 60 * 6e4;
|
|
33126
33137
|
function isLearningChannelItem(labels) {
|
|
33127
|
-
return labels.includes(
|
|
33138
|
+
return labels.includes(LEARNING_LABEL2) || loopKindOf(labels) !== void 0;
|
|
33128
33139
|
}
|
|
33129
33140
|
function claimEvidenceBlocksLearningAdoption(evidence) {
|
|
33130
33141
|
if (evidence.failed.includes("comments")) return true;
|
|
@@ -35901,8 +35912,8 @@ function renderVerifySecrets(body) {
|
|
|
35901
35912
|
}
|
|
35902
35913
|
|
|
35903
35914
|
// src/command-register-collaboration.ts
|
|
35904
|
-
var
|
|
35905
|
-
var
|
|
35915
|
+
var import_node_child_process16 = require("node:child_process");
|
|
35916
|
+
var import_node_fs43 = require("node:fs");
|
|
35906
35917
|
var import_promises7 = require("node:fs/promises");
|
|
35907
35918
|
|
|
35908
35919
|
// src/session-runtime.ts
|
|
@@ -35922,7 +35933,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
35922
35933
|
}
|
|
35923
35934
|
|
|
35924
35935
|
// src/command-register-collaboration.ts
|
|
35925
|
-
var
|
|
35936
|
+
var import_node_path40 = require("node:path");
|
|
35926
35937
|
|
|
35927
35938
|
// src/attach-to-project.ts
|
|
35928
35939
|
function boardAttachRateLimitedReceipt(resetEpochSeconds) {
|
|
@@ -37574,15 +37585,193 @@ function postMergeReconWarnings(input) {
|
|
|
37574
37585
|
return lines2;
|
|
37575
37586
|
}
|
|
37576
37587
|
|
|
37577
|
-
// src/
|
|
37588
|
+
// src/review-verdict.ts
|
|
37578
37589
|
var import_node_child_process14 = require("node:child_process");
|
|
37590
|
+
var import_node_fs39 = require("node:fs");
|
|
37591
|
+
var import_node_os19 = require("node:os");
|
|
37592
|
+
var import_node_path36 = require("node:path");
|
|
37593
|
+
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
37594
|
+
var REVIEW_WAIVER_MARKER = "<!-- zeroci-review-waived v1 -->";
|
|
37595
|
+
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
37596
|
+
function isReviewVerdict(value) {
|
|
37597
|
+
return typeof value === "string" && REVIEW_VERDICTS.includes(value);
|
|
37598
|
+
}
|
|
37599
|
+
function renderReviewVerdictComment(input) {
|
|
37600
|
+
const payload = {
|
|
37601
|
+
v: 1,
|
|
37602
|
+
patch: input.patch,
|
|
37603
|
+
head: input.head,
|
|
37604
|
+
verdict: input.verdict,
|
|
37605
|
+
scope: input.scope,
|
|
37606
|
+
risk: input.risk,
|
|
37607
|
+
unverified: input.unverified,
|
|
37608
|
+
reviewer: input.reviewer
|
|
37609
|
+
};
|
|
37610
|
+
const findings = input.findings?.trim();
|
|
37611
|
+
return `${REVIEW_VERDICT_MARKER}
|
|
37612
|
+
\`\`\`json
|
|
37613
|
+
${JSON.stringify(payload, null, 2)}
|
|
37614
|
+
\`\`\`
|
|
37615
|
+
${findings ? `
|
|
37616
|
+
${findings}
|
|
37617
|
+
` : ""}`;
|
|
37618
|
+
}
|
|
37619
|
+
function isReviewVerdictComment(body) {
|
|
37620
|
+
return body.trimStart().startsWith(REVIEW_VERDICT_MARKER);
|
|
37621
|
+
}
|
|
37622
|
+
var FENCE_RE = /^(?:`{3,}|~{3,})[^\n]*\n([\s\S]*?)\n(?:`{3,}|~{3,})\s*$/m;
|
|
37623
|
+
function parseReviewVerdictComment(body) {
|
|
37624
|
+
if (!isReviewVerdictComment(body)) return void 0;
|
|
37625
|
+
const rest = body.trimStart().slice(REVIEW_VERDICT_MARKER.length);
|
|
37626
|
+
const fence = FENCE_RE.exec(rest);
|
|
37627
|
+
if (!fence) return void 0;
|
|
37628
|
+
let parsed;
|
|
37629
|
+
try {
|
|
37630
|
+
parsed = JSON.parse(fence[1]);
|
|
37631
|
+
} catch {
|
|
37632
|
+
return void 0;
|
|
37633
|
+
}
|
|
37634
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
37635
|
+
const p = parsed;
|
|
37636
|
+
if (p.v !== 1 || !isReviewVerdict(p.verdict)) return void 0;
|
|
37637
|
+
if (typeof p.patch !== "string" || !/^[0-9a-f]{40}$/.test(p.patch)) return void 0;
|
|
37638
|
+
if (typeof p.head !== "string" || typeof p.scope !== "string" || typeof p.risk !== "string" || typeof p.reviewer !== "string") return void 0;
|
|
37639
|
+
const unverified = Array.isArray(p.unverified) ? p.unverified.filter((u) => typeof u === "string") : [];
|
|
37640
|
+
return { v: 1, patch: p.patch, head: p.head, verdict: p.verdict, scope: p.scope, risk: p.risk, unverified, reviewer: p.reviewer };
|
|
37641
|
+
}
|
|
37642
|
+
function evaluateReviewVerdict(comments, currentPatchId) {
|
|
37643
|
+
let latest;
|
|
37644
|
+
for (const c of comments) {
|
|
37645
|
+
if (!isReviewVerdictComment(c.body)) continue;
|
|
37646
|
+
if (!latest || c.createdAt >= latest.createdAt) latest = c;
|
|
37647
|
+
}
|
|
37648
|
+
if (!latest) return { ok: false, reason: "none" };
|
|
37649
|
+
const payload = parseReviewVerdictComment(latest.body);
|
|
37650
|
+
if (!payload) return { ok: false, reason: "malformed" };
|
|
37651
|
+
if (payload.verdict !== "PROCEED") return { ok: false, reason: "not-proceed", verdict: payload.verdict, patch: payload.patch };
|
|
37652
|
+
if (payload.patch !== currentPatchId) return { ok: false, reason: "stale", verdict: payload.verdict, patch: payload.patch };
|
|
37653
|
+
return { ok: true, reason: "proceed", verdict: payload.verdict, patch: payload.patch };
|
|
37654
|
+
}
|
|
37655
|
+
function reviewVerdictRemedyCommand(number, repo) {
|
|
37656
|
+
return `mmi-cli devops pr review-verdict ${number} --repo ${repo} --verdict PROCEED --scope "<one line>" --risk "<one line>" --reviewer <seat-or-model-id>`;
|
|
37657
|
+
}
|
|
37658
|
+
function reviewVerdictRefusalMessage(context, number, repo, evaluation, currentPatchId) {
|
|
37659
|
+
const why = {
|
|
37660
|
+
none: "no review verdict comment on the PR",
|
|
37661
|
+
stale: `the latest verdict is bound to patch ${evaluation.patch} but the PR's current diff is ${currentPatchId} \u2014 the content changed since the review`,
|
|
37662
|
+
"not-proceed": `the latest verdict is ${evaluation.verdict}, not PROCEED`,
|
|
37663
|
+
malformed: "the latest verdict comment is malformed (marker present, JSON block unreadable or wrong shape)",
|
|
37664
|
+
proceed: "proceed"
|
|
37665
|
+
}[evaluation.reason];
|
|
37666
|
+
return `${context}: REVIEW VERDICT REQUIRED (#6255) \u2014 ${why}. Review PR #${number} and post the verdict, then rerun:
|
|
37667
|
+
${reviewVerdictRemedyCommand(number, repo)}
|
|
37668
|
+
To land without a review, pass --without-review "<reason>" \u2014 it posts a waiver comment on the PR.`;
|
|
37669
|
+
}
|
|
37670
|
+
function renderReviewWaiverComment(reason, actor) {
|
|
37671
|
+
return `${REVIEW_WAIVER_MARKER}
|
|
37672
|
+
Review gate waived by @${actor}: ${reason.trim()}
|
|
37673
|
+
`;
|
|
37674
|
+
}
|
|
37675
|
+
function computePrPatchId(number, repo) {
|
|
37676
|
+
return new Promise((resolve7, reject) => {
|
|
37677
|
+
const gh = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
37678
|
+
const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
37679
|
+
let out = "";
|
|
37680
|
+
let ghErr = "";
|
|
37681
|
+
let gitErr = "";
|
|
37682
|
+
const timer = setTimeout(() => {
|
|
37683
|
+
gh.kill();
|
|
37684
|
+
git3.kill();
|
|
37685
|
+
reject(new Error(`patch-id: timed out after ${GC_GH_TIMEOUT_MS4}ms`));
|
|
37686
|
+
}, GC_GH_TIMEOUT_MS4);
|
|
37687
|
+
gh.stdout.pipe(git3.stdin);
|
|
37688
|
+
gh.stderr.on("data", (d) => {
|
|
37689
|
+
ghErr += d.toString();
|
|
37690
|
+
});
|
|
37691
|
+
git3.stderr.on("data", (d) => {
|
|
37692
|
+
gitErr += d.toString();
|
|
37693
|
+
});
|
|
37694
|
+
git3.stdout.on("data", (d) => {
|
|
37695
|
+
out += d.toString();
|
|
37696
|
+
});
|
|
37697
|
+
gh.on("error", (e) => {
|
|
37698
|
+
clearTimeout(timer);
|
|
37699
|
+
reject(e);
|
|
37700
|
+
});
|
|
37701
|
+
git3.on("error", (e) => {
|
|
37702
|
+
clearTimeout(timer);
|
|
37703
|
+
reject(e);
|
|
37704
|
+
});
|
|
37705
|
+
let ghCode;
|
|
37706
|
+
gh.on("close", (code) => {
|
|
37707
|
+
ghCode = code;
|
|
37708
|
+
});
|
|
37709
|
+
git3.on("close", (code) => {
|
|
37710
|
+
clearTimeout(timer);
|
|
37711
|
+
if (ghCode) return reject(Object.assign(new Error(`gh pr diff ${number} --repo ${repo} exited ${ghCode}: ${ghErr.trim()}`), { stderr: ghErr }));
|
|
37712
|
+
if (code) return reject(new Error(`git patch-id --stable exited ${code}: ${gitErr.trim()}`));
|
|
37713
|
+
const id = out.trim().split(/\s+/)[0];
|
|
37714
|
+
if (!/^[0-9a-f]{40}$/.test(id ?? "")) return reject(new Error(`patch-id: PR #${number} diff is empty or unreadable (gh: ${ghErr.trim() || "no stderr"})`));
|
|
37715
|
+
resolve7(id);
|
|
37716
|
+
});
|
|
37717
|
+
});
|
|
37718
|
+
}
|
|
37719
|
+
async function readPrHeadSha(number, repo) {
|
|
37720
|
+
const { stdout } = await execFileP("gh", ["api", `repos/${repo}/pulls/${number}`, "--jq", ".head.sha"], { timeout: GC_GH_TIMEOUT_MS4 });
|
|
37721
|
+
const sha = stdout.trim();
|
|
37722
|
+
if (!/^[0-9a-f]{40}$/.test(sha)) throw new Error(`could not read PR #${number} head sha`);
|
|
37723
|
+
return sha;
|
|
37724
|
+
}
|
|
37725
|
+
async function readPrIssueComments(number, repo) {
|
|
37726
|
+
const { stdout } = await execFileP("gh", ["api", "--paginate", `repos/${repo}/issues/${number}/comments?per_page=100`, "--jq", ".[] | {body, createdAt: .created_at}"], { timeout: GC_GH_TIMEOUT_MS4 });
|
|
37727
|
+
return parseNdjsonLines(stdout);
|
|
37728
|
+
}
|
|
37729
|
+
async function postPrCommentFromFile(number, repo, body) {
|
|
37730
|
+
const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path36.join)((0, import_node_os19.tmpdir)(), "mmi-review-verdict-"));
|
|
37731
|
+
const path2 = (0, import_node_path36.join)(dir, "body.md");
|
|
37732
|
+
try {
|
|
37733
|
+
(0, import_node_fs39.writeFileSync)(path2, body, "utf8");
|
|
37734
|
+
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
37735
|
+
return stdout.trim();
|
|
37736
|
+
} finally {
|
|
37737
|
+
try {
|
|
37738
|
+
(0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
|
|
37739
|
+
} catch {
|
|
37740
|
+
}
|
|
37741
|
+
}
|
|
37742
|
+
}
|
|
37743
|
+
async function requireReviewVerdict(context, number, repo, opts) {
|
|
37744
|
+
if (opts.withoutReview !== void 0) {
|
|
37745
|
+
const reason = opts.withoutReview.trim();
|
|
37746
|
+
if (!reason) throw new Error(`${context}: --without-review needs a non-empty reason`);
|
|
37747
|
+
const actor = await execFileP("gh", ["api", "user", "--jq", ".login"], { timeout: GC_GH_TIMEOUT_MS4 }).then((r) => r.stdout.trim()).catch(() => "") || "unknown";
|
|
37748
|
+
await postPrCommentFromFile(number, repo, renderReviewWaiverComment(reason, actor));
|
|
37749
|
+
console.warn(`${context}: review gate WAIVED by @${actor} \u2014 ${reason} (waiver comment posted on PR #${number})`);
|
|
37750
|
+
return void 0;
|
|
37751
|
+
}
|
|
37752
|
+
let comments;
|
|
37753
|
+
let currentPatchId;
|
|
37754
|
+
try {
|
|
37755
|
+
[comments, currentPatchId] = await Promise.all([readPrIssueComments(number, repo), computePrPatchId(number, repo)]);
|
|
37756
|
+
} catch (e) {
|
|
37757
|
+
const detail = e.message || String(e);
|
|
37758
|
+
const limited = isGitHubRateLimitError(e) ? " (gh rate-limited \u2014 retry after the pool resets)" : "";
|
|
37759
|
+
throw new Error(`${context}: cannot read PR #${number}'s review verdict${limited} \u2014 ${detail}. Refusing to merge WITHOUT the review gate (#6255).`);
|
|
37760
|
+
}
|
|
37761
|
+
const evaluation = evaluateReviewVerdict(comments, currentPatchId);
|
|
37762
|
+
if (evaluation.ok) return void 0;
|
|
37763
|
+
return reviewVerdictRefusalMessage(context, number, repo, evaluation, currentPatchId);
|
|
37764
|
+
}
|
|
37765
|
+
|
|
37766
|
+
// src/pr-create-docs-check.ts
|
|
37767
|
+
var import_node_child_process15 = require("node:child_process");
|
|
37579
37768
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
37580
37769
|
function catFileBatch(root, ref, paths) {
|
|
37581
37770
|
if (paths.length === 0) return Promise.resolve([]);
|
|
37582
37771
|
return new Promise((resolve7) => {
|
|
37583
37772
|
const chunks = [];
|
|
37584
37773
|
let settled = false;
|
|
37585
|
-
const child2 = (0,
|
|
37774
|
+
const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
37586
37775
|
const finish = () => {
|
|
37587
37776
|
if (settled) return;
|
|
37588
37777
|
settled = true;
|
|
@@ -37752,26 +37941,26 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
37752
37941
|
}
|
|
37753
37942
|
|
|
37754
37943
|
// src/worktree-merge-cleanup.ts
|
|
37755
|
-
var
|
|
37756
|
-
var
|
|
37944
|
+
var import_node_fs42 = require("node:fs");
|
|
37945
|
+
var import_node_path39 = require("node:path");
|
|
37757
37946
|
|
|
37758
37947
|
// src/jervcode-node-modules-cleanup.ts
|
|
37759
|
-
var
|
|
37760
|
-
var
|
|
37761
|
-
var
|
|
37762
|
-
var JERVCODE_PACKAGE_ENTRY = (0,
|
|
37948
|
+
var import_node_fs40 = require("node:fs");
|
|
37949
|
+
var import_node_os20 = require("node:os");
|
|
37950
|
+
var import_node_path37 = require("node:path");
|
|
37951
|
+
var JERVCODE_PACKAGE_ENTRY = (0, import_node_path37.join)("node_modules", "@jervaise", "jervcode", "dist", "launcher-entry.js");
|
|
37763
37952
|
var WIN_NAMES2 = ["jervcode.cmd", "jervcode"];
|
|
37764
37953
|
var POSIX_NAMES2 = ["jervcode"];
|
|
37765
37954
|
var NODE_MODULES_CLEANUP_TIMEOUT_MS = 3e5;
|
|
37766
|
-
function jervcodeCandidatePaths(env = process.env, home = (0,
|
|
37955
|
+
function jervcodeCandidatePaths(env = process.env, home = (0, import_node_os20.homedir)(), platform2 = process.platform) {
|
|
37767
37956
|
const names = platform2 === "win32" ? WIN_NAMES2 : POSIX_NAMES2;
|
|
37768
37957
|
const out = [];
|
|
37769
37958
|
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
37770
|
-
for (const name of names) out.push((0,
|
|
37959
|
+
for (const name of names) out.push((0, import_node_path37.join)(dir, name));
|
|
37771
37960
|
}
|
|
37772
37961
|
return out;
|
|
37773
37962
|
}
|
|
37774
|
-
function resolveJervcodePath(env = process.env, home = (0,
|
|
37963
|
+
function resolveJervcodePath(env = process.env, home = (0, import_node_os20.homedir)(), platform2 = process.platform, exists = import_node_fs40.existsSync) {
|
|
37775
37964
|
for (const candidate of jervcodeCandidatePaths(env, home, platform2)) {
|
|
37776
37965
|
if (exists(candidate)) return candidate;
|
|
37777
37966
|
}
|
|
@@ -37779,10 +37968,10 @@ function resolveJervcodePath(env = process.env, home = (0, import_node_os19.home
|
|
|
37779
37968
|
}
|
|
37780
37969
|
function jervcodeExecFileArgs(args, opts = {}) {
|
|
37781
37970
|
const platform2 = opts.platform ?? process.platform;
|
|
37782
|
-
const exists = opts.exists ??
|
|
37783
|
-
const resolved = resolveJervcodePath(opts.env ?? process.env, opts.home ?? (0,
|
|
37971
|
+
const exists = opts.exists ?? import_node_fs40.existsSync;
|
|
37972
|
+
const resolved = resolveJervcodePath(opts.env ?? process.env, opts.home ?? (0, import_node_os20.homedir)(), platform2, exists);
|
|
37784
37973
|
if (resolved) {
|
|
37785
|
-
const entry = (0,
|
|
37974
|
+
const entry = (0, import_node_path37.join)((0, import_node_path37.join)(resolved, ".."), JERVCODE_PACKAGE_ENTRY);
|
|
37786
37975
|
if (exists(entry)) {
|
|
37787
37976
|
return { file: opts.execPath ?? process.execPath, args: [entry, ...args], via: "node-entry" };
|
|
37788
37977
|
}
|
|
@@ -37813,8 +38002,8 @@ async function removeWorktreeNodeModulesViaHelper(wtPath, opts = {}) {
|
|
|
37813
38002
|
}
|
|
37814
38003
|
|
|
37815
38004
|
// src/worktree-evidence-archive.ts
|
|
37816
|
-
var
|
|
37817
|
-
var
|
|
38005
|
+
var import_node_fs41 = require("node:fs");
|
|
38006
|
+
var import_node_path38 = require("node:path");
|
|
37818
38007
|
var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
|
|
37819
38008
|
function sanitizeArchiveSegment(value, max = 80) {
|
|
37820
38009
|
const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -37829,24 +38018,24 @@ function resolveArtifactRunScope(env = process.env) {
|
|
|
37829
38018
|
}
|
|
37830
38019
|
function defaultIsDirectory(path2) {
|
|
37831
38020
|
try {
|
|
37832
|
-
return (0,
|
|
38021
|
+
return (0, import_node_fs41.lstatSync)(path2).isDirectory();
|
|
37833
38022
|
} catch {
|
|
37834
38023
|
return false;
|
|
37835
38024
|
}
|
|
37836
38025
|
}
|
|
37837
38026
|
function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
37838
|
-
const exists = deps.exists ??
|
|
38027
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
37839
38028
|
const isDirectory = deps.isDirectory ?? defaultIsDirectory;
|
|
37840
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
38029
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
|
|
37841
38030
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
37842
|
-
(0,
|
|
38031
|
+
(0, import_node_fs41.mkdirSync)(path2, { recursive: true });
|
|
37843
38032
|
});
|
|
37844
38033
|
const env = deps.env ?? process.env;
|
|
37845
38034
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
37846
38035
|
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
37847
38036
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
37848
38037
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
37849
|
-
const source = (0,
|
|
38038
|
+
const source = (0, import_node_path38.join)(args.worktreePath, ".jerv");
|
|
37850
38039
|
if (!exists(source)) return { status: "absent" };
|
|
37851
38040
|
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
37852
38041
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -37854,7 +38043,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
37854
38043
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
37855
38044
|
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
37856
38045
|
try {
|
|
37857
|
-
mkdirp((0,
|
|
38046
|
+
mkdirp((0, import_node_path38.dirname)(dest));
|
|
37858
38047
|
copyDir(source, dest);
|
|
37859
38048
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
37860
38049
|
return { status: "archived", path: dest, runScope };
|
|
@@ -37863,14 +38052,14 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
37863
38052
|
}
|
|
37864
38053
|
}
|
|
37865
38054
|
function defaultStat(path2) {
|
|
37866
|
-
const st = (0,
|
|
38055
|
+
const st = (0, import_node_fs41.statSync)(path2);
|
|
37867
38056
|
return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
|
|
37868
38057
|
}
|
|
37869
38058
|
function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
37870
|
-
const exists = deps.exists ??
|
|
38059
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
37871
38060
|
const stat4 = deps.stat ?? defaultStat;
|
|
37872
|
-
const readdir2 = deps.readdir ??
|
|
37873
|
-
const tmpRoot = (0,
|
|
38061
|
+
const readdir2 = deps.readdir ?? import_node_fs41.readdirSync;
|
|
38062
|
+
const tmpRoot = (0, import_node_path38.join)(worktreePath, "tmp");
|
|
37874
38063
|
if (!exists(tmpRoot)) return [];
|
|
37875
38064
|
const entries = [];
|
|
37876
38065
|
const walk2 = (dir) => {
|
|
@@ -37881,14 +38070,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
37881
38070
|
return;
|
|
37882
38071
|
}
|
|
37883
38072
|
for (const name of names) {
|
|
37884
|
-
const full = (0,
|
|
38073
|
+
const full = (0, import_node_path38.join)(dir, name);
|
|
37885
38074
|
let st;
|
|
37886
38075
|
try {
|
|
37887
38076
|
st = stat4(full);
|
|
37888
38077
|
} catch {
|
|
37889
38078
|
continue;
|
|
37890
38079
|
}
|
|
37891
|
-
const relPath = (0,
|
|
38080
|
+
const relPath = (0, import_node_path38.relative)(worktreePath, full).replace(/\\/g, "/");
|
|
37892
38081
|
if (st.isDirectory()) {
|
|
37893
38082
|
if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
|
|
37894
38083
|
walk2(full);
|
|
@@ -37902,10 +38091,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
37902
38091
|
return entries;
|
|
37903
38092
|
}
|
|
37904
38093
|
function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
37905
|
-
const exists = deps.exists ??
|
|
37906
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
38094
|
+
const exists = deps.exists ?? import_node_fs41.existsSync;
|
|
38095
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
|
|
37907
38096
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
37908
|
-
(0,
|
|
38097
|
+
(0, import_node_fs41.mkdirSync)(path2, { recursive: true });
|
|
37909
38098
|
});
|
|
37910
38099
|
const env = deps.env ?? process.env;
|
|
37911
38100
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -37913,7 +38102,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
37913
38102
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
37914
38103
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
37915
38104
|
const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
|
|
37916
|
-
const source = (0,
|
|
38105
|
+
const source = (0, import_node_path38.join)(args.worktreePath, "tmp");
|
|
37917
38106
|
if (!scanned.length) return { status: "absent" };
|
|
37918
38107
|
if (!exists(source)) return { status: "absent" };
|
|
37919
38108
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -37921,7 +38110,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
37921
38110
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
37922
38111
|
const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
|
|
37923
38112
|
try {
|
|
37924
|
-
mkdirp((0,
|
|
38113
|
+
mkdirp((0, import_node_path38.dirname)(dest));
|
|
37925
38114
|
copyDir(source, dest);
|
|
37926
38115
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
37927
38116
|
const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
|
|
@@ -37990,9 +38179,9 @@ function normPath2(p) {
|
|
|
37990
38179
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
37991
38180
|
}
|
|
37992
38181
|
function unlinkNodeModulesJunction(wtPath) {
|
|
37993
|
-
const nm = (0,
|
|
38182
|
+
const nm = (0, import_node_path39.join)(wtPath, "node_modules");
|
|
37994
38183
|
try {
|
|
37995
|
-
if ((0,
|
|
38184
|
+
if ((0, import_node_fs42.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs42.rmdirSync)(nm);
|
|
37996
38185
|
return { ok: true };
|
|
37997
38186
|
} catch (e) {
|
|
37998
38187
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -38003,8 +38192,8 @@ function defaultSleep2(ms) {
|
|
|
38003
38192
|
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
38004
38193
|
}
|
|
38005
38194
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
38006
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
38007
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
38195
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs42.rmdirSync, exists = import_node_fs42.existsSync } = options;
|
|
38196
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs42.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
38008
38197
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
38009
38198
|
if (!junction.ok) return junction;
|
|
38010
38199
|
try {
|
|
@@ -38127,31 +38316,31 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
38127
38316
|
}
|
|
38128
38317
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
38129
38318
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
38130
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
38319
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path39.join)(wtPath, line.slice(0, -1)));
|
|
38131
38320
|
}
|
|
38132
38321
|
function safeRemoveTree(path2) {
|
|
38133
|
-
const stat4 = (0,
|
|
38322
|
+
const stat4 = (0, import_node_fs42.lstatSync)(path2);
|
|
38134
38323
|
if (stat4.isSymbolicLink()) {
|
|
38135
38324
|
try {
|
|
38136
|
-
(0,
|
|
38325
|
+
(0, import_node_fs42.rmdirSync)(path2);
|
|
38137
38326
|
} catch {
|
|
38138
|
-
(0,
|
|
38327
|
+
(0, import_node_fs42.unlinkSync)(path2);
|
|
38139
38328
|
}
|
|
38140
38329
|
return;
|
|
38141
38330
|
}
|
|
38142
38331
|
if (stat4.isDirectory()) {
|
|
38143
|
-
for (const entry of (0,
|
|
38144
|
-
(0,
|
|
38332
|
+
for (const entry of (0, import_node_fs42.readdirSync)(path2)) safeRemoveTree((0, import_node_path39.join)(path2, entry));
|
|
38333
|
+
(0, import_node_fs42.rmdirSync)(path2);
|
|
38145
38334
|
return;
|
|
38146
38335
|
}
|
|
38147
|
-
(0,
|
|
38336
|
+
(0, import_node_fs42.unlinkSync)(path2);
|
|
38148
38337
|
}
|
|
38149
38338
|
function errorMessage(e) {
|
|
38150
38339
|
return e instanceof Error ? e.message : String(e);
|
|
38151
38340
|
}
|
|
38152
38341
|
function resolvedOrRaw(path2) {
|
|
38153
38342
|
try {
|
|
38154
|
-
return normPath2((0,
|
|
38343
|
+
return normPath2((0, import_node_fs42.realpathSync)(path2));
|
|
38155
38344
|
} catch {
|
|
38156
38345
|
return normPath2(path2);
|
|
38157
38346
|
}
|
|
@@ -38159,10 +38348,10 @@ function resolvedOrRaw(path2) {
|
|
|
38159
38348
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
38160
38349
|
let realRoot;
|
|
38161
38350
|
try {
|
|
38162
|
-
if ((0,
|
|
38351
|
+
if ((0, import_node_fs42.lstatSync)(root).isSymbolicLink()) {
|
|
38163
38352
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
38164
38353
|
}
|
|
38165
|
-
realRoot = normPath2((0,
|
|
38354
|
+
realRoot = normPath2((0, import_node_fs42.realpathSync)(root));
|
|
38166
38355
|
} catch (e) {
|
|
38167
38356
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
38168
38357
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -38178,16 +38367,16 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
38178
38367
|
const dir = stack.pop();
|
|
38179
38368
|
let entries;
|
|
38180
38369
|
try {
|
|
38181
|
-
entries = (0,
|
|
38370
|
+
entries = (0, import_node_fs42.readdirSync)(dir, { withFileTypes: true });
|
|
38182
38371
|
} catch (e) {
|
|
38183
38372
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
38184
38373
|
}
|
|
38185
38374
|
for (const entry of entries) {
|
|
38186
|
-
const child2 = (0,
|
|
38375
|
+
const child2 = (0, import_node_path39.join)(dir, entry.name);
|
|
38187
38376
|
if (entry.isSymbolicLink()) {
|
|
38188
38377
|
let target = "";
|
|
38189
38378
|
try {
|
|
38190
|
-
target = normPath2((0,
|
|
38379
|
+
target = normPath2((0, import_node_fs42.realpathSync)(child2));
|
|
38191
38380
|
} catch {
|
|
38192
38381
|
target = "";
|
|
38193
38382
|
}
|
|
@@ -38217,7 +38406,7 @@ async function describePreCleanFailure(wtPath, execGit, error) {
|
|
|
38217
38406
|
const more = remaining.length > 10 ? ` (+${remaining.length - 10} more)` : "";
|
|
38218
38407
|
const quote = (path2) => path2.replace(/'/g, "''");
|
|
38219
38408
|
const nested = remaining.find((path2) => path2.endsWith("node_modules/") && path2 !== "node_modules/");
|
|
38220
|
-
const remediation = remaining.includes("node_modules/") ? `jervcode worktree-node-modules-cleanup --worktree '${quote(wtPath)}'` : nested ? `Remove-Item -LiteralPath '${quote((0,
|
|
38409
|
+
const remediation = remaining.includes("node_modules/") ? `jervcode worktree-node-modules-cleanup --worktree '${quote(wtPath)}'` : nested ? `Remove-Item -LiteralPath '${quote((0, import_node_path39.join)(wtPath, nested.slice(0, -1)))}' -Recurse -Force` : void 0;
|
|
38221
38410
|
return { ok: false, error: `${error}; remaining ignored paths: ${shown}${more}`, ...remediation ? { remediation } : {} };
|
|
38222
38411
|
}
|
|
38223
38412
|
function formatWorktreeRemovalFailureDetail(options) {
|
|
@@ -38371,7 +38560,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
38371
38560
|
return report;
|
|
38372
38561
|
}
|
|
38373
38562
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
38374
|
-
const pathExists = options.pathExists ??
|
|
38563
|
+
const pathExists = options.pathExists ?? import_node_fs42.existsSync;
|
|
38375
38564
|
let afterWorktrees = [];
|
|
38376
38565
|
try {
|
|
38377
38566
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -38511,7 +38700,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
38511
38700
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
38512
38701
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
38513
38702
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
38514
|
-
if (pathExists((0,
|
|
38703
|
+
if (pathExists((0, import_node_path39.join)(wtPath, "node_modules"))) {
|
|
38515
38704
|
const nmRemoved = await (options.removeRealNodeModules ?? ((p) => removeWorktreeNodeModulesViaHelper(p, { cwd: mainWorktreePath })))(wtPath);
|
|
38516
38705
|
if (!nmRemoved.ok) {
|
|
38517
38706
|
report.worktree = {
|
|
@@ -38678,10 +38867,10 @@ function argvWantsJson2() {
|
|
|
38678
38867
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
38679
38868
|
}
|
|
38680
38869
|
function hubRoot() {
|
|
38681
|
-
const fromPkg = (0,
|
|
38870
|
+
const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
|
|
38682
38871
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
38683
|
-
if ((0,
|
|
38684
|
-
if ((0,
|
|
38872
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(fromPkg, marker))) return fromPkg;
|
|
38873
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(process.cwd(), marker))) return process.cwd();
|
|
38685
38874
|
return null;
|
|
38686
38875
|
}
|
|
38687
38876
|
function ciAuditDeps() {
|
|
@@ -38693,8 +38882,8 @@ function ciAuditDeps() {
|
|
|
38693
38882
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
38694
38883
|
readSeedFile: (path2) => {
|
|
38695
38884
|
if (!root) return null;
|
|
38696
|
-
const fullPath = (0,
|
|
38697
|
-
return (0,
|
|
38885
|
+
const fullPath = (0, import_node_path40.join)(root, path2);
|
|
38886
|
+
return (0, import_node_fs43.existsSync)(fullPath) ? (0, import_node_fs43.readFileSync)(fullPath, "utf8") : null;
|
|
38698
38887
|
}
|
|
38699
38888
|
};
|
|
38700
38889
|
}
|
|
@@ -38776,7 +38965,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
38776
38965
|
try {
|
|
38777
38966
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
38778
38967
|
if (o.repo) args.push("--repo", o.repo);
|
|
38779
|
-
spawnDetachedSelf(args, { spawn:
|
|
38968
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
38780
38969
|
} catch {
|
|
38781
38970
|
}
|
|
38782
38971
|
}
|
|
@@ -39199,6 +39388,7 @@ ${list}`);
|
|
|
39199
39388
|
}
|
|
39200
39389
|
}
|
|
39201
39390
|
program3.command("skill-lesson").description("file a skill-lesson on the Hub board (GitHub auth, dedups open lessons) and print {number,url} JSON").addOption(new Option("--skill <name>", `which skill misfired (${SKILL_NAMES.join(" | ")})`).makeOptionMandatory().choices([...SKILL_NAMES])).option("--title <title>", "one-line summary of what misfired").option("--title-file <path|->", "read the one-line summary from a UTF-8 file, or from stdin with -").option("--body <body>", "lesson body: what misfired, the evidence, and the proposed amendment (markdown)").option("--body-file <path|->", "read the lesson body from a UTF-8 file, or from stdin with -").option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only, #416)").option("--repo <owner/repo>", `target repo (defaults to the org Hub: ${HUB_REPO})`).option("--force", "file a new issue even when an open lesson looks like a duplicate").option("--json", "machine-readable output (already the default \u2014 skill-lesson always prints JSON)").action(async (o) => {
|
|
39391
|
+
warnSkillLessonDeprecated();
|
|
39202
39392
|
const targetRepo2 = o.repo ?? HUB_REPO;
|
|
39203
39393
|
const sourceRepo = await resolveRepo(void 0);
|
|
39204
39394
|
const pluginSha = await resolvePluginSha();
|
|
@@ -39257,7 +39447,7 @@ ${list}`);
|
|
|
39257
39447
|
});
|
|
39258
39448
|
if (surfaceWarn) process.stderr.write(`${surfaceWarn}
|
|
39259
39449
|
`);
|
|
39260
|
-
for (const [label, color] of [[SKILL_LESSON_LABEL, "c2e0c6"], [
|
|
39450
|
+
for (const [label, color] of [[SKILL_LESSON_LABEL, "c2e0c6"], [LEARNING_LABEL2, "ededed"]]) {
|
|
39261
39451
|
try {
|
|
39262
39452
|
await execFileP("gh", ["label", "create", label, "--color", color, "--repo", targetRepo2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
39263
39453
|
} catch {
|
|
@@ -39336,11 +39526,11 @@ ${list}`);
|
|
|
39336
39526
|
}
|
|
39337
39527
|
});
|
|
39338
39528
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
39339
|
-
const wfDir = (0,
|
|
39340
|
-
if (!(0,
|
|
39341
|
-
return (0,
|
|
39529
|
+
const wfDir = (0, import_node_path40.join)(cwd, ".github", "workflows");
|
|
39530
|
+
if (!(0, import_node_fs43.existsSync)(wfDir)) return [];
|
|
39531
|
+
return (0, import_node_fs43.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
39342
39532
|
try {
|
|
39343
|
-
return workflowReportsPrChecks((0,
|
|
39533
|
+
return workflowReportsPrChecks((0, import_node_fs43.readFileSync)((0, import_node_path40.join)(wfDir, name), "utf8"));
|
|
39344
39534
|
} catch {
|
|
39345
39535
|
return true;
|
|
39346
39536
|
}
|
|
@@ -39528,7 +39718,28 @@ ${list}`);
|
|
|
39528
39718
|
if (result.status === "failure" || result.status === "conflicting") process.exitCode = 1;
|
|
39529
39719
|
if (result.status === "timeout" || result.status === "rate-limited") process.exitCode = PR_CHECKS_TIMEOUT_EXIT_CODE;
|
|
39530
39720
|
});
|
|
39531
|
-
pr.command("
|
|
39721
|
+
jsonParity(pr.command("review-verdict <number>").description("post the ZeroCI review verdict comment the merge helper requires (#6255): computes the PR diff patch-id and head sha itself, renders the v1 contract body, posts it as a PR comment. Only a PROCEED bound to the CURRENT diff lets `pr merge` / `pr land` merge").requiredOption("--repo <owner/repo>", "target repo").requiredOption("--verdict <verdict>", `one of ${REVIEW_VERDICTS.join("|")}`).requiredOption("--scope <text>", "one line: what was reviewed").requiredOption("--risk <text>", "one line: the residual risk").option("--unverified <text>", "a claim the reviewer could not verify (repeatable)", (v, acc) => [...acc, v], []).requiredOption("--reviewer <id>", "reviewer seat or model id").option("--findings-file <path>", "Markdown findings appended after the JSON block")).action(async (number, o) => {
|
|
39722
|
+
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
39723
|
+
const verdict = o.verdict;
|
|
39724
|
+
const findings = o.findingsFile ? (0, import_node_fs43.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
39725
|
+
let patch;
|
|
39726
|
+
let head;
|
|
39727
|
+
try {
|
|
39728
|
+
[patch, head] = await Promise.all([computePrPatchId(number, o.repo), readPrHeadSha(number, o.repo)]);
|
|
39729
|
+
} catch (e) {
|
|
39730
|
+
return failGraceful(`pr review-verdict: ${e.message}`);
|
|
39731
|
+
}
|
|
39732
|
+
const body = renderReviewVerdictComment({ v: 1, patch, head, verdict, scope: o.scope, risk: o.risk, unverified: o.unverified, reviewer: o.reviewer, findings });
|
|
39733
|
+
let commentUrl;
|
|
39734
|
+
try {
|
|
39735
|
+
commentUrl = await postPrCommentFromFile(number, o.repo, body);
|
|
39736
|
+
} catch (e) {
|
|
39737
|
+
const err = e;
|
|
39738
|
+
return failGraceful(`pr review-verdict: ${(err.stderr || err.message || String(e)).trim()}`);
|
|
39739
|
+
}
|
|
39740
|
+
printLine(JSON.stringify({ number: Number(number), repo: o.repo, verdict, patch, head, commentUrl }));
|
|
39741
|
+
});
|
|
39742
|
+
pr.command("land <number>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "acknowledge and land 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").option("--without-review <reason>", "land without a review verdict (#6255): posts a `zeroci-review-waived` comment carrying the reason and actor, then proceeds").action(async (number, o) => {
|
|
39532
39743
|
if (/^(?:[^/]+\/[^/]+)?#\d+$/.test(number.trim())) {
|
|
39533
39744
|
try {
|
|
39534
39745
|
const parsed = parseIssueRef(number, o.repo);
|
|
@@ -39563,6 +39774,15 @@ ${list}`);
|
|
|
39563
39774
|
return;
|
|
39564
39775
|
}
|
|
39565
39776
|
if (landClosingGuardVerdict.message) console.warn(landClosingGuardVerdict.message);
|
|
39777
|
+
{
|
|
39778
|
+
const gateRepo = landRepoForGuard ?? await requireRepo(o.repo);
|
|
39779
|
+
const refusal = await requireReviewVerdict("pr land", number, gateRepo, { withoutReview: o.withoutReview });
|
|
39780
|
+
if (refusal) {
|
|
39781
|
+
console.error(refusal);
|
|
39782
|
+
process.exitCode = 1;
|
|
39783
|
+
return;
|
|
39784
|
+
}
|
|
39785
|
+
}
|
|
39566
39786
|
const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false }, {
|
|
39567
39787
|
resolveRepo: async (prNumber, repoOpt) => {
|
|
39568
39788
|
const args = repoOpt ? ["--repo", repoOpt] : repoArgs;
|
|
@@ -39697,7 +39917,7 @@ ${list}`);
|
|
|
39697
39917
|
else printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
|
|
39698
39918
|
if (result.status === "failed") process.exitCode = 1;
|
|
39699
39919
|
});
|
|
39700
|
-
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); from a foreign checkout the remote probe/delete address this repo and local cleanup runs only in the verified sibling checkout ../<repo>, else localBranch reports skipped-foreign-cwd and the receipt carries foreignCwd: true (#6148)").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) \u2014 run as a background/monitor task or under a shell timeout above that budget; a short foreground timeout (e.g. 120s) kills it after checks pass and leaves the PR open (#6027)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888); also the merge settings-proof bypass when delete_branch_on_merge cannot be proven (#6210)").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) => {
|
|
39920
|
+
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); from a foreign checkout the remote probe/delete address this repo and local cleanup runs only in the verified sibling checkout ../<repo>, else localBranch reports skipped-foreign-cwd and the receipt carries foreignCwd: true (#6148)").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) \u2014 run as a background/monitor task or under a shell timeout above that budget; a short foreground timeout (e.g. 120s) kills it after checks pass and leaves the PR open (#6027)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888); also the merge settings-proof bypass when delete_branch_on_merge cannot be proven (#6210)").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").option("--without-review <reason>", "merge without a review verdict (#6255): posts a `zeroci-review-waived` comment carrying the reason and actor, then proceeds")).action(async (number, o) => {
|
|
39701
39921
|
const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
|
|
39702
39922
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
39703
39923
|
if (o.disableAuto) {
|
|
@@ -39735,7 +39955,7 @@ ${list}`);
|
|
|
39735
39955
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
39736
39956
|
closingGuardInput,
|
|
39737
39957
|
method === "--squash",
|
|
39738
|
-
o.squashBodyFile ? (0,
|
|
39958
|
+
o.squashBodyFile ? (0, import_node_fs43.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
39739
39959
|
);
|
|
39740
39960
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
39741
39961
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -39749,6 +39969,15 @@ ${list}`);
|
|
|
39749
39969
|
return;
|
|
39750
39970
|
}
|
|
39751
39971
|
if (closingGuardVerdict.message) console.warn(closingGuardVerdict.message);
|
|
39972
|
+
if (prMeta.state !== "MERGED") {
|
|
39973
|
+
const gateRepo = repoForPostCleanup ?? await requireRepo(o.repo);
|
|
39974
|
+
const refusal = await requireReviewVerdict("pr merge", number, gateRepo, { withoutReview: o.withoutReview });
|
|
39975
|
+
if (refusal) {
|
|
39976
|
+
console.error(refusal);
|
|
39977
|
+
process.exitCode = 1;
|
|
39978
|
+
return;
|
|
39979
|
+
}
|
|
39980
|
+
}
|
|
39752
39981
|
if (prMeta.state !== "MERGED" && !o.preserveWorktree) {
|
|
39753
39982
|
if (!repoForPostCleanup) throw new Error("pr merge: repository is unreadable; cannot prove remote branch preservation");
|
|
39754
39983
|
const repoSettings = await fetchRestRepoMergeSettings(repoForPostCleanup);
|
|
@@ -39767,7 +39996,7 @@ ${list}`);
|
|
|
39767
39996
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
39768
39997
|
let foreignCheckout;
|
|
39769
39998
|
if (foreignCwd) {
|
|
39770
|
-
const sibling = (0,
|
|
39999
|
+
const sibling = (0, import_node_path40.join)((0, import_node_path40.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
39771
40000
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
39772
40001
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
39773
40002
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -39859,7 +40088,7 @@ ${list}`);
|
|
|
39859
40088
|
}
|
|
39860
40089
|
if (!repoForPostCleanup) throw e;
|
|
39861
40090
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
39862
|
-
const commitMessage = bodyFile ? (0,
|
|
40091
|
+
const commitMessage = bodyFile ? (0, import_node_fs43.readFileSync)(bodyFile, "utf8") : void 0;
|
|
39863
40092
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
39864
40093
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
39865
40094
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -39965,7 +40194,7 @@ ${list}`);
|
|
|
39965
40194
|
preserveWorktree: o.preserveWorktree,
|
|
39966
40195
|
gcAcknowledged: o.gc,
|
|
39967
40196
|
expectedHeadOid: headRefOid,
|
|
39968
|
-
pathExists: (p) => (0,
|
|
40197
|
+
pathExists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
39969
40198
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
39970
40199
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
39971
40200
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -40086,7 +40315,7 @@ ${list}`);
|
|
|
40086
40315
|
}
|
|
40087
40316
|
|
|
40088
40317
|
// src/command-register-developer.ts
|
|
40089
|
-
var
|
|
40318
|
+
var import_node_fs48 = require("node:fs");
|
|
40090
40319
|
|
|
40091
40320
|
// src/whoami.ts
|
|
40092
40321
|
async function resolveWhoami(deps) {
|
|
@@ -40116,7 +40345,7 @@ async function resolveWhoami(deps) {
|
|
|
40116
40345
|
}
|
|
40117
40346
|
|
|
40118
40347
|
// src/command-register-developer.ts
|
|
40119
|
-
var
|
|
40348
|
+
var import_node_path45 = require("node:path");
|
|
40120
40349
|
|
|
40121
40350
|
// src/wave-land.ts
|
|
40122
40351
|
function planWaveLand(prs) {
|
|
@@ -40147,7 +40376,7 @@ async function executeWaveLand(plan, deps) {
|
|
|
40147
40376
|
}
|
|
40148
40377
|
|
|
40149
40378
|
// src/box-commands.ts
|
|
40150
|
-
var
|
|
40379
|
+
var import_node_fs44 = require("node:fs");
|
|
40151
40380
|
|
|
40152
40381
|
// src/box.ts
|
|
40153
40382
|
var BOX_KEYS = {
|
|
@@ -40349,7 +40578,7 @@ function registerBoxCommands(program3) {
|
|
|
40349
40578
|
return;
|
|
40350
40579
|
}
|
|
40351
40580
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
40352
|
-
if (wroteScript) (0,
|
|
40581
|
+
if (wroteScript) (0, import_node_fs44.writeFileSync)(wroteScript, sshRecipeScript(found), "utf8");
|
|
40353
40582
|
if (o.json) {
|
|
40354
40583
|
console.log(JSON.stringify({
|
|
40355
40584
|
box: found,
|
|
@@ -40369,21 +40598,21 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
40369
40598
|
}
|
|
40370
40599
|
|
|
40371
40600
|
// src/dist-drift.ts
|
|
40372
|
-
var
|
|
40601
|
+
var import_node_child_process17 = require("node:child_process");
|
|
40373
40602
|
var import_node_crypto12 = require("node:crypto");
|
|
40374
|
-
var
|
|
40375
|
-
var
|
|
40376
|
-
var
|
|
40603
|
+
var import_node_fs46 = require("node:fs");
|
|
40604
|
+
var import_node_os21 = require("node:os");
|
|
40605
|
+
var import_node_path42 = require("node:path");
|
|
40377
40606
|
|
|
40378
40607
|
// ../scripts/distribution-digest.mjs
|
|
40379
40608
|
var import_node_crypto11 = require("node:crypto");
|
|
40380
|
-
var
|
|
40381
|
-
var
|
|
40609
|
+
var import_node_fs45 = require("node:fs");
|
|
40610
|
+
var import_node_path41 = require("node:path");
|
|
40382
40611
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
40383
40612
|
function repoPath(root, declaredPath, label) {
|
|
40384
|
-
const absoluteRoot = (0,
|
|
40385
|
-
const target = (0,
|
|
40386
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
40613
|
+
const absoluteRoot = (0, import_node_path41.resolve)(root);
|
|
40614
|
+
const target = (0, import_node_path41.resolve)(root, declaredPath);
|
|
40615
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path41.sep}`)) {
|
|
40387
40616
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
40388
40617
|
}
|
|
40389
40618
|
return target;
|
|
@@ -40391,7 +40620,7 @@ function repoPath(root, declaredPath, label) {
|
|
|
40391
40620
|
function digestFiles(files) {
|
|
40392
40621
|
const hash = (0, import_node_crypto11.createHash)("sha256");
|
|
40393
40622
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
40394
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
40623
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs45.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs45.readFileSync)(file.absolute);
|
|
40395
40624
|
hash.update(file.relative, "utf8");
|
|
40396
40625
|
hash.update("\0");
|
|
40397
40626
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -40406,8 +40635,8 @@ function digestFiles(files) {
|
|
|
40406
40635
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
40407
40636
|
return digestFiles(packedFiles.map((path2) => {
|
|
40408
40637
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
40409
|
-
if (!(0,
|
|
40410
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
40638
|
+
if (!(0, import_node_fs45.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
40639
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs45.lstatSync)(absolute) };
|
|
40411
40640
|
}));
|
|
40412
40641
|
}
|
|
40413
40642
|
|
|
@@ -40491,13 +40720,13 @@ function renderDistDriftReceipt(receipt) {
|
|
|
40491
40720
|
return lines2;
|
|
40492
40721
|
}
|
|
40493
40722
|
function readOrNull(path2) {
|
|
40494
|
-
return (0,
|
|
40723
|
+
return (0, import_node_fs46.existsSync)(path2) ? (0, import_node_fs46.readFileSync)(path2) : null;
|
|
40495
40724
|
}
|
|
40496
40725
|
function walkFiles(root) {
|
|
40497
40726
|
const files = [];
|
|
40498
40727
|
const walk2 = (directory) => {
|
|
40499
|
-
for (const entry of (0,
|
|
40500
|
-
const child2 = (0,
|
|
40728
|
+
for (const entry of (0, import_node_fs46.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
40729
|
+
const child2 = (0, import_node_path42.join)(directory, entry.name);
|
|
40501
40730
|
if (entry.isDirectory()) walk2(child2);
|
|
40502
40731
|
else files.push(child2);
|
|
40503
40732
|
}
|
|
@@ -40507,14 +40736,14 @@ function walkFiles(root) {
|
|
|
40507
40736
|
}
|
|
40508
40737
|
function bomPathFor(root) {
|
|
40509
40738
|
try {
|
|
40510
|
-
const registry2 = JSON.parse((0,
|
|
40511
|
-
return (0,
|
|
40739
|
+
const registry2 = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path42.join)(root, "surfaces.json"), "utf8"));
|
|
40740
|
+
return (0, import_node_path42.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
40512
40741
|
} catch {
|
|
40513
|
-
return (0,
|
|
40742
|
+
return (0, import_node_path42.join)(root, "distribution-bom.json");
|
|
40514
40743
|
}
|
|
40515
40744
|
}
|
|
40516
40745
|
function rebuildTo(packageRoot, outDir) {
|
|
40517
|
-
(0,
|
|
40746
|
+
(0, import_node_child_process17.execFileSync)(process.execPath, ["build.mjs"], {
|
|
40518
40747
|
cwd: packageRoot,
|
|
40519
40748
|
env: { ...process.env, MMI_DIST_OUTDIR: outDir },
|
|
40520
40749
|
windowsHide: true,
|
|
@@ -40523,35 +40752,35 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
40523
40752
|
});
|
|
40524
40753
|
}
|
|
40525
40754
|
function runDistStatus(root) {
|
|
40526
|
-
const stage = (0,
|
|
40755
|
+
const stage = (0, import_node_fs46.mkdtempSync)((0, import_node_path42.join)((0, import_node_os21.tmpdir)(), "mmi-dist-drift-"));
|
|
40527
40756
|
let overlayCount = 0;
|
|
40528
40757
|
try {
|
|
40529
|
-
const cliOut = (0,
|
|
40530
|
-
const hubOut = (0,
|
|
40531
|
-
rebuildTo((0,
|
|
40532
|
-
rebuildTo((0,
|
|
40758
|
+
const cliOut = (0, import_node_path42.join)(stage, "cli-dist");
|
|
40759
|
+
const hubOut = (0, import_node_path42.join)(stage, "hub-dist");
|
|
40760
|
+
rebuildTo((0, import_node_path42.join)(root, "cli"), cliOut);
|
|
40761
|
+
rebuildTo((0, import_node_path42.join)(root, "updater"), hubOut);
|
|
40533
40762
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
40534
40763
|
const rebuilt = (path2) => {
|
|
40535
40764
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
40536
|
-
return spec ? readOrNull((0,
|
|
40765
|
+
return spec ? readOrNull((0, import_node_path42.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
40537
40766
|
};
|
|
40538
|
-
const committed = (path2) => readOrNull((0,
|
|
40539
|
-
const tree = (path2) => readOrNull((0,
|
|
40540
|
-
const distRoot = (0,
|
|
40541
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
40542
|
-
const bom = JSON.parse((0,
|
|
40767
|
+
const committed = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
|
|
40768
|
+
const tree = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
|
|
40769
|
+
const distRoot = (0, import_node_path42.join)(root, "cli", "dist");
|
|
40770
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path42.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
40771
|
+
const bom = JSON.parse((0, import_node_fs46.readFileSync)(bomPathFor(root), "utf8"));
|
|
40543
40772
|
const digest = (entries) => {
|
|
40544
|
-
const overlay = (0,
|
|
40773
|
+
const overlay = (0, import_node_path42.join)(stage, `overlay-${overlayCount++}`);
|
|
40545
40774
|
for (const entry of entries) {
|
|
40546
|
-
const target = (0,
|
|
40547
|
-
(0,
|
|
40548
|
-
(0,
|
|
40775
|
+
const target = (0, import_node_path42.join)(overlay, entry.path);
|
|
40776
|
+
(0, import_node_fs46.mkdirSync)((0, import_node_path42.dirname)(target), { recursive: true });
|
|
40777
|
+
(0, import_node_fs46.writeFileSync)(target, entry.bytes);
|
|
40549
40778
|
}
|
|
40550
40779
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
40551
40780
|
};
|
|
40552
40781
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
40553
40782
|
} finally {
|
|
40554
|
-
(0,
|
|
40783
|
+
(0, import_node_fs46.rmSync)(stage, { recursive: true, force: true });
|
|
40555
40784
|
}
|
|
40556
40785
|
}
|
|
40557
40786
|
|
|
@@ -40623,7 +40852,7 @@ function registerEdgeCommands(program3) {
|
|
|
40623
40852
|
|
|
40624
40853
|
// src/schedules-lift-command.ts
|
|
40625
40854
|
var import_promises8 = require("node:fs/promises");
|
|
40626
|
-
var
|
|
40855
|
+
var import_node_path43 = require("node:path");
|
|
40627
40856
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
40628
40857
|
var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
|
|
40629
40858
|
var SchedulesLiftUsageError = class extends Error {
|
|
@@ -40650,7 +40879,7 @@ async function readWorkflowFiles(dir) {
|
|
|
40650
40879
|
const files = [];
|
|
40651
40880
|
for (const name of names.sort()) {
|
|
40652
40881
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
40653
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0,
|
|
40882
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0, import_node_path43.join)(dir, name), "utf8") });
|
|
40654
40883
|
}
|
|
40655
40884
|
return files;
|
|
40656
40885
|
}
|
|
@@ -40732,9 +40961,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
40732
40961
|
}
|
|
40733
40962
|
|
|
40734
40963
|
// src/spawn-policy-core.ts
|
|
40735
|
-
var
|
|
40736
|
-
var
|
|
40737
|
-
var
|
|
40964
|
+
var import_node_child_process18 = require("node:child_process");
|
|
40965
|
+
var import_node_fs47 = require("node:fs");
|
|
40966
|
+
var import_node_path44 = require("node:path");
|
|
40738
40967
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
40739
40968
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
40740
40969
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -40803,7 +41032,7 @@ function findViolationsInSource(raw) {
|
|
|
40803
41032
|
return found;
|
|
40804
41033
|
}
|
|
40805
41034
|
function policedFiles(root) {
|
|
40806
|
-
const r = (0,
|
|
41035
|
+
const r = (0, import_node_child_process18.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
40807
41036
|
cwd: root,
|
|
40808
41037
|
encoding: "utf8",
|
|
40809
41038
|
windowsHide: true,
|
|
@@ -40820,7 +41049,7 @@ function runSpawnPolicy(root) {
|
|
|
40820
41049
|
for (const file of files) {
|
|
40821
41050
|
let raw;
|
|
40822
41051
|
try {
|
|
40823
|
-
raw = (0,
|
|
41052
|
+
raw = (0, import_node_fs47.readFileSync)((0, import_node_path44.join)(root, file), "utf8");
|
|
40824
41053
|
} catch {
|
|
40825
41054
|
continue;
|
|
40826
41055
|
}
|
|
@@ -40840,19 +41069,19 @@ function runSpawnPolicy(root) {
|
|
|
40840
41069
|
function registerDeveloperCommands(program3) {
|
|
40841
41070
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
40842
41071
|
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) => {
|
|
40843
|
-
const path2 = (0,
|
|
40844
|
-
const current = (0,
|
|
41072
|
+
const path2 = (0, import_node_path45.join)(process.cwd(), ".gitignore");
|
|
41073
|
+
const current = (0, import_node_fs48.existsSync)(path2) ? (0, import_node_fs48.readFileSync)(path2, "utf8") : null;
|
|
40845
41074
|
const plan = planManagedGitignore(current);
|
|
40846
41075
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
40847
41076
|
if (opts.json) {
|
|
40848
|
-
if (opts.write && plan.changed) (0,
|
|
41077
|
+
if (opts.write && plan.changed) (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
40849
41078
|
console.log(JSON.stringify(plan, null, 2));
|
|
40850
41079
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
40851
41080
|
return;
|
|
40852
41081
|
}
|
|
40853
41082
|
if (opts.write) {
|
|
40854
41083
|
if (plan.changed) {
|
|
40855
|
-
(0,
|
|
41084
|
+
(0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
40856
41085
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
40857
41086
|
} else {
|
|
40858
41087
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -42340,12 +42569,12 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = H
|
|
|
42340
42569
|
}
|
|
42341
42570
|
|
|
42342
42571
|
// src/hotfix-coverage.ts
|
|
42343
|
-
var
|
|
42572
|
+
var import_node_child_process19 = require("node:child_process");
|
|
42344
42573
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
42345
42574
|
function checkHotfixCoverage(options = {}) {
|
|
42346
42575
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
42347
42576
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
42348
|
-
const git3 = options.git ?? ((args, opts) => (0,
|
|
42577
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
42349
42578
|
const revList = (range) => {
|
|
42350
42579
|
const out = git3(["rev-list", "--no-merges", range]).trim();
|
|
42351
42580
|
return out ? out.split("\n") : [];
|
|
@@ -42413,7 +42642,7 @@ function checkHotfixCoverage(options = {}) {
|
|
|
42413
42642
|
}
|
|
42414
42643
|
function checkHotfixCarries(options) {
|
|
42415
42644
|
const { cwd = process.cwd(), branch, baseRef, targets } = options;
|
|
42416
|
-
const git3 = options.git ?? ((args, opts) => (0,
|
|
42645
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
42417
42646
|
const isAncestor = (sha, ref) => {
|
|
42418
42647
|
try {
|
|
42419
42648
|
git3(["merge-base", "--is-ancestor", sha, ref]);
|
|
@@ -42445,8 +42674,8 @@ function checkHotfixCarries(options) {
|
|
|
42445
42674
|
}
|
|
42446
42675
|
|
|
42447
42676
|
// src/train-commands.ts
|
|
42448
|
-
var
|
|
42449
|
-
var
|
|
42677
|
+
var import_node_fs49 = require("node:fs");
|
|
42678
|
+
var import_node_path46 = require("node:path");
|
|
42450
42679
|
var INVOKED_ARGV = process.argv.slice(2);
|
|
42451
42680
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
42452
42681
|
function resolveReleaseBumpIntent(raw) {
|
|
@@ -42458,7 +42687,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
42458
42687
|
}
|
|
42459
42688
|
function readRepoVersion() {
|
|
42460
42689
|
try {
|
|
42461
|
-
return JSON.parse((0,
|
|
42690
|
+
return JSON.parse((0, import_node_fs49.readFileSync)((0, import_node_path46.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
42462
42691
|
} catch {
|
|
42463
42692
|
return void 0;
|
|
42464
42693
|
}
|
|
@@ -43202,12 +43431,12 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
43202
43431
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
43203
43432
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
43204
43433
|
function envHealLockPath(home) {
|
|
43205
|
-
return (0,
|
|
43434
|
+
return (0, import_node_path47.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
43206
43435
|
}
|
|
43207
43436
|
async function withEnvHealLock(what, run) {
|
|
43208
43437
|
try {
|
|
43209
43438
|
return await withFileLock(
|
|
43210
|
-
envHealLockPath((0,
|
|
43439
|
+
envHealLockPath((0, import_node_os22.homedir)()),
|
|
43211
43440
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
43212
43441
|
run
|
|
43213
43442
|
);
|
|
@@ -43303,7 +43532,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
43303
43532
|
const configRoot = surfaceConfigRoot(surface);
|
|
43304
43533
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
43305
43534
|
const plan = buildPluginCachePlan(
|
|
43306
|
-
(0,
|
|
43535
|
+
(0, import_node_os22.homedir)(),
|
|
43307
43536
|
running,
|
|
43308
43537
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
43309
43538
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -43331,14 +43560,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
43331
43560
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
43332
43561
|
const installed = installedActivePluginVersion(surface);
|
|
43333
43562
|
const plan = buildPluginCachePlan(
|
|
43334
|
-
(0,
|
|
43563
|
+
(0, import_node_os22.homedir)(),
|
|
43335
43564
|
running,
|
|
43336
43565
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
43337
43566
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
43338
43567
|
);
|
|
43339
43568
|
const result = applyPluginCachePlan(
|
|
43340
43569
|
plan,
|
|
43341
|
-
(p) => (0,
|
|
43570
|
+
(p) => (0, import_node_fs50.rmSync)(p, { recursive: true }),
|
|
43342
43571
|
stagingApplyFsGuard(configRoot)
|
|
43343
43572
|
);
|
|
43344
43573
|
return {
|
|
@@ -43373,7 +43602,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
43373
43602
|
// has no generated routing index would
|
|
43374
43603
|
// get a permanent — demanding an artifact it never asked for.
|
|
43375
43604
|
docsIndexState: (root) => {
|
|
43376
|
-
if (!(0,
|
|
43605
|
+
if (!(0, import_node_fs50.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
43377
43606
|
const real = createDocsIndexDeps(root);
|
|
43378
43607
|
let docs;
|
|
43379
43608
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -43382,7 +43611,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
43382
43611
|
},
|
|
43383
43612
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
43384
43613
|
healDocsIndex: (root) => {
|
|
43385
|
-
if (!(0,
|
|
43614
|
+
if (!(0, import_node_fs50.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
43386
43615
|
const real = createDocsIndexDeps(root);
|
|
43387
43616
|
let docs;
|
|
43388
43617
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -43941,7 +44170,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
43941
44170
|
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`);
|
|
43942
44171
|
if (o.secretsFile) {
|
|
43943
44172
|
try {
|
|
43944
|
-
vars.push(`secrets=${(0,
|
|
44173
|
+
vars.push(`secrets=${(0, import_node_fs50.readFileSync)(o.secretsFile, "utf8")}`);
|
|
43945
44174
|
} catch (e) {
|
|
43946
44175
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
43947
44176
|
}
|
|
@@ -44254,16 +44483,16 @@ function ciAuditDeps2() {
|
|
|
44254
44483
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
44255
44484
|
readSeedFile: (path2) => {
|
|
44256
44485
|
if (!root) return null;
|
|
44257
|
-
const fullPath = (0,
|
|
44258
|
-
return (0,
|
|
44486
|
+
const fullPath = (0, import_node_path47.join)(root, path2);
|
|
44487
|
+
return (0, import_node_fs50.existsSync)(fullPath) ? (0, import_node_fs50.readFileSync)(fullPath, "utf8") : null;
|
|
44259
44488
|
}
|
|
44260
44489
|
};
|
|
44261
44490
|
}
|
|
44262
44491
|
function hubRoot2() {
|
|
44263
|
-
const fromPkg = (0,
|
|
44492
|
+
const fromPkg = (0, import_node_path47.join)(__dirname, "..", "..");
|
|
44264
44493
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
44265
|
-
if ((0,
|
|
44266
|
-
if ((0,
|
|
44494
|
+
if ((0, import_node_fs50.existsSync)((0, import_node_path47.join)(fromPkg, marker))) return fromPkg;
|
|
44495
|
+
if ((0, import_node_fs50.existsSync)((0, import_node_path47.join)(process.cwd(), marker))) return process.cwd();
|
|
44267
44496
|
return null;
|
|
44268
44497
|
}
|
|
44269
44498
|
registerQueryCommands(program2);
|
|
@@ -44363,10 +44592,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
44363
44592
|
targets = resolution.targets;
|
|
44364
44593
|
}
|
|
44365
44594
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
44366
|
-
const fileMatrix = (0,
|
|
44595
|
+
const fileMatrix = (0, import_node_fs50.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs50.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
44367
44596
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
44368
44597
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
44369
|
-
const sanctioned = (0,
|
|
44598
|
+
const sanctioned = (0, import_node_fs50.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs50.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
44370
44599
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
44371
44600
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
44372
44601
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -44397,16 +44626,16 @@ function directoryBytes(path2) {
|
|
|
44397
44626
|
let total = 0;
|
|
44398
44627
|
let entries;
|
|
44399
44628
|
try {
|
|
44400
|
-
entries = (0,
|
|
44629
|
+
entries = (0, import_node_fs50.readdirSync)(path2, { withFileTypes: true });
|
|
44401
44630
|
} catch {
|
|
44402
44631
|
return 0;
|
|
44403
44632
|
}
|
|
44404
44633
|
for (const entry of entries) {
|
|
44405
|
-
const child2 = (0,
|
|
44634
|
+
const child2 = (0, import_node_path47.join)(path2, entry.name);
|
|
44406
44635
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
44407
44636
|
else {
|
|
44408
44637
|
try {
|
|
44409
|
-
total += (0,
|
|
44638
|
+
total += (0, import_node_fs50.statSync)(child2).size;
|
|
44410
44639
|
} catch {
|
|
44411
44640
|
}
|
|
44412
44641
|
}
|
|
@@ -44414,25 +44643,25 @@ function directoryBytes(path2) {
|
|
|
44414
44643
|
return total;
|
|
44415
44644
|
}
|
|
44416
44645
|
function listDirEntries(dir) {
|
|
44417
|
-
return (0,
|
|
44646
|
+
return (0, import_node_fs50.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
44418
44647
|
}
|
|
44419
44648
|
function readInstalledPluginRefs(configRoot) {
|
|
44420
44649
|
const p = installedPluginsPathForConfig(configRoot);
|
|
44421
|
-
if (!(0,
|
|
44650
|
+
if (!(0, import_node_fs50.existsSync)(p)) return [];
|
|
44422
44651
|
try {
|
|
44423
|
-
return installedPluginPaths((0,
|
|
44652
|
+
return installedPluginPaths((0, import_node_fs50.readFileSync)(p, "utf8"));
|
|
44424
44653
|
} catch {
|
|
44425
44654
|
return null;
|
|
44426
44655
|
}
|
|
44427
44656
|
}
|
|
44428
44657
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
44429
44658
|
return {
|
|
44430
|
-
exists: (p) => (0,
|
|
44431
|
-
listVersionDirs: (root) => (0,
|
|
44659
|
+
exists: (p) => (0, import_node_fs50.existsSync)(p),
|
|
44660
|
+
listVersionDirs: (root) => (0, import_node_fs50.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
44432
44661
|
dirBytes,
|
|
44433
|
-
listStagingDirs: (root) => (0,
|
|
44662
|
+
listStagingDirs: (root) => (0, import_node_fs50.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
44434
44663
|
try {
|
|
44435
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
44664
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path47.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs50.statSync)(p).mtimeMs) };
|
|
44436
44665
|
} catch {
|
|
44437
44666
|
return { name: d.name, mtimeMs: Date.now() };
|
|
44438
44667
|
}
|
|
@@ -44446,10 +44675,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
44446
44675
|
return {
|
|
44447
44676
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
44448
44677
|
mtimeMs: (name) => {
|
|
44449
|
-
const p = (0,
|
|
44450
|
-
if (!(0,
|
|
44678
|
+
const p = (0, import_node_path47.join)(stagingRoot, name);
|
|
44679
|
+
if (!(0, import_node_fs50.existsSync)(p)) return null;
|
|
44451
44680
|
try {
|
|
44452
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
44681
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs50.statSync)(q).mtimeMs);
|
|
44453
44682
|
} catch {
|
|
44454
44683
|
return null;
|
|
44455
44684
|
}
|
|
@@ -44469,13 +44698,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
44469
44698
|
return;
|
|
44470
44699
|
}
|
|
44471
44700
|
const plan = buildPluginCachePlan(
|
|
44472
|
-
(0,
|
|
44701
|
+
(0, import_node_os22.homedir)(),
|
|
44473
44702
|
running,
|
|
44474
44703
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
44475
44704
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
44476
44705
|
);
|
|
44477
44706
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
44478
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
44707
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs50.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
44479
44708
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
44480
44709
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
44481
44710
|
else console.log(renderPluginCachePlan(plan, result));
|