@mutmutco/cli 4.3.43 → 4.3.45
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 +3 -4
- package/dist/main.cjs +448 -551
- 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_fs48 = require("node:fs");
|
|
3420
3420
|
|
|
3421
3421
|
// src/clean-exit.ts
|
|
3422
3422
|
var UNDICI_GLOBAL_DISPATCHER_SYMBOL = Object.getOwnPropertySymbols(globalThis).find(
|
|
@@ -5252,16 +5252,14 @@ var GUIDES = /* @__PURE__ */ new Map([
|
|
|
5252
5252
|
did_you_mean: "oracle issue edit"
|
|
5253
5253
|
}
|
|
5254
5254
|
],
|
|
5255
|
-
//
|
|
5256
|
-
// envelope built `mmi-cli explain jervcode worktree-create --json` — an explain hop that itself
|
|
5257
|
-
// dead-ends on ERR_NOT_FOUND — so the guide carries the corrected command directly.
|
|
5255
|
+
// Worktree creation belongs to Git or the selected host, not an MMI command.
|
|
5258
5256
|
[
|
|
5259
5257
|
"worktree:create",
|
|
5260
5258
|
{
|
|
5261
|
-
hint: "no `worktree create` in mmi-cli \u2014
|
|
5262
|
-
expected: ["
|
|
5263
|
-
did_you_mean: "
|
|
5264
|
-
corrected_command: "
|
|
5259
|
+
hint: "no `worktree create` in mmi-cli \u2014 after claiming the issue, use Git or your host to create an isolated worktree",
|
|
5260
|
+
expected: ["git worktree add"],
|
|
5261
|
+
did_you_mean: "git worktree add",
|
|
5262
|
+
corrected_command: "git worktree -h"
|
|
5265
5263
|
}
|
|
5266
5264
|
]
|
|
5267
5265
|
]);
|
|
@@ -5304,8 +5302,8 @@ function resolveVerbAliasShim(argv) {
|
|
|
5304
5302
|
}
|
|
5305
5303
|
|
|
5306
5304
|
// src/command-composition.ts
|
|
5307
|
-
var
|
|
5308
|
-
var
|
|
5305
|
+
var import_node_os21 = require("node:os");
|
|
5306
|
+
var import_node_path46 = require("node:path");
|
|
5309
5307
|
|
|
5310
5308
|
// src/board-read.ts
|
|
5311
5309
|
var import_node_fs13 = require("node:fs");
|
|
@@ -7348,7 +7346,7 @@ var OWNER = "mutmutco";
|
|
|
7348
7346
|
var SSM_ROOT = "/mmi-future";
|
|
7349
7347
|
var PROJECT_TIER_SEGMENT = "dev";
|
|
7350
7348
|
var ORG_INFRA_SLUG = "_org";
|
|
7351
|
-
var
|
|
7349
|
+
var MMI_VAULT_GUIDANCE = 'Check MMI access with `mmi-cli oracle org access capabilities`; a master can locate credentials with `mmi-cli vault secrets find "<what it is for>"`.';
|
|
7352
7350
|
var KEY_MAX_PATH_SEGMENTS = 4;
|
|
7353
7351
|
var KEY_RE = new RegExp(`^(?:[a-z0-9][a-z0-9-]*/){0,${KEY_MAX_PATH_SEGMENTS}}[A-Za-z][A-Za-z0-9_]*$`);
|
|
7354
7352
|
function isValidSecretKey(key) {
|
|
@@ -7365,7 +7363,7 @@ function secretParamName(slug, key) {
|
|
|
7365
7363
|
return `${SSM_ROOT}/${slug}/${key}`;
|
|
7366
7364
|
}
|
|
7367
7365
|
function formatSecretList(items) {
|
|
7368
|
-
if (!items.length) return `no secrets \u2014 ${
|
|
7366
|
+
if (!items.length) return `no secrets \u2014 ${MMI_VAULT_GUIDANCE}`;
|
|
7369
7367
|
const width = Math.max(...items.map((i) => i.key.length));
|
|
7370
7368
|
return items.map((i) => {
|
|
7371
7369
|
const head = `${i.canManage ? "*" : " "} ${i.key.padEnd(width)} ${i.tier}`;
|
|
@@ -7558,7 +7556,7 @@ function formatCapabilities(r) {
|
|
|
7558
7556
|
const items = [...r.capabilities ?? []].sort((a, b) => a.scope.localeCompare(b.scope));
|
|
7559
7557
|
if (!items.length) return `${head}
|
|
7560
7558
|
|
|
7561
|
-
no vault credentials visible \u2014 ${
|
|
7559
|
+
no vault credentials visible \u2014 ${MMI_VAULT_GUIDANCE}`;
|
|
7562
7560
|
const width = Math.max(...items.map((i) => i.scope.length));
|
|
7563
7561
|
const lines2 = items.map(
|
|
7564
7562
|
(i) => `${i.accessible ? "+" : " "} ${i.scope.padEnd(width)} ${i.tier.padEnd(7)} ${i.grantScope ? `${i.reason} (${i.grantScope})` : i.reason}`
|
|
@@ -7682,7 +7680,7 @@ function resolveNotFoundGuidance(input) {
|
|
|
7682
7680
|
`Secret ${key} was not found at ${tried}, and no key named ${leaf} is visible to you.`,
|
|
7683
7681
|
`Locate it by intent first: \`mmi-cli vault secrets find "<what it is for>"\` \u2014 it returns the exact keyless-use command.`,
|
|
7684
7682
|
mnemonic,
|
|
7685
|
-
|
|
7683
|
+
MMI_VAULT_GUIDANCE
|
|
7686
7684
|
];
|
|
7687
7685
|
if (isMaster) {
|
|
7688
7686
|
lines2.push("You are master: if it truly does not exist yet, create it with `mmi-cli vault secrets set` rather than requesting a grant from yourself.");
|
|
@@ -8792,7 +8790,7 @@ async function secretsVerify(deps, key, opts) {
|
|
|
8792
8790
|
if (!value) {
|
|
8793
8791
|
deps.err(`${key}: ABSENT or unreachable at this coordinate \u2014 no value could be read`);
|
|
8794
8792
|
deps.err(`Locate a key across every vault you can reach: mmi-cli oracle org access capabilities${opts.repo ? ` --repo ${opts.repo}` : ""}`);
|
|
8795
|
-
deps.err(
|
|
8793
|
+
deps.err(MMI_VAULT_GUIDANCE);
|
|
8796
8794
|
return false;
|
|
8797
8795
|
}
|
|
8798
8796
|
deps.log(`${key}: PRESENT (${value.length} chars) \u2014 reachability only; no provider verifier is configured for ${secretLeafName(key)}, so the credential itself was not validated`);
|
|
@@ -9632,7 +9630,7 @@ async function loadConfigForBoardSelector(selector, repoOption) {
|
|
|
9632
9630
|
var SKILL_LESSON_LABEL = "skill-lesson";
|
|
9633
9631
|
var SKILL_LESSON_FILE_LABELS = [SKILL_LESSON_LABEL, LEARNING_LABEL2];
|
|
9634
9632
|
var SKILL_LESSON_LOOP_KIND = "lesson";
|
|
9635
|
-
var SKILL_NAMES = ["bootstrap", "epic", "hotfix", "rcand", "release", "secrets", "stage"];
|
|
9633
|
+
var SKILL_NAMES = ["board", "bootstrap", "epic", "hotfix", "rcand", "release", "secrets", "stage"];
|
|
9636
9634
|
function assertSkillName(name) {
|
|
9637
9635
|
const match = SKILL_NAMES.find((skill) => skill === name);
|
|
9638
9636
|
if (!match) throw new Error(`unknown skill "${name}" \u2014 expected one of: ${SKILL_NAMES.join(", ")}`);
|
|
@@ -9661,11 +9659,6 @@ ${buildSkillLessonBody(body, sourceRepo, pluginSha)}`;
|
|
|
9661
9659
|
function findDuplicateLesson(source, openLessons) {
|
|
9662
9660
|
return findDuplicateReport(source, openLessons);
|
|
9663
9661
|
}
|
|
9664
|
-
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.";
|
|
9665
|
-
function warnSkillLessonDeprecated(write = (line) => void process.stderr.write(line)) {
|
|
9666
|
-
write(`${SKILL_LESSON_DEPRECATION_NOTICE}
|
|
9667
|
-
`);
|
|
9668
|
-
}
|
|
9669
9662
|
|
|
9670
9663
|
// src/session-identity.ts
|
|
9671
9664
|
var import_node_crypto4 = require("node:crypto");
|
|
@@ -15710,10 +15703,10 @@ var rollout_plan_default = {
|
|
|
15710
15703
|
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)."
|
|
15711
15704
|
},
|
|
15712
15705
|
baseline: {
|
|
15713
|
-
version: "4.3.
|
|
15714
|
-
tag: "v4.3.
|
|
15715
|
-
commit: "
|
|
15716
|
-
npm: "@mutmutco/cli@4.3.
|
|
15706
|
+
version: "4.3.45",
|
|
15707
|
+
tag: "v4.3.45",
|
|
15708
|
+
commit: "519cee3af091",
|
|
15709
|
+
npm: "@mutmutco/cli@4.3.45"
|
|
15717
15710
|
},
|
|
15718
15711
|
exitCriterion: "fleet-n-of-n",
|
|
15719
15712
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15730,14 +15723,14 @@ var rollout_plan_default = {
|
|
|
15730
15723
|
repo: "mutmutco/mmi-hub",
|
|
15731
15724
|
role: "canary",
|
|
15732
15725
|
schedule: "train",
|
|
15733
|
-
v3Target: "v4.3.
|
|
15726
|
+
v3Target: "v4.3.45"
|
|
15734
15727
|
}
|
|
15735
15728
|
],
|
|
15736
15729
|
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.",
|
|
15737
15730
|
rollback: {
|
|
15738
15731
|
independent: true,
|
|
15739
|
-
mechanism: "npm dist-tag latest -> 4.3.
|
|
15740
|
-
v3Target: "v4.3.
|
|
15732
|
+
mechanism: "npm dist-tag latest -> 4.3.45 and redeploy the Hub Lambda from tag v4.3.45 (519cee3af091); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15733
|
+
v3Target: "v4.3.45 (@mutmutco/cli@4.3.45, tag commit 519cee3af091 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15741
15734
|
}
|
|
15742
15735
|
},
|
|
15743
15736
|
{
|
|
@@ -19126,7 +19119,7 @@ function renderTenantControl(r) {
|
|
|
19126
19119
|
}
|
|
19127
19120
|
|
|
19128
19121
|
// src/train-apply-phases.ts
|
|
19129
|
-
var
|
|
19122
|
+
var import_node_fs23 = require("node:fs");
|
|
19130
19123
|
var import_promises3 = require("node:fs/promises");
|
|
19131
19124
|
var import_node_path23 = require("node:path");
|
|
19132
19125
|
|
|
@@ -19381,7 +19374,7 @@ Resume it: ${resumeCommand}
|
|
|
19381
19374
|
|
|
19382
19375
|
// src/train-doctor.ts
|
|
19383
19376
|
var import_node_child_process9 = require("node:child_process");
|
|
19384
|
-
var
|
|
19377
|
+
var import_node_fs22 = require("node:fs");
|
|
19385
19378
|
var import_node_os13 = require("node:os");
|
|
19386
19379
|
var import_node_path22 = require("node:path");
|
|
19387
19380
|
|
|
@@ -19592,16 +19585,13 @@ function compatHolds(range, version) {
|
|
|
19592
19585
|
}
|
|
19593
19586
|
|
|
19594
19587
|
// src/claude-binary-doctor.ts
|
|
19595
|
-
var
|
|
19588
|
+
var import_node_fs20 = require("node:fs");
|
|
19596
19589
|
var import_node_os12 = require("node:os");
|
|
19597
19590
|
var import_node_path19 = require("node:path");
|
|
19598
19591
|
|
|
19599
19592
|
// src/jerv-cli-spawn.ts
|
|
19600
|
-
var import_node_fs20 = require("node:fs");
|
|
19601
19593
|
var import_node_os11 = require("node:os");
|
|
19602
19594
|
var import_node_path18 = require("node:path");
|
|
19603
|
-
var WIN_NAMES = ["jerv-cli.cmd", "jerv-cli.exe", "jerv-cli"];
|
|
19604
|
-
var POSIX_NAMES = ["jerv-cli"];
|
|
19605
19595
|
var JERV_CLI_ENTRY = (0, import_node_path18.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
19606
19596
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
19607
19597
|
if (platform2 !== "win32") {
|
|
@@ -19645,67 +19635,6 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os11.hom
|
|
|
19645
19635
|
}
|
|
19646
19636
|
return out;
|
|
19647
19637
|
}
|
|
19648
|
-
function jervCliCandidatePaths(env = process.env, home = (0, import_node_os11.homedir)(), platform2 = process.platform) {
|
|
19649
|
-
const names = platform2 === "win32" ? WIN_NAMES : POSIX_NAMES;
|
|
19650
|
-
const out = [];
|
|
19651
|
-
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
19652
|
-
for (const name of names) out.push((0, import_node_path18.join)(dir, name));
|
|
19653
|
-
}
|
|
19654
|
-
return out;
|
|
19655
|
-
}
|
|
19656
|
-
function resolveJervCliPath(env = process.env, home = (0, import_node_os11.homedir)(), platform2 = process.platform, exists = import_node_fs20.existsSync) {
|
|
19657
|
-
for (const candidate of jervCliCandidatePaths(env, home, platform2)) {
|
|
19658
|
-
if (exists(candidate)) return candidate;
|
|
19659
|
-
}
|
|
19660
|
-
return void 0;
|
|
19661
|
-
}
|
|
19662
|
-
function resolveJervCliNodeEntry(shimPath, exists = import_node_fs20.existsSync) {
|
|
19663
|
-
const entry = (0, import_node_path18.join)((0, import_node_path18.dirname)(shimPath), JERV_CLI_ENTRY);
|
|
19664
|
-
return exists(entry) ? entry : void 0;
|
|
19665
|
-
}
|
|
19666
|
-
function jervCliExecFileArgs(args, opts = {}) {
|
|
19667
|
-
const platform2 = opts.platform ?? process.platform;
|
|
19668
|
-
const exists = opts.exists ?? import_node_fs20.existsSync;
|
|
19669
|
-
const resolved = resolveJervCliPath(opts.env ?? process.env, opts.home ?? (0, import_node_os11.homedir)(), platform2, exists);
|
|
19670
|
-
if (resolved) {
|
|
19671
|
-
const entry = resolveJervCliNodeEntry(resolved, exists);
|
|
19672
|
-
if (entry) {
|
|
19673
|
-
return { file: opts.execPath ?? process.execPath, args: [entry, ...args], via: "node-entry" };
|
|
19674
|
-
}
|
|
19675
|
-
}
|
|
19676
|
-
const bin = resolved ?? "jerv-cli";
|
|
19677
|
-
if (platform2 === "win32") {
|
|
19678
|
-
return { file: "cmd.exe", args: ["/c", bin, ...args], via: resolved ? "cmd-shim" : "bare" };
|
|
19679
|
-
}
|
|
19680
|
-
return { file: bin, args: [...args], via: resolved ? "posix" : "bare" };
|
|
19681
|
-
}
|
|
19682
|
-
function formatJervCliSpawnFailure(err, plan, candidates) {
|
|
19683
|
-
const base = (err.stderr?.trim() || err.message.trim()).split("\n")[0] || "spawn failed";
|
|
19684
|
-
const code = err.code;
|
|
19685
|
-
if (code !== "ENOENT" && !/\bENOENT\b/i.test(base)) return base;
|
|
19686
|
-
const tried = candidates.length > 0 ? candidates.join(" | ") : "(no candidates)";
|
|
19687
|
-
return `${base} [via=${plan.via} file=${plan.file}; tried: ${tried}]`;
|
|
19688
|
-
}
|
|
19689
|
-
function isEnoent(err) {
|
|
19690
|
-
const code = err.code;
|
|
19691
|
-
if (code === "ENOENT") return true;
|
|
19692
|
-
return /\bENOENT\b/i.test(err.message ?? "");
|
|
19693
|
-
}
|
|
19694
|
-
function execJervCli(args, options = {}) {
|
|
19695
|
-
const env = options.env ?? process.env;
|
|
19696
|
-
const candidates = jervCliCandidatePaths(env);
|
|
19697
|
-
const plan = jervCliExecFileArgs(args, { env });
|
|
19698
|
-
return execFileP(plan.file, plan.args, options).catch((err) => {
|
|
19699
|
-
if (!isEnoent(err)) throw err;
|
|
19700
|
-
const wrapped = new Error(formatJervCliSpawnFailure(err, plan, candidates));
|
|
19701
|
-
Object.assign(wrapped, {
|
|
19702
|
-
code: "ENOENT",
|
|
19703
|
-
stderr: err.stderr,
|
|
19704
|
-
cause: err
|
|
19705
|
-
});
|
|
19706
|
-
throw wrapped;
|
|
19707
|
-
});
|
|
19708
|
-
}
|
|
19709
19638
|
|
|
19710
19639
|
// src/claude-binary-doctor.ts
|
|
19711
19640
|
var PACKAGE = "@anthropic-ai/claude-code";
|
|
@@ -19765,16 +19694,16 @@ function globalNodeModulesRoots(host) {
|
|
|
19765
19694
|
function readHead(path2) {
|
|
19766
19695
|
let fd;
|
|
19767
19696
|
try {
|
|
19768
|
-
fd = (0,
|
|
19697
|
+
fd = (0, import_node_fs20.openSync)(path2, "r");
|
|
19769
19698
|
const buffer = new Uint8Array(MAGIC_HEAD_BYTES);
|
|
19770
|
-
const read = (0,
|
|
19699
|
+
const read = (0, import_node_fs20.readSync)(fd, buffer, 0, MAGIC_HEAD_BYTES, 0);
|
|
19771
19700
|
return buffer.subarray(0, read);
|
|
19772
19701
|
} catch {
|
|
19773
19702
|
return void 0;
|
|
19774
19703
|
} finally {
|
|
19775
19704
|
if (fd !== void 0) {
|
|
19776
19705
|
try {
|
|
19777
|
-
(0,
|
|
19706
|
+
(0, import_node_fs20.closeSync)(fd);
|
|
19778
19707
|
} catch {
|
|
19779
19708
|
}
|
|
19780
19709
|
}
|
|
@@ -19782,7 +19711,7 @@ function readHead(path2) {
|
|
|
19782
19711
|
}
|
|
19783
19712
|
function fileBytes(path2) {
|
|
19784
19713
|
try {
|
|
19785
|
-
return (0,
|
|
19714
|
+
return (0, import_node_fs20.statSync)(path2).size;
|
|
19786
19715
|
} catch {
|
|
19787
19716
|
return void 0;
|
|
19788
19717
|
}
|
|
@@ -19796,13 +19725,13 @@ function readClaudeBinaryState(host = {}) {
|
|
|
19796
19725
|
const arch = host.arch ?? process.arch;
|
|
19797
19726
|
const magic = EXECUTABLE_MAGIC[platform2];
|
|
19798
19727
|
if (!magic) return void 0;
|
|
19799
|
-
const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path19.join)(root, ...PACKAGE.split("/"))).find((dir) => (0,
|
|
19728
|
+
const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path19.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs20.existsSync)((0, import_node_path19.join)(dir, "package.json")));
|
|
19800
19729
|
if (!packageRoot) return void 0;
|
|
19801
19730
|
const keys = platformPackageKeys(platform2, arch);
|
|
19802
19731
|
const fallbackPackage = `${PACKAGE}-${keys[0]}`;
|
|
19803
19732
|
let manifest;
|
|
19804
19733
|
try {
|
|
19805
|
-
manifest = JSON.parse((0,
|
|
19734
|
+
manifest = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path19.join)(packageRoot, "package.json"), "utf8"));
|
|
19806
19735
|
} catch (e) {
|
|
19807
19736
|
return {
|
|
19808
19737
|
state: "unreadable",
|
|
@@ -19833,7 +19762,7 @@ function readClaudeBinaryState(host = {}) {
|
|
|
19833
19762
|
(0, import_node_path19.join)(packageRoot, "node_modules", ...name.split("/"), binName),
|
|
19834
19763
|
(0, import_node_path19.join)((0, import_node_path19.dirname)((0, import_node_path19.dirname)(packageRoot)), ...name.split("/"), binName)
|
|
19835
19764
|
];
|
|
19836
|
-
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0,
|
|
19765
|
+
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs20.existsSync)(file)) })).find((c) => c.path);
|
|
19837
19766
|
const platformPackage = found?.name ?? published[0];
|
|
19838
19767
|
let source;
|
|
19839
19768
|
let sourceProblem;
|
|
@@ -19844,7 +19773,7 @@ function readClaudeBinaryState(host = {}) {
|
|
|
19844
19773
|
} else {
|
|
19845
19774
|
source = { path: found.path, bytes: fileBytes(found.path) ?? 0 };
|
|
19846
19775
|
}
|
|
19847
|
-
if (!(0,
|
|
19776
|
+
if (!(0, import_node_fs20.existsSync)(binPath)) {
|
|
19848
19777
|
return { state: "missing", binPath, expectedMagic: magic.name, platformPackage, ...source ? { source } : {}, ...sourceProblem ? { sourceProblem } : {} };
|
|
19849
19778
|
}
|
|
19850
19779
|
const head = readHead(binPath);
|
|
@@ -19887,9 +19816,9 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
19887
19816
|
const platform2 = host.platform ?? process.platform;
|
|
19888
19817
|
const aside = `${probe.binPath}.stub-${Date.now()}`;
|
|
19889
19818
|
let renamed = false;
|
|
19890
|
-
if ((0,
|
|
19819
|
+
if ((0, import_node_fs20.existsSync)(probe.binPath)) {
|
|
19891
19820
|
try {
|
|
19892
|
-
(0,
|
|
19821
|
+
(0, import_node_fs20.renameSync)(probe.binPath, aside);
|
|
19893
19822
|
renamed = true;
|
|
19894
19823
|
onStep?.(`renamed the stub aside: ${aside}`);
|
|
19895
19824
|
} catch (e) {
|
|
@@ -19898,12 +19827,12 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
19898
19827
|
}
|
|
19899
19828
|
try {
|
|
19900
19829
|
onStep?.(`copying ${probe.source.path} \u2192 ${probe.binPath} (${(probe.source.bytes / 1e6).toFixed(0)} MB)`);
|
|
19901
|
-
(0,
|
|
19902
|
-
if (platform2 !== "win32") (0,
|
|
19830
|
+
(0, import_node_fs20.copyFileSync)(probe.source.path, probe.binPath);
|
|
19831
|
+
if (platform2 !== "win32") (0, import_node_fs20.chmodSync)(probe.binPath, 493);
|
|
19903
19832
|
} catch (e) {
|
|
19904
19833
|
if (renamed) {
|
|
19905
19834
|
try {
|
|
19906
|
-
(0,
|
|
19835
|
+
(0, import_node_fs20.renameSync)(aside, probe.binPath);
|
|
19907
19836
|
} catch {
|
|
19908
19837
|
return { ok: false, detail: `copy failed (${e.message}) and the stub could not be restored \u2014 the original is at ${aside}` };
|
|
19909
19838
|
}
|
|
@@ -19917,7 +19846,7 @@ function healClaudeBinary(host = {}, onStep) {
|
|
|
19917
19846
|
let kept = false;
|
|
19918
19847
|
if (renamed) {
|
|
19919
19848
|
try {
|
|
19920
|
-
(0,
|
|
19849
|
+
(0, import_node_fs20.rmSync)(aside);
|
|
19921
19850
|
} catch {
|
|
19922
19851
|
kept = true;
|
|
19923
19852
|
}
|
|
@@ -20276,7 +20205,7 @@ function checkLineEndings(probe) {
|
|
|
20276
20205
|
}
|
|
20277
20206
|
|
|
20278
20207
|
// src/release-ledger.ts
|
|
20279
|
-
var
|
|
20208
|
+
var import_node_fs21 = require("node:fs");
|
|
20280
20209
|
var import_node_path21 = require("node:path");
|
|
20281
20210
|
var import_node_crypto5 = require("node:crypto");
|
|
20282
20211
|
|
|
@@ -20533,7 +20462,7 @@ function resolvePrimaryGitDir(cwd) {
|
|
|
20533
20462
|
}
|
|
20534
20463
|
let common;
|
|
20535
20464
|
try {
|
|
20536
|
-
common = (0,
|
|
20465
|
+
common = (0, import_node_fs21.readFileSync)((0, import_node_path21.join)(gitDir, "commondir"), "utf8").trim();
|
|
20537
20466
|
} catch (e) {
|
|
20538
20467
|
if (e.code !== "ENOENT") {
|
|
20539
20468
|
throw new ReleaseLedgerError("unreadable", `release ledger: the worktree gitdir's commondir pointer could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not primary (#5987/#4713)`);
|
|
@@ -20542,7 +20471,7 @@ function resolvePrimaryGitDir(cwd) {
|
|
|
20542
20471
|
if (!common) return gitDir;
|
|
20543
20472
|
const commonDir = (0, import_node_path21.isAbsolute)(common) ? common : (0, import_node_path21.resolve)(gitDir, common);
|
|
20544
20473
|
try {
|
|
20545
|
-
(0,
|
|
20474
|
+
(0, import_node_fs21.readFileSync)((0, import_node_path21.join)(commonDir, "HEAD"), "utf8");
|
|
20546
20475
|
} catch (e) {
|
|
20547
20476
|
throw new ReleaseLedgerError("no-checkout", `release ledger: the worktree's commondir pointer names ${commonDir}, which has no readable HEAD (${e instanceof Error ? e.message : String(e)}) \u2014 the primary checkout may have been removed; refusing to write the ledger into a doomed location`);
|
|
20548
20477
|
}
|
|
@@ -20552,18 +20481,18 @@ function releaseLedgerPath(cwd) {
|
|
|
20552
20481
|
return (0, import_node_path21.join)(resolvePrimaryGitDir(cwd), "mmi-runtime", "release", "phases.json");
|
|
20553
20482
|
}
|
|
20554
20483
|
function atomicWriteJson(path2, ledger) {
|
|
20555
|
-
(0,
|
|
20484
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path21.dirname)(path2), { recursive: true });
|
|
20556
20485
|
const tmp = `${path2}.tmp-${process.pid}-${(0, import_node_crypto5.randomBytes)(4).toString("hex")}`;
|
|
20557
|
-
(0,
|
|
20486
|
+
(0, import_node_fs21.writeFileSync)(tmp, `${JSON.stringify(ledger, null, 2)}
|
|
20558
20487
|
`, "utf8");
|
|
20559
|
-
(0,
|
|
20488
|
+
(0, import_node_fs21.renameSync)(tmp, path2);
|
|
20560
20489
|
}
|
|
20561
20490
|
async function withReleaseLedger(path2, expected, fn, opts = {}) {
|
|
20562
20491
|
return withFileLock(`${path2}.lock`, { label: "release ledger", maxWaitMs: opts.maxWaitMs ?? 5e3 }, async () => {
|
|
20563
20492
|
let current;
|
|
20564
20493
|
let raw;
|
|
20565
20494
|
try {
|
|
20566
|
-
raw = (0,
|
|
20495
|
+
raw = (0, import_node_fs21.readFileSync)(path2, "utf8");
|
|
20567
20496
|
} catch (e) {
|
|
20568
20497
|
if (e.code !== "ENOENT") {
|
|
20569
20498
|
throw new ReleaseLedgerError("unreadable", `release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987)`);
|
|
@@ -21197,7 +21126,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
|
|
|
21197
21126
|
return withFileLock(`${path2}.lock`, { label: "release ledger", maxWaitMs }, async () => {
|
|
21198
21127
|
let raw;
|
|
21199
21128
|
try {
|
|
21200
|
-
raw = (0,
|
|
21129
|
+
raw = (0, import_node_fs21.readFileSync)(path2, "utf8");
|
|
21201
21130
|
} catch (e) {
|
|
21202
21131
|
if (e.code === "ENOENT") return { cleared: false };
|
|
21203
21132
|
return { cleared: false, note: `release ledger at ${path2} could not be read for clearing (${e instanceof Error ? e.message : String(e)}) \u2014 left in place` };
|
|
@@ -21213,7 +21142,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
|
|
|
21213
21142
|
} catch (e) {
|
|
21214
21143
|
return { cleared: false, note: `release ledger at ${path2} belongs to ${ledger.tag} @ ${ledger.tagSha.slice(0, 12)} \u2014 not this release; left in place (${e instanceof Error ? e.message : String(e)})` };
|
|
21215
21144
|
}
|
|
21216
|
-
(0,
|
|
21145
|
+
(0, import_node_fs21.unlinkSync)(path2);
|
|
21217
21146
|
return { cleared: true, note: `cleared the release ledger at ${path2} for the live-proven aborted ${anchors.tag}` };
|
|
21218
21147
|
});
|
|
21219
21148
|
}
|
|
@@ -21316,7 +21245,7 @@ async function healStaleAlignmentLeg(deps, cwd, expected) {
|
|
|
21316
21245
|
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
21317
21246
|
let prior;
|
|
21318
21247
|
try {
|
|
21319
|
-
prior = parseReleaseLedger((0,
|
|
21248
|
+
prior = parseReleaseLedger((0, import_node_fs21.readFileSync)(path2, "utf8"));
|
|
21320
21249
|
} catch {
|
|
21321
21250
|
return void 0;
|
|
21322
21251
|
}
|
|
@@ -21336,7 +21265,7 @@ async function healStaleAlignmentLeg(deps, cwd, expected) {
|
|
|
21336
21265
|
async function reverifyPriorRunLegs(deps, path2, targetTag) {
|
|
21337
21266
|
let prior;
|
|
21338
21267
|
try {
|
|
21339
|
-
prior = parseReleaseLedger((0,
|
|
21268
|
+
prior = parseReleaseLedger((0, import_node_fs21.readFileSync)(path2, "utf8"));
|
|
21340
21269
|
} catch {
|
|
21341
21270
|
return;
|
|
21342
21271
|
}
|
|
@@ -21356,7 +21285,7 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
21356
21285
|
return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
|
|
21357
21286
|
let raw;
|
|
21358
21287
|
try {
|
|
21359
|
-
raw = (0,
|
|
21288
|
+
raw = (0, import_node_fs21.readFileSync)(path2, "utf8");
|
|
21360
21289
|
} catch (e) {
|
|
21361
21290
|
if (e.code === "ENOENT") return { ok: true };
|
|
21362
21291
|
return { ok: false, error: `the release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987/#4713)` };
|
|
@@ -21395,13 +21324,13 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
|
|
|
21395
21324
|
}
|
|
21396
21325
|
const archivePath = (0, import_node_path21.join)((0, import_node_path21.dirname)(path2), `phases.archive-${prior.tag}.json`);
|
|
21397
21326
|
try {
|
|
21398
|
-
(0,
|
|
21327
|
+
(0, import_node_fs21.unlinkSync)(archivePath);
|
|
21399
21328
|
} catch (e) {
|
|
21400
21329
|
if (e.code !== "ENOENT") {
|
|
21401
21330
|
return { ok: false, error: `could not clear the stale archive at ${archivePath} (${e instanceof Error ? e.message : String(e)}) \u2014 the ledger for ${prior.tag} stays active (#5987)` };
|
|
21402
21331
|
}
|
|
21403
21332
|
}
|
|
21404
|
-
(0,
|
|
21333
|
+
(0, import_node_fs21.renameSync)(path2, archivePath);
|
|
21405
21334
|
return { ok: true, note: `archived the completed release ledger ${prior.tag} to ${archivePath} (live-proven at ${prior.tagSha.slice(0, 12)})` };
|
|
21406
21335
|
});
|
|
21407
21336
|
}
|
|
@@ -21498,10 +21427,10 @@ function readOriginWorkflowsDefault(root, ref) {
|
|
|
21498
21427
|
}
|
|
21499
21428
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
21500
21429
|
async function probeRegistryWhoami(train) {
|
|
21501
|
-
const dir = (0,
|
|
21430
|
+
const dir = (0, import_node_fs22.mkdtempSync)((0, import_node_path22.join)((0, import_node_os13.tmpdir)(), "mmi-npmrc-"));
|
|
21502
21431
|
const rc = (0, import_node_path22.join)(dir, "npmrc");
|
|
21503
21432
|
try {
|
|
21504
|
-
(0,
|
|
21433
|
+
(0, import_node_fs22.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
|
|
21505
21434
|
await train.runSelf(["secrets", "use", "npm/NPM_TOKEN", "--slug", "_org", "--", "npm", "whoami", "--registry", NPM_REGISTRY, "--userconfig", rc]);
|
|
21506
21435
|
return { kind: "ok", detail: "the registry answered a login" };
|
|
21507
21436
|
} catch (e) {
|
|
@@ -21512,7 +21441,7 @@ async function probeRegistryWhoami(train) {
|
|
|
21512
21441
|
if (/\bE401\b|401 Unauthorized/.test(stderr)) return { kind: "rejected", detail: "npm whoami answered 401 Unauthorized" };
|
|
21513
21442
|
return { kind: "unverified", detail: first };
|
|
21514
21443
|
} finally {
|
|
21515
|
-
(0,
|
|
21444
|
+
(0, import_node_fs22.rmSync)(dir, { recursive: true, force: true });
|
|
21516
21445
|
}
|
|
21517
21446
|
}
|
|
21518
21447
|
var REGISTRY_TOKEN_ENV = /\b(NODE_AUTH_TOKEN|NPM_TOKEN)\b/;
|
|
@@ -21530,7 +21459,7 @@ var defaultDeps2 = {
|
|
|
21530
21459
|
ledgerPath: releaseLedgerPath,
|
|
21531
21460
|
readLedgerRaw: (path2) => {
|
|
21532
21461
|
try {
|
|
21533
|
-
return (0,
|
|
21462
|
+
return (0, import_node_fs22.readFileSync)(path2, "utf8");
|
|
21534
21463
|
} catch (e) {
|
|
21535
21464
|
if (e.code === "ENOENT") return void 0;
|
|
21536
21465
|
throw e;
|
|
@@ -21539,7 +21468,7 @@ var defaultDeps2 = {
|
|
|
21539
21468
|
healAlignmentLeg: healStaleAlignmentLeg,
|
|
21540
21469
|
readSurfacesRaw: (cwd) => {
|
|
21541
21470
|
try {
|
|
21542
|
-
return (0,
|
|
21471
|
+
return (0, import_node_fs22.readFileSync)((0, import_node_path22.join)(cwd, "surfaces.json"), "utf8");
|
|
21543
21472
|
} catch {
|
|
21544
21473
|
return void 0;
|
|
21545
21474
|
}
|
|
@@ -22029,14 +21958,14 @@ function readLocalGateWorkflows() {
|
|
|
22029
21958
|
const dir = (0, import_node_path23.join)(".github", "workflows");
|
|
22030
21959
|
let names;
|
|
22031
21960
|
try {
|
|
22032
|
-
names = (0,
|
|
21961
|
+
names = (0, import_node_fs23.readdirSync)(dir);
|
|
22033
21962
|
} catch {
|
|
22034
21963
|
return null;
|
|
22035
21964
|
}
|
|
22036
21965
|
const files = [];
|
|
22037
21966
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
22038
21967
|
try {
|
|
22039
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
21968
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(dir, name), "utf8") });
|
|
22040
21969
|
} catch {
|
|
22041
21970
|
}
|
|
22042
21971
|
}
|
|
@@ -22046,14 +21975,14 @@ function readLocalWorkflows() {
|
|
|
22046
21975
|
const dir = (0, import_node_path23.join)(".github", "workflows");
|
|
22047
21976
|
let names;
|
|
22048
21977
|
try {
|
|
22049
|
-
names = (0,
|
|
21978
|
+
names = (0, import_node_fs23.readdirSync)(dir);
|
|
22050
21979
|
} catch {
|
|
22051
21980
|
return null;
|
|
22052
21981
|
}
|
|
22053
21982
|
const files = [];
|
|
22054
21983
|
for (const name of names.filter((n) => /\.ya?ml$/i.test(n))) {
|
|
22055
21984
|
try {
|
|
22056
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
21985
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(dir, name), "utf8") });
|
|
22057
21986
|
} catch {
|
|
22058
21987
|
}
|
|
22059
21988
|
}
|
|
@@ -22823,7 +22752,7 @@ async function activeRcandLedgerCandidate(deps, cwd) {
|
|
|
22823
22752
|
}
|
|
22824
22753
|
let prior;
|
|
22825
22754
|
try {
|
|
22826
|
-
prior = parseReleaseLedger((0,
|
|
22755
|
+
prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
22827
22756
|
} catch {
|
|
22828
22757
|
return void 0;
|
|
22829
22758
|
}
|
|
@@ -25874,22 +25803,6 @@ async function loadConfigForBoardSelector2(selector, repoOption) {
|
|
|
25874
25803
|
return loadConfigForRepo(repoFromSelector(selector) ?? repoOption);
|
|
25875
25804
|
}
|
|
25876
25805
|
|
|
25877
|
-
// src/statusline-invalidate.ts
|
|
25878
|
-
var import_node_child_process10 = require("node:child_process");
|
|
25879
|
-
function invalidateStatuslineBoardCache() {
|
|
25880
|
-
try {
|
|
25881
|
-
const child2 = (0, import_node_child_process10.spawn)("jerv-cli", ["lane", "ops", "invalidate-board"], {
|
|
25882
|
-
detached: true,
|
|
25883
|
-
stdio: "ignore",
|
|
25884
|
-
windowsHide: true
|
|
25885
|
-
});
|
|
25886
|
-
child2.on("error", () => {
|
|
25887
|
-
});
|
|
25888
|
-
child2.unref();
|
|
25889
|
-
} catch {
|
|
25890
|
-
}
|
|
25891
|
-
}
|
|
25892
|
-
|
|
25893
25806
|
// src/board-commands.ts
|
|
25894
25807
|
function refuseRateLimited(e, json) {
|
|
25895
25808
|
if (!isRateLimitedError(e)) return false;
|
|
@@ -25990,7 +25903,6 @@ function registerBoardCommands(program3) {
|
|
|
25990
25903
|
check: o.check,
|
|
25991
25904
|
allowPartial: o.allowPartial
|
|
25992
25905
|
}, { snapshot: registryClientDeps(config) });
|
|
25993
|
-
if (!result.checked) invalidateStatuslineBoardCache();
|
|
25994
25906
|
if (o.json) return console.log(JSON.stringify(result));
|
|
25995
25907
|
console.log(claimVerdict(result.item.ref, result));
|
|
25996
25908
|
printClaimWarnings(result.warnings);
|
|
@@ -26012,7 +25924,6 @@ function registerBoardCommands(program3) {
|
|
|
26012
25924
|
check: o.check,
|
|
26013
25925
|
allowPartial: o.allowPartial
|
|
26014
25926
|
}, { snapshot: registryClientDeps(config) });
|
|
26015
|
-
if (bulk.results.some((r) => r.claimed && !r.checked)) invalidateStatuslineBoardCache();
|
|
26016
25927
|
if (o.json) {
|
|
26017
25928
|
console.log(JSON.stringify(bulk.results));
|
|
26018
25929
|
printClaimWarnings(bulk.warnings, true);
|
|
@@ -26068,7 +25979,6 @@ function registerBoardCommands(program3) {
|
|
|
26068
25979
|
repo: o.repo,
|
|
26069
25980
|
allowPartial: o.allowPartial
|
|
26070
25981
|
});
|
|
26071
|
-
if (bulk.results.some((r) => r.moved)) invalidateStatuslineBoardCache();
|
|
26072
25982
|
if (o.json) {
|
|
26073
25983
|
console.log(JSON.stringify(bulk.results));
|
|
26074
25984
|
} else {
|
|
@@ -26093,7 +26003,6 @@ function registerBoardCommands(program3) {
|
|
|
26093
26003
|
if (issueRefs.length === 1) {
|
|
26094
26004
|
try {
|
|
26095
26005
|
const result = await moveBoardItem({ config: await loadConfigForBoardSelector2(issueRefs[0], o.repo), selector: issueRefs[0], status: canonicalStatus, repo: o.repo, allowPartial: o.allowPartial });
|
|
26096
|
-
invalidateStatuslineBoardCache();
|
|
26097
26006
|
if (o.json) return console.log(JSON.stringify(result));
|
|
26098
26007
|
console.log(result.archived ? `Already terminal ${result.item.ref}: ${result.warning}` : result.partial ? `Partially moved ${result.item.ref}: ${result.warning}` : `Moved ${result.item.ref} -> ${result.status}`);
|
|
26099
26008
|
} catch (e) {
|
|
@@ -26145,7 +26054,6 @@ function registerBoardCommands(program3) {
|
|
|
26145
26054
|
force: o.force,
|
|
26146
26055
|
allowPartial: o.allowPartial
|
|
26147
26056
|
});
|
|
26148
|
-
invalidateStatuslineBoardCache();
|
|
26149
26057
|
if (o.json) return console.log(JSON.stringify(result));
|
|
26150
26058
|
console.log(result.partial ? `Partially unclaimed ${result.item.ref}: ${result.warning}` : `Unclaimed ${result.item.ref} -> ${result.status}`);
|
|
26151
26059
|
} catch (e) {
|
|
@@ -26159,7 +26067,6 @@ function registerBoardCommands(program3) {
|
|
|
26159
26067
|
repo: o.repo,
|
|
26160
26068
|
apply: o.apply
|
|
26161
26069
|
});
|
|
26162
|
-
if (result.archived.length) invalidateStatuslineBoardCache();
|
|
26163
26070
|
if (o.json) return console.log(JSON.stringify(result));
|
|
26164
26071
|
console.log(o.apply ? `board archive-done: archived ${result.archived.length}/${result.candidates.length}, failed ${result.failures.length}` : `board archive-done: would archive ${result.candidates.length} closed Done cards (scanned ${result.scanned}; pass --apply to write)`);
|
|
26165
26072
|
if (result.failures.length) process.exitCode = 1;
|
|
@@ -26195,7 +26102,7 @@ function registerBoardCommands(program3) {
|
|
|
26195
26102
|
}
|
|
26196
26103
|
|
|
26197
26104
|
// src/bootstrap-commands.ts
|
|
26198
|
-
var
|
|
26105
|
+
var import_node_fs25 = require("node:fs");
|
|
26199
26106
|
var import_node_os14 = require("node:os");
|
|
26200
26107
|
var import_node_path25 = require("node:path");
|
|
26201
26108
|
|
|
@@ -26362,7 +26269,7 @@ function renderSteps(title, steps) {
|
|
|
26362
26269
|
}
|
|
26363
26270
|
|
|
26364
26271
|
// src/port-registry.ts
|
|
26365
|
-
var
|
|
26272
|
+
var import_node_fs24 = require("node:fs");
|
|
26366
26273
|
var import_node_path24 = require("node:path");
|
|
26367
26274
|
|
|
26368
26275
|
// ../infra/port-geometry.mjs
|
|
@@ -26377,8 +26284,8 @@ function nextPortBlock(registry2) {
|
|
|
26377
26284
|
return [base, base + PORT_SPAN];
|
|
26378
26285
|
}
|
|
26379
26286
|
function loadPortRegistry(path2) {
|
|
26380
|
-
if (!(0,
|
|
26381
|
-
const raw = JSON.parse((0,
|
|
26287
|
+
if (!(0, import_node_fs24.existsSync)(path2)) return {};
|
|
26288
|
+
const raw = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
|
|
26382
26289
|
const out = {};
|
|
26383
26290
|
for (const [key, value] of Object.entries(raw)) {
|
|
26384
26291
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -26392,9 +26299,9 @@ function ensurePortRange(repo, path2) {
|
|
|
26392
26299
|
const existing = registry2[repo];
|
|
26393
26300
|
if (existing) return existing;
|
|
26394
26301
|
const range = nextPortBlock(registry2);
|
|
26395
|
-
const raw = (0,
|
|
26302
|
+
const raw = (0, import_node_fs24.existsSync)(path2) ? JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8")) : {};
|
|
26396
26303
|
raw[repo] = range;
|
|
26397
|
-
(0,
|
|
26304
|
+
(0, import_node_fs24.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
26398
26305
|
return range;
|
|
26399
26306
|
}
|
|
26400
26307
|
function portCursorSeed(registry2) {
|
|
@@ -26418,7 +26325,7 @@ function existingPortRange(repo, registry2) {
|
|
|
26418
26325
|
function portRangeInfraAt(root, source) {
|
|
26419
26326
|
const registryPath = (0, import_node_path24.join)(root, "infra", "port-ranges.json");
|
|
26420
26327
|
const ddbScriptPath = (0, import_node_path24.join)(root, "infra", "port-ddb.mjs");
|
|
26421
|
-
if (!(0,
|
|
26328
|
+
if (!(0, import_node_fs24.existsSync)(registryPath) || !(0, import_node_fs24.existsSync)(ddbScriptPath)) return null;
|
|
26422
26329
|
return { root, source, registryPath, ddbScriptPath };
|
|
26423
26330
|
}
|
|
26424
26331
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
@@ -27900,13 +27807,13 @@ function registerBootstrapCommands(program3) {
|
|
|
27900
27807
|
deployFactsRead: deployFacts !== null,
|
|
27901
27808
|
pushAllowlistOwners: verifyOwners,
|
|
27902
27809
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
27903
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
27810
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null,
|
|
27904
27811
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
27905
27812
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
27906
27813
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
27907
27814
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
27908
27815
|
// sanction, which is the pre-#3664 behaviour.
|
|
27909
|
-
sanctionedAdmins: (0,
|
|
27816
|
+
sanctionedAdmins: (0, import_node_fs25.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs25.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
27910
27817
|
requiredGcpApis: (() => {
|
|
27911
27818
|
const v = meta?.requiredGcpApis;
|
|
27912
27819
|
if (Array.isArray(v)) return v;
|
|
@@ -27984,14 +27891,14 @@ function registerBootstrapCommands(program3) {
|
|
|
27984
27891
|
bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
|
|
27985
27892
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
27986
27893
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
27987
|
-
if (!(0,
|
|
27894
|
+
if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
|
|
27988
27895
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
27989
27896
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
27990
|
-
const manifest = loadBootstrapSeeds((0,
|
|
27897
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
|
|
27991
27898
|
const hubContents = /* @__PURE__ */ new Map();
|
|
27992
27899
|
for (const s of manifest.seeds) {
|
|
27993
27900
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
27994
|
-
hubContents.set(s.target, (0,
|
|
27901
|
+
hubContents.set(s.target, (0, import_node_fs25.existsSync)(s.target) ? (0, import_node_fs25.readFileSync)(s.target, "utf8") : null);
|
|
27995
27902
|
}
|
|
27996
27903
|
let targets;
|
|
27997
27904
|
let classOf = (_repo) => "deployable";
|
|
@@ -28154,10 +28061,10 @@ function registerBootstrapCommands(program3) {
|
|
|
28154
28061
|
return;
|
|
28155
28062
|
}
|
|
28156
28063
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
28157
|
-
if (!(0,
|
|
28064
|
+
if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
|
|
28158
28065
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
28159
28066
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
28160
|
-
const manifest = loadBootstrapSeeds((0,
|
|
28067
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
|
|
28161
28068
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
28162
28069
|
const slug = parsedRepo.slug;
|
|
28163
28070
|
const onlyTarget = o.only.trim();
|
|
@@ -28169,16 +28076,16 @@ function registerBootstrapCommands(program3) {
|
|
|
28169
28076
|
}
|
|
28170
28077
|
const onlyManagedBlock = onlyTarget ? seedsToApply[0]?.managedBlock != null : false;
|
|
28171
28078
|
const gh = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
28172
|
-
const readFile9 = (p) => (0,
|
|
28079
|
+
const readFile9 = (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null;
|
|
28173
28080
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
28174
28081
|
const putSeed = async (target, content, ref, sha) => {
|
|
28175
28082
|
const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
28176
|
-
(0,
|
|
28083
|
+
(0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
28177
28084
|
try {
|
|
28178
28085
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
28179
28086
|
} finally {
|
|
28180
28087
|
try {
|
|
28181
|
-
(0,
|
|
28088
|
+
(0, import_node_fs25.unlinkSync)(tmp);
|
|
28182
28089
|
} catch {
|
|
28183
28090
|
}
|
|
28184
28091
|
}
|
|
@@ -28627,10 +28534,10 @@ LIVE apply to ${repo}:
|
|
|
28627
28534
|
bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an org-owned whole file or declared Hub-managed block)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
|
|
28628
28535
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
28629
28536
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
28630
|
-
if (!(0,
|
|
28537
|
+
if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
|
|
28631
28538
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
28632
28539
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
28633
|
-
const manifest = loadBootstrapSeeds((0,
|
|
28540
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
|
|
28634
28541
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
28635
28542
|
if (!o.target) {
|
|
28636
28543
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -28639,9 +28546,9 @@ LIVE apply to ${repo}:
|
|
|
28639
28546
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
28640
28547
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
|
|
28641
28548
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
28642
|
-
if (!seed.managedBlock && !(0,
|
|
28643
|
-
const hubContent = seed.managedBlock ? null : (0,
|
|
28644
|
-
const readSeedFile2 = (path2) => (0,
|
|
28549
|
+
if (!seed.managedBlock && !(0, import_node_fs25.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
28550
|
+
const hubContent = seed.managedBlock ? null : (0, import_node_fs25.readFileSync)(seed.target, "utf8");
|
|
28551
|
+
const readSeedFile2 = (path2) => (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null;
|
|
28645
28552
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
28646
28553
|
const cfg = await loadConfig();
|
|
28647
28554
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -28650,9 +28557,9 @@ LIVE apply to ${repo}:
|
|
|
28650
28557
|
}
|
|
28651
28558
|
const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
28652
28559
|
let independentCount = rosterRepos.length;
|
|
28653
|
-
if ((0,
|
|
28560
|
+
if ((0, import_node_fs25.existsSync)("projects.json")) {
|
|
28654
28561
|
try {
|
|
28655
|
-
const local = JSON.parse((0,
|
|
28562
|
+
const local = JSON.parse((0, import_node_fs25.readFileSync)("projects.json", "utf8"));
|
|
28656
28563
|
const localRepos = /* @__PURE__ */ new Set();
|
|
28657
28564
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
28658
28565
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -28816,12 +28723,12 @@ LIVE apply to ${repo}:
|
|
|
28816
28723
|
const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
28817
28724
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
28818
28725
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
28819
|
-
(0,
|
|
28726
|
+
(0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
|
|
28820
28727
|
try {
|
|
28821
28728
|
await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
28822
28729
|
} finally {
|
|
28823
28730
|
try {
|
|
28824
|
-
(0,
|
|
28731
|
+
(0, import_node_fs25.unlinkSync)(tmp);
|
|
28825
28732
|
} catch {
|
|
28826
28733
|
}
|
|
28827
28734
|
}
|
|
@@ -28888,10 +28795,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
28888
28795
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
28889
28796
|
}
|
|
28890
28797
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
28891
|
-
if (!(0,
|
|
28798
|
+
if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
|
|
28892
28799
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
28893
28800
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
28894
|
-
const manifest = loadBootstrapSeeds((0,
|
|
28801
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
|
|
28895
28802
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
28896
28803
|
if (!o.target) {
|
|
28897
28804
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -28908,10 +28815,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
28908
28815
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
28909
28816
|
let candidates;
|
|
28910
28817
|
if (o.record) {
|
|
28911
|
-
if (!(0,
|
|
28818
|
+
if (!(0, import_node_fs25.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
28912
28819
|
let parsed;
|
|
28913
28820
|
try {
|
|
28914
|
-
parsed = JSON.parse((0,
|
|
28821
|
+
parsed = JSON.parse((0, import_node_fs25.readFileSync)(o.record, "utf8"));
|
|
28915
28822
|
} catch (e) {
|
|
28916
28823
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
28917
28824
|
}
|
|
@@ -28989,12 +28896,12 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
28989
28896
|
existingSha = void 0;
|
|
28990
28897
|
}
|
|
28991
28898
|
const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
28992
|
-
(0,
|
|
28899
|
+
(0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
28993
28900
|
try {
|
|
28994
28901
|
await gh(contentPutInputArgs(repo, seed.target, tmp));
|
|
28995
28902
|
} finally {
|
|
28996
28903
|
try {
|
|
28997
|
-
(0,
|
|
28904
|
+
(0, import_node_fs25.unlinkSync)(tmp);
|
|
28998
28905
|
} catch {
|
|
28999
28906
|
}
|
|
29000
28907
|
}
|
|
@@ -29159,7 +29066,7 @@ function registerDeployCommands(program3) {
|
|
|
29159
29066
|
}
|
|
29160
29067
|
|
|
29161
29068
|
// src/project-set.ts
|
|
29162
|
-
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "tenantTasks", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "requiredCheckBranches", "ciExemptReason", "gate", "seedCanary"];
|
|
29069
|
+
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "tenantTasks", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "releaseChannel", "releaseLanguage", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "requiredCheckBranches", "ciExemptReason", "gate", "seedCanary"];
|
|
29163
29070
|
var UNSET_KEY_SET = new Set(UNSET_KEYS);
|
|
29164
29071
|
var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
|
|
29165
29072
|
var SECRET_CONSUMERS = ["runtime", "build", "lambda", "actions", "agent", "box"];
|
|
@@ -29443,6 +29350,20 @@ function parsePublishDirVar(raw) {
|
|
|
29443
29350
|
}
|
|
29444
29351
|
return v;
|
|
29445
29352
|
}
|
|
29353
|
+
function parseReleaseChannelVar(raw) {
|
|
29354
|
+
const v = raw.trim();
|
|
29355
|
+
if (!/^[CG][A-Z0-9]{8,}$/.test(v)) {
|
|
29356
|
+
throw new Error("org project set: releaseChannel must be a Slack channel id (e.g. C0BE0BRGAT0); --unset releaseChannel disables release notes");
|
|
29357
|
+
}
|
|
29358
|
+
return v;
|
|
29359
|
+
}
|
|
29360
|
+
function parseReleaseLanguageVar(raw) {
|
|
29361
|
+
const v = raw.trim();
|
|
29362
|
+
if (!/^[a-z]{2}$/.test(v)) {
|
|
29363
|
+
throw new Error("org project set: releaseLanguage must be a 2-letter lowercase language code (e.g. tr); --unset releaseLanguage returns to en");
|
|
29364
|
+
}
|
|
29365
|
+
return v;
|
|
29366
|
+
}
|
|
29446
29367
|
function parseDsManifestPathVar(raw) {
|
|
29447
29368
|
const v = raw.trim();
|
|
29448
29369
|
if (v === "" || !/^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/.test(v) || /(^|\/)\.\.(\/|$)/.test(v)) {
|
|
@@ -29559,6 +29480,8 @@ var SETTABLE_VAR_KEYS = [
|
|
|
29559
29480
|
"oauth",
|
|
29560
29481
|
"publishRequired",
|
|
29561
29482
|
"publishDir",
|
|
29483
|
+
"releaseChannel",
|
|
29484
|
+
"releaseLanguage",
|
|
29562
29485
|
"dsManifestPath",
|
|
29563
29486
|
"fofuEnabled",
|
|
29564
29487
|
"consumesDesignSystem",
|
|
@@ -29586,6 +29509,8 @@ var SETTABLE_VAR_HINTS = {
|
|
|
29586
29509
|
projectNumber: "numeric",
|
|
29587
29510
|
publishRequired: "true|false",
|
|
29588
29511
|
publishDir: "relative subpath, e.g. packages/ui",
|
|
29512
|
+
releaseChannel: "Slack channel id, e.g. C0BE0BRGAT0",
|
|
29513
|
+
releaseLanguage: "2-letter language code for release notes, e.g. tr (default en)",
|
|
29589
29514
|
dsManifestPath: "relative path to a package.json, e.g. web/package.json",
|
|
29590
29515
|
fofuEnabled: "true|false",
|
|
29591
29516
|
consumesDesignSystem: '"fofu"',
|
|
@@ -29698,6 +29623,10 @@ function buildProjectSetPatch(input) {
|
|
|
29698
29623
|
patch[key] = parseConsumesDesignSystemVar(raw);
|
|
29699
29624
|
} else if (key === "publishDir") {
|
|
29700
29625
|
patch[key] = parsePublishDirVar(raw);
|
|
29626
|
+
} else if (key === "releaseChannel") {
|
|
29627
|
+
patch[key] = parseReleaseChannelVar(raw);
|
|
29628
|
+
} else if (key === "releaseLanguage") {
|
|
29629
|
+
patch[key] = parseReleaseLanguageVar(raw);
|
|
29701
29630
|
} else if (key === "dsManifestPath") {
|
|
29702
29631
|
patch[key] = parseDsManifestPathVar(raw);
|
|
29703
29632
|
} else if (key === "ci") {
|
|
@@ -29986,7 +29915,7 @@ function renderDeployPortDoctor(report) {
|
|
|
29986
29915
|
}
|
|
29987
29916
|
|
|
29988
29917
|
// src/wave-status.ts
|
|
29989
|
-
var
|
|
29918
|
+
var import_node_fs28 = require("node:fs");
|
|
29990
29919
|
|
|
29991
29920
|
// src/git-worktree-observation.ts
|
|
29992
29921
|
function parseGitWorktreePorcelain(text) {
|
|
@@ -30016,8 +29945,8 @@ function deriveComposeProjectName(worktreePath) {
|
|
|
30016
29945
|
}
|
|
30017
29946
|
|
|
30018
29947
|
// src/stage-runner.ts
|
|
30019
|
-
var
|
|
30020
|
-
var
|
|
29948
|
+
var import_node_child_process10 = require("node:child_process");
|
|
29949
|
+
var import_node_fs27 = require("node:fs");
|
|
30021
29950
|
var import_node_path27 = require("node:path");
|
|
30022
29951
|
var import_node_net = require("node:net");
|
|
30023
29952
|
var import_node_util5 = require("node:util");
|
|
@@ -30028,7 +29957,7 @@ function normalizeEol2(s) {
|
|
|
30028
29957
|
}
|
|
30029
29958
|
|
|
30030
29959
|
// src/env-detection.ts
|
|
30031
|
-
var
|
|
29960
|
+
var import_node_fs26 = require("node:fs");
|
|
30032
29961
|
var import_node_path26 = require("node:path");
|
|
30033
29962
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
30034
29963
|
function isEnvBaseName(name) {
|
|
@@ -30039,7 +29968,7 @@ function findEnvFiles(root) {
|
|
|
30039
29968
|
const walk2 = (dir, rel) => {
|
|
30040
29969
|
let entries;
|
|
30041
29970
|
try {
|
|
30042
|
-
entries = (0,
|
|
29971
|
+
entries = (0, import_node_fs26.readdirSync)(dir, { withFileTypes: true });
|
|
30043
29972
|
} catch {
|
|
30044
29973
|
return;
|
|
30045
29974
|
}
|
|
@@ -30053,7 +29982,7 @@ function findEnvFiles(root) {
|
|
|
30053
29982
|
if (isEnvBaseName(entry.name)) {
|
|
30054
29983
|
let isDir = false;
|
|
30055
29984
|
try {
|
|
30056
|
-
isDir = (0,
|
|
29985
|
+
isDir = (0, import_node_fs26.lstatSync)((0, import_node_path26.join)(dir, entry.name)).isDirectory();
|
|
30057
29986
|
} catch {
|
|
30058
29987
|
}
|
|
30059
29988
|
if (!isDir) found.push(relPath);
|
|
@@ -30110,17 +30039,17 @@ function stageComposeFileEnv(files) {
|
|
|
30110
30039
|
}
|
|
30111
30040
|
|
|
30112
30041
|
// src/stage-runner.ts
|
|
30113
|
-
var execFileP3 = (0, import_node_util5.promisify)(
|
|
30042
|
+
var execFileP3 = (0, import_node_util5.promisify)(import_node_child_process10.execFile);
|
|
30114
30043
|
var DOCKER_TIMEOUT_MS = 15e3;
|
|
30115
30044
|
function stageBash() {
|
|
30116
30045
|
if (process.platform !== "win32") return "bash";
|
|
30117
30046
|
const configured = process.env.SHELL;
|
|
30118
|
-
if (configured && (0, import_node_path27.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0,
|
|
30047
|
+
if (configured && (0, import_node_path27.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs27.existsSync)(configured)) return configured;
|
|
30119
30048
|
const candidates = [
|
|
30120
30049
|
(0, import_node_path27.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
30121
30050
|
...process.env.LOCALAPPDATA ? [(0, import_node_path27.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
30122
30051
|
];
|
|
30123
|
-
const bash = candidates.find((path2) => (0,
|
|
30052
|
+
const bash = candidates.find((path2) => (0, import_node_fs27.existsSync)(path2));
|
|
30124
30053
|
if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
|
|
30125
30054
|
return bash;
|
|
30126
30055
|
}
|
|
@@ -30469,7 +30398,7 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
30469
30398
|
function composeResolvesPort(cwd) {
|
|
30470
30399
|
if (process.env.PORT) return true;
|
|
30471
30400
|
const envFile = (0, import_node_path27.join)(cwd, ".env");
|
|
30472
|
-
return (0,
|
|
30401
|
+
return (0, import_node_fs27.existsSync)(envFile) && envFileKeys((0, import_node_fs27.readFileSync)(envFile, "utf8")).has("PORT");
|
|
30473
30402
|
}
|
|
30474
30403
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
30475
30404
|
return {
|
|
@@ -30482,13 +30411,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
30482
30411
|
function stageComposeFiles(cwd) {
|
|
30483
30412
|
const files = ["docker-compose.yml"];
|
|
30484
30413
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
30485
|
-
if ((0,
|
|
30414
|
+
if ((0, import_node_fs27.existsSync)((0, import_node_path27.join)(cwd, conventional))) files.push(conventional);
|
|
30486
30415
|
}
|
|
30487
30416
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
30488
30417
|
}
|
|
30489
30418
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
30490
30419
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
30491
|
-
if (!keys.length || !(0,
|
|
30420
|
+
if (!keys.length || !(0, import_node_fs27.existsSync)((0, import_node_path27.join)(cwd, "docker-compose.yml"))) return {};
|
|
30492
30421
|
let configJson;
|
|
30493
30422
|
try {
|
|
30494
30423
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -30505,20 +30434,20 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
30505
30434
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys);
|
|
30506
30435
|
if (!body) return {};
|
|
30507
30436
|
const overridePath = (0, import_node_path27.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
30508
|
-
(0,
|
|
30509
|
-
(0,
|
|
30437
|
+
(0, import_node_fs27.mkdirSync)((0, import_node_path27.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
30438
|
+
(0, import_node_fs27.writeFileSync)(overridePath, body, "utf8");
|
|
30510
30439
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
30511
30440
|
}
|
|
30512
30441
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
30513
30442
|
if (!config.ensureEnv) return;
|
|
30514
30443
|
const target = (0, import_node_path27.join)(cwd, config.ensureEnv.target);
|
|
30515
30444
|
const example = (0, import_node_path27.join)(cwd, config.ensureEnv.example);
|
|
30516
|
-
if (!(0,
|
|
30517
|
-
(0,
|
|
30518
|
-
} else if ((0,
|
|
30519
|
-
const stale = detectStaleEnvFile((0,
|
|
30520
|
-
exampleMtimeMs: (0,
|
|
30521
|
-
targetMtimeMs: (0,
|
|
30445
|
+
if (!(0, import_node_fs27.existsSync)(target) && (0, import_node_fs27.existsSync)(example)) {
|
|
30446
|
+
(0, import_node_fs27.copyFileSync)(example, target);
|
|
30447
|
+
} else if ((0, import_node_fs27.existsSync)(target) && (0, import_node_fs27.existsSync)(example)) {
|
|
30448
|
+
const stale = detectStaleEnvFile((0, import_node_fs27.readFileSync)(example, "utf8"), (0, import_node_fs27.readFileSync)(target, "utf8"), {
|
|
30449
|
+
exampleMtimeMs: (0, import_node_fs27.statSync)(example).mtimeMs,
|
|
30450
|
+
targetMtimeMs: (0, import_node_fs27.statSync)(target).mtimeMs
|
|
30522
30451
|
});
|
|
30523
30452
|
if (stale) {
|
|
30524
30453
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -30526,8 +30455,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
30526
30455
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
30527
30456
|
}
|
|
30528
30457
|
}
|
|
30529
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
30530
|
-
(0,
|
|
30458
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs27.existsSync)(target)) {
|
|
30459
|
+
(0, import_node_fs27.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs27.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
30531
30460
|
}
|
|
30532
30461
|
}
|
|
30533
30462
|
async function gitText(cwd, args) {
|
|
@@ -30557,20 +30486,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
30557
30486
|
return void 0;
|
|
30558
30487
|
}
|
|
30559
30488
|
function readState(path2) {
|
|
30560
|
-
if (!(0,
|
|
30489
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return null;
|
|
30561
30490
|
try {
|
|
30562
|
-
return JSON.parse((0,
|
|
30491
|
+
return JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
30563
30492
|
} catch {
|
|
30564
30493
|
return null;
|
|
30565
30494
|
}
|
|
30566
30495
|
}
|
|
30567
30496
|
function mkdirFor(path2) {
|
|
30568
30497
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
30569
|
-
(0,
|
|
30498
|
+
(0, import_node_fs27.mkdirSync)(dir, { recursive: true });
|
|
30570
30499
|
}
|
|
30571
30500
|
function writeState(path2, state) {
|
|
30572
30501
|
mkdirFor(path2);
|
|
30573
|
-
(0,
|
|
30502
|
+
(0, import_node_fs27.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
30574
30503
|
}
|
|
30575
30504
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
30576
30505
|
const reservation = {
|
|
@@ -30599,7 +30528,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
30599
30528
|
);
|
|
30600
30529
|
}
|
|
30601
30530
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
30602
|
-
(0,
|
|
30531
|
+
(0, import_node_fs27.rmSync)(path2, { force: true });
|
|
30603
30532
|
}
|
|
30604
30533
|
}
|
|
30605
30534
|
async function killTree(pid) {
|
|
@@ -30693,7 +30622,7 @@ async function startStage(config = {}, opts = {}) {
|
|
|
30693
30622
|
let up = sub(config.up.trim());
|
|
30694
30623
|
if (opts.forceRecreate) up = appendForceRecreate(up);
|
|
30695
30624
|
const identity = await resolveStageIdentity(cwd);
|
|
30696
|
-
const child2 = (0,
|
|
30625
|
+
const child2 = (0, import_node_child_process10.spawn)(bash, ["-c", up], {
|
|
30697
30626
|
cwd,
|
|
30698
30627
|
// POSIX-only: the process group exists for the group-kill in stopStage. On win32 teardown is
|
|
30699
30628
|
// `taskkill /T /F` (no group needed), and detached+shell defeats windowsHide — every spawn would
|
|
@@ -30786,8 +30715,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
30786
30715
|
});
|
|
30787
30716
|
}
|
|
30788
30717
|
} catch (e) {
|
|
30789
|
-
(0,
|
|
30790
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
30718
|
+
(0, import_node_fs27.rmSync)(statePath, { force: true });
|
|
30719
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs27.rmSync)(globalStatePath, { force: true });
|
|
30791
30720
|
throw e;
|
|
30792
30721
|
}
|
|
30793
30722
|
const started = await startStage(config, {
|
|
@@ -30804,9 +30733,9 @@ async function runStage(config = {}, opts = {}) {
|
|
|
30804
30733
|
// src/wave-status.ts
|
|
30805
30734
|
function readStageSummary(worktreePath) {
|
|
30806
30735
|
const statePath = stageStatePath(worktreePath);
|
|
30807
|
-
if (!(0,
|
|
30736
|
+
if (!(0, import_node_fs28.existsSync)(statePath)) return void 0;
|
|
30808
30737
|
try {
|
|
30809
|
-
const state = JSON.parse((0,
|
|
30738
|
+
const state = JSON.parse((0, import_node_fs28.readFileSync)(statePath, "utf8"));
|
|
30810
30739
|
const port = typeof state.port === "number" ? state.port : void 0;
|
|
30811
30740
|
if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
|
|
30812
30741
|
return { port, url: typeof state.url === "string" ? state.url : void 0 };
|
|
@@ -30861,7 +30790,7 @@ async function collectWaveStatus(deps) {
|
|
|
30861
30790
|
}
|
|
30862
30791
|
|
|
30863
30792
|
// src/discovery-commands.ts
|
|
30864
|
-
var
|
|
30793
|
+
var import_node_fs29 = require("node:fs");
|
|
30865
30794
|
var import_node_os15 = require("node:os");
|
|
30866
30795
|
var import_node_path28 = require("node:path");
|
|
30867
30796
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
@@ -31053,8 +30982,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
31053
30982
|
}
|
|
31054
30983
|
const home = (0, import_node_os15.homedir)();
|
|
31055
30984
|
const plugin = onboardPluginGate({
|
|
31056
|
-
readKnown: () => readFileSyncSafe((0, import_node_path28.join)(home, ...KNOWN_MARKETPLACES_RELATIVE),
|
|
31057
|
-
readSettings: () => readFileSyncSafe((0, import_node_path28.join)(home, ".claude", "settings.json"),
|
|
30985
|
+
readKnown: () => readFileSyncSafe((0, import_node_path28.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs29.readFileSync),
|
|
30986
|
+
readSettings: () => readFileSyncSafe((0, import_node_path28.join)(home, ".claude", "settings.json"), import_node_fs29.readFileSync)
|
|
31058
30987
|
});
|
|
31059
30988
|
return {
|
|
31060
30989
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -33007,7 +32936,7 @@ function parseOriginRepo(remoteUrl) {
|
|
|
33007
32936
|
}
|
|
33008
32937
|
|
|
33009
32938
|
// src/issue-commands.ts
|
|
33010
|
-
var
|
|
32939
|
+
var import_node_fs30 = require("node:fs");
|
|
33011
32940
|
var import_node_crypto10 = require("node:crypto");
|
|
33012
32941
|
|
|
33013
32942
|
// src/issue-body.ts
|
|
@@ -33257,7 +33186,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
33257
33186
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
33258
33187
|
const patch = {};
|
|
33259
33188
|
let bodyChanged = false;
|
|
33260
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
33189
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs30.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
33261
33190
|
if (options.titleFile !== void 0) {
|
|
33262
33191
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
33263
33192
|
} else if (options.title !== void 0) {
|
|
@@ -33913,7 +33842,7 @@ function extendCreateCommand(issue, batchAttach) {
|
|
|
33913
33842
|
if (opts.batch) {
|
|
33914
33843
|
let specs;
|
|
33915
33844
|
try {
|
|
33916
|
-
const raw = (0,
|
|
33845
|
+
const raw = (0, import_node_fs30.readFileSync)(opts.batch, "utf8");
|
|
33917
33846
|
specs = JSON.parse(raw);
|
|
33918
33847
|
if (!Array.isArray(specs)) {
|
|
33919
33848
|
const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
|
|
@@ -33965,7 +33894,6 @@ ${lines2}`, {
|
|
|
33965
33894
|
surface: batchSurface,
|
|
33966
33895
|
noSurface: batchNoSurface
|
|
33967
33896
|
}, { attach: batchAttach });
|
|
33968
|
-
if (result.created.some((row) => !row.idempotent)) invalidateStatuslineBoardCache();
|
|
33969
33897
|
console.log(JSON.stringify(result));
|
|
33970
33898
|
if (result.failures.length) process.exitCode = 1;
|
|
33971
33899
|
} catch (e) {
|
|
@@ -35073,7 +35001,7 @@ function registerPrLifecycleCommands(program3) {
|
|
|
35073
35001
|
}
|
|
35074
35002
|
|
|
35075
35003
|
// src/project-info-sync.ts
|
|
35076
|
-
var
|
|
35004
|
+
var import_node_fs31 = require("node:fs");
|
|
35077
35005
|
var import_node_path29 = require("node:path");
|
|
35078
35006
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
35079
35007
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
@@ -35122,13 +35050,13 @@ function sharedName(entries, fallback) {
|
|
|
35122
35050
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
35123
35051
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
35124
35052
|
const readmePath = (0, import_node_path29.join)(repoRoot2, "README.md");
|
|
35125
|
-
if (!(0,
|
|
35053
|
+
if (!(0, import_node_fs31.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
35126
35054
|
const entries = entriesFor(project2, projects);
|
|
35127
35055
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
35128
35056
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
35129
35057
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
35130
35058
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
35131
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
35059
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs31.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
35132
35060
|
const lines2 = [
|
|
35133
35061
|
`# ${projectName}`,
|
|
35134
35062
|
"",
|
|
@@ -35147,8 +35075,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
35147
35075
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
35148
35076
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
35149
35077
|
const orgDocs = [
|
|
35150
|
-
(0,
|
|
35151
|
-
(0,
|
|
35078
|
+
(0, import_node_fs31.existsSync)((0, import_node_path29.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
35079
|
+
(0, import_node_fs31.existsSync)((0, import_node_path29.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
35152
35080
|
].filter(Boolean);
|
|
35153
35081
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
35154
35082
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -35249,9 +35177,9 @@ function writeError(res) {
|
|
|
35249
35177
|
|
|
35250
35178
|
// src/schedules-commands.ts
|
|
35251
35179
|
var import_promises5 = require("node:fs/promises");
|
|
35252
|
-
var
|
|
35180
|
+
var import_node_child_process11 = require("node:child_process");
|
|
35253
35181
|
var import_node_util6 = require("node:util");
|
|
35254
|
-
var execFileP4 = (0, import_node_util6.promisify)(
|
|
35182
|
+
var execFileP4 = (0, import_node_util6.promisify)(import_node_child_process11.execFile);
|
|
35255
35183
|
var AWS_REGION = "eu-central-1";
|
|
35256
35184
|
var AWS_TIMEOUT_MS = 3e4;
|
|
35257
35185
|
var AWS_RETRY_DELAY_MS = 1500;
|
|
@@ -35566,7 +35494,7 @@ function registerSchedulesCommands(program3) {
|
|
|
35566
35494
|
}
|
|
35567
35495
|
|
|
35568
35496
|
// src/secrets-commands.ts
|
|
35569
|
-
var
|
|
35497
|
+
var import_node_fs32 = require("node:fs");
|
|
35570
35498
|
var import_node_path30 = require("node:path");
|
|
35571
35499
|
var import_node_os17 = require("node:os");
|
|
35572
35500
|
|
|
@@ -35680,8 +35608,8 @@ async function decryptRailsCredentials(input) {
|
|
|
35680
35608
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
35681
35609
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
35682
35610
|
};
|
|
35683
|
-
if ((0,
|
|
35684
|
-
env.RAILS_MASTER_KEY = (0,
|
|
35611
|
+
if ((0, import_node_fs32.existsSync)(masterKeyPath)) {
|
|
35612
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs32.readFileSync)(masterKeyPath, "utf8").trim();
|
|
35685
35613
|
}
|
|
35686
35614
|
const script = [
|
|
35687
35615
|
'require "json"',
|
|
@@ -35691,9 +35619,9 @@ async function decryptRailsCredentials(input) {
|
|
|
35691
35619
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
35692
35620
|
"puts JSON.generate(config.config)"
|
|
35693
35621
|
].join("\n");
|
|
35694
|
-
const scriptDir = (0,
|
|
35622
|
+
const scriptDir = (0, import_node_fs32.mkdtempSync)((0, import_node_path30.join)((0, import_node_os17.tmpdir)(), "mmi-rails-decrypt-"));
|
|
35695
35623
|
const scriptPath = (0, import_node_path30.join)(scriptDir, "decrypt.rb");
|
|
35696
|
-
(0,
|
|
35624
|
+
(0, import_node_fs32.writeFileSync)(scriptPath, script, "utf8");
|
|
35697
35625
|
try {
|
|
35698
35626
|
const args = ["exec", "ruby", scriptPath];
|
|
35699
35627
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -35705,7 +35633,7 @@ async function decryptRailsCredentials(input) {
|
|
|
35705
35633
|
});
|
|
35706
35634
|
return JSON.parse(stdout);
|
|
35707
35635
|
} finally {
|
|
35708
|
-
(0,
|
|
35636
|
+
(0, import_node_fs32.rmSync)(scriptDir, { recursive: true, force: true });
|
|
35709
35637
|
}
|
|
35710
35638
|
}
|
|
35711
35639
|
async function readSecretStdin() {
|
|
@@ -35795,7 +35723,7 @@ function registerSecretsCommands(program3) {
|
|
|
35795
35723
|
let body;
|
|
35796
35724
|
if (o.file) {
|
|
35797
35725
|
try {
|
|
35798
|
-
body = (0,
|
|
35726
|
+
body = (0, import_node_fs32.readFileSync)((0, import_node_path30.resolve)(o.file), "utf8");
|
|
35799
35727
|
} catch (e) {
|
|
35800
35728
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
35801
35729
|
}
|
|
@@ -35903,7 +35831,7 @@ function registerSecretsCommands(program3) {
|
|
|
35903
35831
|
{
|
|
35904
35832
|
...d,
|
|
35905
35833
|
decryptRailsCredentials,
|
|
35906
|
-
removeFile: (path2) => (0,
|
|
35834
|
+
removeFile: (path2) => (0, import_node_fs32.unlinkSync)((0, import_node_path30.resolve)(o.appDir ?? process.cwd(), path2))
|
|
35907
35835
|
},
|
|
35908
35836
|
{
|
|
35909
35837
|
repo: o.repo,
|
|
@@ -36065,7 +35993,7 @@ function registerSessionReport(program3) {
|
|
|
36065
35993
|
}
|
|
36066
35994
|
|
|
36067
35995
|
// src/stage-commands.ts
|
|
36068
|
-
var
|
|
35996
|
+
var import_node_fs33 = require("node:fs");
|
|
36069
35997
|
var import_node_path31 = require("node:path");
|
|
36070
35998
|
|
|
36071
35999
|
// src/stage-default.ts
|
|
@@ -36350,8 +36278,8 @@ function registerStageCommands(program3) {
|
|
|
36350
36278
|
return {
|
|
36351
36279
|
resolution: decideStage({
|
|
36352
36280
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
36353
|
-
hasCompose: (0,
|
|
36354
|
-
hasEnvExample: (0,
|
|
36281
|
+
hasCompose: (0, import_node_fs33.existsSync)((0, import_node_path31.join)(process.cwd(), "docker-compose.yml")),
|
|
36282
|
+
hasEnvExample: (0, import_node_fs33.existsSync)((0, import_node_path31.join)(process.cwd(), ".env.example")),
|
|
36355
36283
|
repo
|
|
36356
36284
|
}),
|
|
36357
36285
|
project: project2,
|
|
@@ -36612,14 +36540,14 @@ function registerStageCommands(program3) {
|
|
|
36612
36540
|
|
|
36613
36541
|
// src/tenant-artifact.ts
|
|
36614
36542
|
var import_node_crypto11 = require("node:crypto");
|
|
36615
|
-
var
|
|
36543
|
+
var import_node_fs34 = require("node:fs");
|
|
36616
36544
|
var import_promises6 = require("node:fs/promises");
|
|
36617
36545
|
var import_node_path32 = require("node:path");
|
|
36618
36546
|
var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
36619
36547
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
36620
36548
|
async function sha256File(path2) {
|
|
36621
36549
|
const hash = (0, import_node_crypto11.createHash)("sha256");
|
|
36622
|
-
for await (const chunk of (0,
|
|
36550
|
+
for await (const chunk of (0, import_node_fs34.createReadStream)(path2)) hash.update(chunk);
|
|
36623
36551
|
return hash.digest("hex");
|
|
36624
36552
|
}
|
|
36625
36553
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
@@ -36642,7 +36570,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
36642
36570
|
return [key, value];
|
|
36643
36571
|
}));
|
|
36644
36572
|
headers["content-length"] = String(info.size);
|
|
36645
|
-
const stream = (0,
|
|
36573
|
+
const stream = (0, import_node_fs34.createReadStream)(path2);
|
|
36646
36574
|
let uploaded;
|
|
36647
36575
|
try {
|
|
36648
36576
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -36781,8 +36709,8 @@ function renderVerifySecrets(body) {
|
|
|
36781
36709
|
}
|
|
36782
36710
|
|
|
36783
36711
|
// src/command-register-collaboration.ts
|
|
36784
|
-
var
|
|
36785
|
-
var
|
|
36712
|
+
var import_node_child_process16 = require("node:child_process");
|
|
36713
|
+
var import_node_fs41 = require("node:fs");
|
|
36786
36714
|
var import_promises7 = require("node:fs/promises");
|
|
36787
36715
|
|
|
36788
36716
|
// src/session-runtime.ts
|
|
@@ -36802,7 +36730,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
36802
36730
|
}
|
|
36803
36731
|
|
|
36804
36732
|
// src/command-register-collaboration.ts
|
|
36805
|
-
var
|
|
36733
|
+
var import_node_path39 = require("node:path");
|
|
36806
36734
|
|
|
36807
36735
|
// src/attach-to-project.ts
|
|
36808
36736
|
function boardAttachRateLimitedReceipt(resetEpochSeconds) {
|
|
@@ -37048,7 +36976,7 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
37048
36976
|
}
|
|
37049
36977
|
|
|
37050
36978
|
// src/merge-cleanup.ts
|
|
37051
|
-
var
|
|
36979
|
+
var import_node_fs36 = require("node:fs");
|
|
37052
36980
|
var import_node_path34 = require("node:path");
|
|
37053
36981
|
var import_node_os18 = require("node:os");
|
|
37054
36982
|
|
|
@@ -37155,12 +37083,12 @@ function boardAdvanceFailureMessage(result) {
|
|
|
37155
37083
|
}
|
|
37156
37084
|
|
|
37157
37085
|
// src/test-policy-core.ts
|
|
37158
|
-
var
|
|
37159
|
-
var
|
|
37086
|
+
var import_node_child_process13 = require("node:child_process");
|
|
37087
|
+
var import_node_fs35 = require("node:fs");
|
|
37160
37088
|
var import_node_path33 = require("node:path");
|
|
37161
37089
|
|
|
37162
37090
|
// src/test-command-policy-shared.mjs
|
|
37163
|
-
var
|
|
37091
|
+
var import_node_child_process12 = require("node:child_process");
|
|
37164
37092
|
var TEST_COMMAND_CLASS = "test";
|
|
37165
37093
|
var TRAILER_KEY = "Test-Policy-Override";
|
|
37166
37094
|
var OVERRIDE_RE = /^Test-Policy-Override:\s*(.+)$/im;
|
|
@@ -37277,7 +37205,7 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true, overrid
|
|
|
37277
37205
|
};
|
|
37278
37206
|
}
|
|
37279
37207
|
function git(args, cwd) {
|
|
37280
|
-
return (0,
|
|
37208
|
+
return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
37281
37209
|
}
|
|
37282
37210
|
function parseScope(value) {
|
|
37283
37211
|
const scoped = /^\[([^\]]*)\]\s*([\s\S]*)$/.exec(value);
|
|
@@ -37710,7 +37638,7 @@ function loadPolicySource(root, explicitRef, diffBase) {
|
|
|
37710
37638
|
}
|
|
37711
37639
|
function readFileOrNull2(path2) {
|
|
37712
37640
|
try {
|
|
37713
|
-
return (0,
|
|
37641
|
+
return (0, import_node_fs35.readFileSync)(path2, "utf8");
|
|
37714
37642
|
} catch {
|
|
37715
37643
|
return null;
|
|
37716
37644
|
}
|
|
@@ -37738,10 +37666,10 @@ function classify(changed, policy, present = () => false) {
|
|
|
37738
37666
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
37739
37667
|
return { mandatoryHits, untestedHits, testChanges, meaningfulTestChanges, addedTests, removedProtected };
|
|
37740
37668
|
}
|
|
37741
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
37669
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs35.existsSync)(path2)) {
|
|
37742
37670
|
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path33.join)(root, p)));
|
|
37743
37671
|
}
|
|
37744
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
37672
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs35.existsSync)(path2)) {
|
|
37745
37673
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
37746
37674
|
return [...new Set(declared)].filter((p) => !exists((0, import_node_path33.join)(root, p)));
|
|
37747
37675
|
}
|
|
@@ -37776,14 +37704,14 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
37776
37704
|
return findings;
|
|
37777
37705
|
}
|
|
37778
37706
|
function git2(args, cwd) {
|
|
37779
|
-
return (0,
|
|
37707
|
+
return (0, import_node_child_process13.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
37780
37708
|
}
|
|
37781
37709
|
var COAUTHOR_KEY = "Co-authored-by";
|
|
37782
37710
|
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
37783
37711
|
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
37784
37712
|
function parseTrailers(message2, cwd) {
|
|
37785
37713
|
try {
|
|
37786
|
-
return (0,
|
|
37714
|
+
return (0, import_node_child_process13.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
37787
37715
|
windowsHide: true,
|
|
37788
37716
|
cwd,
|
|
37789
37717
|
input: message2,
|
|
@@ -37882,7 +37810,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
37882
37810
|
}
|
|
37883
37811
|
const loaded = deps.policy ? { policy: deps.policy, source: { ref: null, sha: null, path: POLICY_FILE } } : loadPolicySource(root, deps.policyRef, deps.base);
|
|
37884
37812
|
const { policy, source: policySource } = loaded;
|
|
37885
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
37813
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs35.existsSync)(path2));
|
|
37886
37814
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
37887
37815
|
const base = deps.changed ? "(injected)" : deps.policyRef ? resolveHotfixBase(root) : resolveBase(root, deps.base);
|
|
37888
37816
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
@@ -38118,13 +38046,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
38118
38046
|
return rewritten === base ? null : rewritten;
|
|
38119
38047
|
}
|
|
38120
38048
|
function writeSquashBodyFile(body) {
|
|
38121
|
-
const dir = (0,
|
|
38049
|
+
const dir = (0, import_node_fs36.mkdtempSync)((0, import_node_path34.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
|
|
38122
38050
|
const path2 = (0, import_node_path34.join)(dir, "body.txt");
|
|
38123
|
-
(0,
|
|
38051
|
+
(0, import_node_fs36.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
38124
38052
|
`, "utf8");
|
|
38125
38053
|
return { path: path2, cleanup: () => {
|
|
38126
38054
|
try {
|
|
38127
|
-
(0,
|
|
38055
|
+
(0, import_node_fs36.rmSync)(dir, { recursive: true, force: true });
|
|
38128
38056
|
} catch {
|
|
38129
38057
|
}
|
|
38130
38058
|
} };
|
|
@@ -38331,7 +38259,7 @@ async function deleteMergedRemoteBranch(options) {
|
|
|
38331
38259
|
}
|
|
38332
38260
|
|
|
38333
38261
|
// src/post-merge-recon.ts
|
|
38334
|
-
var
|
|
38262
|
+
var import_node_fs37 = require("node:fs");
|
|
38335
38263
|
var import_node_path35 = require("node:path");
|
|
38336
38264
|
|
|
38337
38265
|
// src/cross-repo-filing-issue.ts
|
|
@@ -38498,16 +38426,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
38498
38426
|
}
|
|
38499
38427
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
38500
38428
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
38501
|
-
(0,
|
|
38502
|
-
(0,
|
|
38429
|
+
(0, import_node_fs37.mkdirSync)((0, import_node_path35.dirname)(path2), { recursive: true });
|
|
38430
|
+
(0, import_node_fs37.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
38503
38431
|
`, "utf8");
|
|
38504
38432
|
return path2;
|
|
38505
38433
|
}
|
|
38506
38434
|
function clearPostMergeReconRecovery(cwd, repo, pr) {
|
|
38507
38435
|
const path2 = postMergeReconStatePath(cwd, repo, pr);
|
|
38508
|
-
if (!(0,
|
|
38436
|
+
if (!(0, import_node_fs37.existsSync)(path2)) return;
|
|
38509
38437
|
try {
|
|
38510
|
-
(0,
|
|
38438
|
+
(0, import_node_fs37.unlinkSync)(path2);
|
|
38511
38439
|
} catch {
|
|
38512
38440
|
}
|
|
38513
38441
|
}
|
|
@@ -38538,8 +38466,8 @@ function postMergeReconWarnings(input) {
|
|
|
38538
38466
|
}
|
|
38539
38467
|
|
|
38540
38468
|
// src/review-verdict.ts
|
|
38541
|
-
var
|
|
38542
|
-
var
|
|
38469
|
+
var import_node_child_process14 = require("node:child_process");
|
|
38470
|
+
var import_node_fs38 = require("node:fs");
|
|
38543
38471
|
var import_node_os19 = require("node:os");
|
|
38544
38472
|
var import_node_path36 = require("node:path");
|
|
38545
38473
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
@@ -38637,8 +38565,8 @@ async function checkPrReview(number, repo, head, deps = {
|
|
|
38637
38565
|
}
|
|
38638
38566
|
function computePrPatchId(number, repo) {
|
|
38639
38567
|
return new Promise((resolve7, reject) => {
|
|
38640
|
-
const gh = (0,
|
|
38641
|
-
const git3 = (0,
|
|
38568
|
+
const gh = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
38569
|
+
const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
38642
38570
|
let out = "";
|
|
38643
38571
|
let ghErr = "";
|
|
38644
38572
|
let gitErr = "";
|
|
@@ -38705,29 +38633,29 @@ async function readPrIssueComments(number, repo) {
|
|
|
38705
38633
|
return comments;
|
|
38706
38634
|
}
|
|
38707
38635
|
async function postPrCommentFromFile(number, repo, body) {
|
|
38708
|
-
const dir = (0,
|
|
38636
|
+
const dir = (0, import_node_fs38.mkdtempSync)((0, import_node_path36.join)((0, import_node_os19.tmpdir)(), "mmi-review-verdict-"));
|
|
38709
38637
|
const path2 = (0, import_node_path36.join)(dir, "body.md");
|
|
38710
38638
|
try {
|
|
38711
|
-
(0,
|
|
38639
|
+
(0, import_node_fs38.writeFileSync)(path2, body, "utf8");
|
|
38712
38640
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
38713
38641
|
return stdout.trim();
|
|
38714
38642
|
} finally {
|
|
38715
38643
|
try {
|
|
38716
|
-
(0,
|
|
38644
|
+
(0, import_node_fs38.rmSync)(dir, { recursive: true, force: true });
|
|
38717
38645
|
} catch {
|
|
38718
38646
|
}
|
|
38719
38647
|
}
|
|
38720
38648
|
}
|
|
38721
38649
|
|
|
38722
38650
|
// src/pr-create-docs-check.ts
|
|
38723
|
-
var
|
|
38651
|
+
var import_node_child_process15 = require("node:child_process");
|
|
38724
38652
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
38725
38653
|
function catFileBatch(root, ref, paths) {
|
|
38726
38654
|
if (paths.length === 0) return Promise.resolve([]);
|
|
38727
38655
|
return new Promise((resolve7) => {
|
|
38728
38656
|
const chunks = [];
|
|
38729
38657
|
let settled = false;
|
|
38730
|
-
const child2 = (0,
|
|
38658
|
+
const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
38731
38659
|
const finish = () => {
|
|
38732
38660
|
if (settled) return;
|
|
38733
38661
|
settled = true;
|
|
@@ -38897,69 +38825,12 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
|
|
|
38897
38825
|
}
|
|
38898
38826
|
|
|
38899
38827
|
// src/worktree-merge-cleanup.ts
|
|
38900
|
-
var import_node_fs42 = require("node:fs");
|
|
38901
|
-
var import_node_path39 = require("node:path");
|
|
38902
|
-
|
|
38903
|
-
// src/jervcode-node-modules-cleanup.ts
|
|
38904
38828
|
var import_node_fs40 = require("node:fs");
|
|
38905
|
-
var
|
|
38906
|
-
var import_node_path37 = require("node:path");
|
|
38907
|
-
var JERVCODE_PACKAGE_ENTRY = (0, import_node_path37.join)("node_modules", "@jervaise", "jervcode", "dist", "launcher-entry.js");
|
|
38908
|
-
var WIN_NAMES2 = ["jervcode.cmd", "jervcode"];
|
|
38909
|
-
var POSIX_NAMES2 = ["jervcode"];
|
|
38910
|
-
var NODE_MODULES_CLEANUP_TIMEOUT_MS = 3e5;
|
|
38911
|
-
function jervcodeCandidatePaths(env = process.env, home = (0, import_node_os20.homedir)(), platform2 = process.platform) {
|
|
38912
|
-
const names = platform2 === "win32" ? WIN_NAMES2 : POSIX_NAMES2;
|
|
38913
|
-
const out = [];
|
|
38914
|
-
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
38915
|
-
for (const name of names) out.push((0, import_node_path37.join)(dir, name));
|
|
38916
|
-
}
|
|
38917
|
-
return out;
|
|
38918
|
-
}
|
|
38919
|
-
function resolveJervcodePath(env = process.env, home = (0, import_node_os20.homedir)(), platform2 = process.platform, exists = import_node_fs40.existsSync) {
|
|
38920
|
-
for (const candidate of jervcodeCandidatePaths(env, home, platform2)) {
|
|
38921
|
-
if (exists(candidate)) return candidate;
|
|
38922
|
-
}
|
|
38923
|
-
return void 0;
|
|
38924
|
-
}
|
|
38925
|
-
function jervcodeExecFileArgs(args, opts = {}) {
|
|
38926
|
-
const platform2 = opts.platform ?? process.platform;
|
|
38927
|
-
const exists = opts.exists ?? import_node_fs40.existsSync;
|
|
38928
|
-
const resolved = resolveJervcodePath(opts.env ?? process.env, opts.home ?? (0, import_node_os20.homedir)(), platform2, exists);
|
|
38929
|
-
if (resolved) {
|
|
38930
|
-
const entry = (0, import_node_path37.join)((0, import_node_path37.join)(resolved, ".."), JERVCODE_PACKAGE_ENTRY);
|
|
38931
|
-
if (exists(entry)) {
|
|
38932
|
-
return { file: opts.execPath ?? process.execPath, args: [entry, ...args], via: "node-entry" };
|
|
38933
|
-
}
|
|
38934
|
-
}
|
|
38935
|
-
const bin = resolved ?? "jervcode";
|
|
38936
|
-
if (platform2 === "win32") {
|
|
38937
|
-
return { file: "cmd.exe", args: ["/c", bin, ...args], via: resolved ? "cmd-shim" : "bare" };
|
|
38938
|
-
}
|
|
38939
|
-
return { file: bin, args: [...args], via: resolved ? "posix" : "bare" };
|
|
38940
|
-
}
|
|
38941
|
-
async function removeWorktreeNodeModulesViaHelper(wtPath, opts = {}) {
|
|
38942
|
-
const { cwd, timeoutMs = NODE_MODULES_CLEANUP_TIMEOUT_MS } = opts;
|
|
38943
|
-
const env = opts.env ?? process.env;
|
|
38944
|
-
const exec = opts.exec ?? execFileP;
|
|
38945
|
-
const candidates = jervcodeCandidatePaths(env, opts.home, opts.platform ?? process.platform);
|
|
38946
|
-
const plan = jervcodeExecFileArgs(["worktree-node-modules-cleanup", "--worktree", wtPath], opts);
|
|
38947
|
-
const execOptions = { timeout: timeoutMs, ...cwd ? { cwd } : {} };
|
|
38948
|
-
try {
|
|
38949
|
-
await exec(plan.file, plan.args, execOptions);
|
|
38950
|
-
return { ok: true };
|
|
38951
|
-
} catch (e) {
|
|
38952
|
-
const err = e;
|
|
38953
|
-
const base = formatJervCliSpawnFailure(err, plan, candidates);
|
|
38954
|
-
const stderr = typeof err.stderr === "string" ? err.stderr.trim() : "";
|
|
38955
|
-
const detail = stderr.split("\n")[0] || base;
|
|
38956
|
-
return { ok: false, error: `jervcode worktree-node-modules-cleanup failed: ${detail}` };
|
|
38957
|
-
}
|
|
38958
|
-
}
|
|
38829
|
+
var import_node_path38 = require("node:path");
|
|
38959
38830
|
|
|
38960
38831
|
// src/worktree-evidence-archive.ts
|
|
38961
|
-
var
|
|
38962
|
-
var
|
|
38832
|
+
var import_node_fs39 = require("node:fs");
|
|
38833
|
+
var import_node_path37 = require("node:path");
|
|
38963
38834
|
var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
|
|
38964
38835
|
function sanitizeArchiveSegment(value, max = 80) {
|
|
38965
38836
|
const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -38974,24 +38845,24 @@ function resolveArtifactRunScope(env = process.env) {
|
|
|
38974
38845
|
}
|
|
38975
38846
|
function defaultIsDirectory(path2) {
|
|
38976
38847
|
try {
|
|
38977
|
-
return (0,
|
|
38848
|
+
return (0, import_node_fs39.lstatSync)(path2).isDirectory();
|
|
38978
38849
|
} catch {
|
|
38979
38850
|
return false;
|
|
38980
38851
|
}
|
|
38981
38852
|
}
|
|
38982
38853
|
function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
38983
|
-
const exists = deps.exists ??
|
|
38854
|
+
const exists = deps.exists ?? import_node_fs39.existsSync;
|
|
38984
38855
|
const isDirectory = deps.isDirectory ?? defaultIsDirectory;
|
|
38985
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
38856
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs39.cpSync)(from, to, { recursive: true, force: true }));
|
|
38986
38857
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
38987
|
-
(0,
|
|
38858
|
+
(0, import_node_fs39.mkdirSync)(path2, { recursive: true });
|
|
38988
38859
|
});
|
|
38989
38860
|
const env = deps.env ?? process.env;
|
|
38990
38861
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
38991
38862
|
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
38992
38863
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
38993
38864
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
38994
|
-
const source = (0,
|
|
38865
|
+
const source = (0, import_node_path37.join)(args.worktreePath, ".jerv");
|
|
38995
38866
|
if (!exists(source)) return { status: "absent" };
|
|
38996
38867
|
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
38997
38868
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -38999,7 +38870,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
38999
38870
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
39000
38871
|
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
39001
38872
|
try {
|
|
39002
|
-
mkdirp((0,
|
|
38873
|
+
mkdirp((0, import_node_path37.dirname)(dest));
|
|
39003
38874
|
copyDir(source, dest);
|
|
39004
38875
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
39005
38876
|
return { status: "archived", path: dest, runScope };
|
|
@@ -39008,14 +38879,14 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
39008
38879
|
}
|
|
39009
38880
|
}
|
|
39010
38881
|
function defaultStat(path2) {
|
|
39011
|
-
const st = (0,
|
|
38882
|
+
const st = (0, import_node_fs39.statSync)(path2);
|
|
39012
38883
|
return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
|
|
39013
38884
|
}
|
|
39014
38885
|
function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
39015
|
-
const exists = deps.exists ??
|
|
38886
|
+
const exists = deps.exists ?? import_node_fs39.existsSync;
|
|
39016
38887
|
const stat4 = deps.stat ?? defaultStat;
|
|
39017
|
-
const readdir2 = deps.readdir ??
|
|
39018
|
-
const tmpRoot = (0,
|
|
38888
|
+
const readdir2 = deps.readdir ?? import_node_fs39.readdirSync;
|
|
38889
|
+
const tmpRoot = (0, import_node_path37.join)(worktreePath, "tmp");
|
|
39019
38890
|
if (!exists(tmpRoot)) return [];
|
|
39020
38891
|
const entries = [];
|
|
39021
38892
|
const walk2 = (dir) => {
|
|
@@ -39026,14 +38897,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
39026
38897
|
return;
|
|
39027
38898
|
}
|
|
39028
38899
|
for (const name of names) {
|
|
39029
|
-
const full = (0,
|
|
38900
|
+
const full = (0, import_node_path37.join)(dir, name);
|
|
39030
38901
|
let st;
|
|
39031
38902
|
try {
|
|
39032
38903
|
st = stat4(full);
|
|
39033
38904
|
} catch {
|
|
39034
38905
|
continue;
|
|
39035
38906
|
}
|
|
39036
|
-
const relPath = (0,
|
|
38907
|
+
const relPath = (0, import_node_path37.relative)(worktreePath, full).replace(/\\/g, "/");
|
|
39037
38908
|
if (st.isDirectory()) {
|
|
39038
38909
|
if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
|
|
39039
38910
|
walk2(full);
|
|
@@ -39047,10 +38918,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
|
|
|
39047
38918
|
return entries;
|
|
39048
38919
|
}
|
|
39049
38920
|
function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
39050
|
-
const exists = deps.exists ??
|
|
39051
|
-
const copyDir = deps.copyDir ?? ((from, to) => (0,
|
|
38921
|
+
const exists = deps.exists ?? import_node_fs39.existsSync;
|
|
38922
|
+
const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs39.cpSync)(from, to, { recursive: true, force: true }));
|
|
39052
38923
|
const mkdirp = deps.mkdirp ?? ((path2) => {
|
|
39053
|
-
(0,
|
|
38924
|
+
(0, import_node_fs39.mkdirSync)(path2, { recursive: true });
|
|
39054
38925
|
});
|
|
39055
38926
|
const env = deps.env ?? process.env;
|
|
39056
38927
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
@@ -39058,7 +38929,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
39058
38929
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
39059
38930
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
39060
38931
|
const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
|
|
39061
|
-
const source = (0,
|
|
38932
|
+
const source = (0, import_node_path37.join)(args.worktreePath, "tmp");
|
|
39062
38933
|
if (!scanned.length) return { status: "absent" };
|
|
39063
38934
|
if (!exists(source)) return { status: "absent" };
|
|
39064
38935
|
const runScope = resolveArtifactRunScope(env);
|
|
@@ -39066,7 +38937,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
|
|
|
39066
38937
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
39067
38938
|
const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
|
|
39068
38939
|
try {
|
|
39069
|
-
mkdirp((0,
|
|
38940
|
+
mkdirp((0, import_node_path37.dirname)(dest));
|
|
39070
38941
|
copyDir(source, dest);
|
|
39071
38942
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
39072
38943
|
const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
|
|
@@ -39135,9 +39006,9 @@ function normPath2(p) {
|
|
|
39135
39006
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
39136
39007
|
}
|
|
39137
39008
|
function unlinkNodeModulesJunction(wtPath) {
|
|
39138
|
-
const nm = (0,
|
|
39009
|
+
const nm = (0, import_node_path38.join)(wtPath, "node_modules");
|
|
39139
39010
|
try {
|
|
39140
|
-
if ((0,
|
|
39011
|
+
if ((0, import_node_fs40.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs40.rmdirSync)(nm);
|
|
39141
39012
|
return { ok: true };
|
|
39142
39013
|
} catch (e) {
|
|
39143
39014
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -39148,8 +39019,8 @@ function defaultSleep2(ms) {
|
|
|
39148
39019
|
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
39149
39020
|
}
|
|
39150
39021
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
39151
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
39152
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
39022
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs40.rmdirSync, exists = import_node_fs40.existsSync } = options;
|
|
39023
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs40.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
39153
39024
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
39154
39025
|
if (!junction.ok) return junction;
|
|
39155
39026
|
try {
|
|
@@ -39195,7 +39066,7 @@ function isWindowsCwdLockResidueError(error) {
|
|
|
39195
39066
|
}
|
|
39196
39067
|
function deferredCwdLockRemediation(wtPath) {
|
|
39197
39068
|
const quote = (path2) => path2.replace(/'/g, "''");
|
|
39198
|
-
return `After
|
|
39069
|
+
return `After leaving the directory and closing its handles, remove it only if empty: [System.IO.Directory]::Delete('${quote(wtPath)}', $false)`;
|
|
39199
39070
|
}
|
|
39200
39071
|
function primaryCheckoutBranchRemediation(primaryRoot, baseRef, branch) {
|
|
39201
39072
|
const quote = (value) => value.replace(/'/g, "''");
|
|
@@ -39272,31 +39143,40 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
39272
39143
|
}
|
|
39273
39144
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
39274
39145
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
39275
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
39146
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path38.join)(wtPath, line.slice(0, -1)));
|
|
39276
39147
|
}
|
|
39277
39148
|
function safeRemoveTree(path2) {
|
|
39278
|
-
const stat4 = (0,
|
|
39149
|
+
const stat4 = (0, import_node_fs40.lstatSync)(path2);
|
|
39279
39150
|
if (stat4.isSymbolicLink()) {
|
|
39280
39151
|
try {
|
|
39281
|
-
(0,
|
|
39152
|
+
(0, import_node_fs40.rmdirSync)(path2);
|
|
39282
39153
|
} catch {
|
|
39283
|
-
(0,
|
|
39154
|
+
(0, import_node_fs40.unlinkSync)(path2);
|
|
39284
39155
|
}
|
|
39285
39156
|
return;
|
|
39286
39157
|
}
|
|
39287
39158
|
if (stat4.isDirectory()) {
|
|
39288
|
-
for (const entry of (0,
|
|
39289
|
-
(0,
|
|
39159
|
+
for (const entry of (0, import_node_fs40.readdirSync)(path2)) safeRemoveTree((0, import_node_path38.join)(path2, entry));
|
|
39160
|
+
(0, import_node_fs40.rmdirSync)(path2);
|
|
39290
39161
|
return;
|
|
39291
39162
|
}
|
|
39292
|
-
(0,
|
|
39163
|
+
(0, import_node_fs40.unlinkSync)(path2);
|
|
39164
|
+
}
|
|
39165
|
+
async function removeWorktreeNodeModules(wtPath) {
|
|
39166
|
+
try {
|
|
39167
|
+
safeRemoveTree((0, import_node_path38.join)(wtPath, "node_modules"));
|
|
39168
|
+
return { ok: true };
|
|
39169
|
+
} catch (e) {
|
|
39170
|
+
if (e.code === "ENOENT") return { ok: true };
|
|
39171
|
+
return { ok: false, error: errorMessage(e) };
|
|
39172
|
+
}
|
|
39293
39173
|
}
|
|
39294
39174
|
function errorMessage(e) {
|
|
39295
39175
|
return e instanceof Error ? e.message : String(e);
|
|
39296
39176
|
}
|
|
39297
39177
|
function resolvedOrRaw(path2) {
|
|
39298
39178
|
try {
|
|
39299
|
-
return normPath2((0,
|
|
39179
|
+
return normPath2((0, import_node_fs40.realpathSync)(path2));
|
|
39300
39180
|
} catch {
|
|
39301
39181
|
return normPath2(path2);
|
|
39302
39182
|
}
|
|
@@ -39304,10 +39184,10 @@ function resolvedOrRaw(path2) {
|
|
|
39304
39184
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
39305
39185
|
let realRoot;
|
|
39306
39186
|
try {
|
|
39307
|
-
if ((0,
|
|
39187
|
+
if ((0, import_node_fs40.lstatSync)(root).isSymbolicLink()) {
|
|
39308
39188
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
39309
39189
|
}
|
|
39310
|
-
realRoot = normPath2((0,
|
|
39190
|
+
realRoot = normPath2((0, import_node_fs40.realpathSync)(root));
|
|
39311
39191
|
} catch (e) {
|
|
39312
39192
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
39313
39193
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -39323,16 +39203,16 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
39323
39203
|
const dir = stack.pop();
|
|
39324
39204
|
let entries;
|
|
39325
39205
|
try {
|
|
39326
|
-
entries = (0,
|
|
39206
|
+
entries = (0, import_node_fs40.readdirSync)(dir, { withFileTypes: true });
|
|
39327
39207
|
} catch (e) {
|
|
39328
39208
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
39329
39209
|
}
|
|
39330
39210
|
for (const entry of entries) {
|
|
39331
|
-
const child2 = (0,
|
|
39211
|
+
const child2 = (0, import_node_path38.join)(dir, entry.name);
|
|
39332
39212
|
if (entry.isSymbolicLink()) {
|
|
39333
39213
|
let target = "";
|
|
39334
39214
|
try {
|
|
39335
|
-
target = normPath2((0,
|
|
39215
|
+
target = normPath2((0, import_node_fs40.realpathSync)(child2));
|
|
39336
39216
|
} catch {
|
|
39337
39217
|
target = "";
|
|
39338
39218
|
}
|
|
@@ -39361,8 +39241,7 @@ async function describePreCleanFailure(wtPath, execGit, error) {
|
|
|
39361
39241
|
const shown = remaining.slice(0, 10).join(", ");
|
|
39362
39242
|
const more = remaining.length > 10 ? ` (+${remaining.length - 10} more)` : "";
|
|
39363
39243
|
const quote = (path2) => path2.replace(/'/g, "''");
|
|
39364
|
-
const
|
|
39365
|
-
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;
|
|
39244
|
+
const remediation = remaining.some((path2) => path2.endsWith("node_modules/")) ? `Close handles to '${quote(wtPath)}' and retry MMI PR landing from the primary checkout` : void 0;
|
|
39366
39245
|
return { ok: false, error: `${error}; remaining ignored paths: ${shown}${more}`, ...remediation ? { remediation } : {} };
|
|
39367
39246
|
}
|
|
39368
39247
|
function formatWorktreeRemovalFailureDetail(options) {
|
|
@@ -39516,7 +39395,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
39516
39395
|
return report;
|
|
39517
39396
|
}
|
|
39518
39397
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
39519
|
-
const pathExists = options.pathExists ??
|
|
39398
|
+
const pathExists = options.pathExists ?? import_node_fs40.existsSync;
|
|
39520
39399
|
let afterWorktrees = [];
|
|
39521
39400
|
try {
|
|
39522
39401
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -39656,8 +39535,8 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
39656
39535
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
39657
39536
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
39658
39537
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
39659
|
-
if (pathExists((0,
|
|
39660
|
-
const nmRemoved = await (options.removeRealNodeModules ??
|
|
39538
|
+
if (pathExists((0, import_node_path38.join)(wtPath, "node_modules"))) {
|
|
39539
|
+
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
39661
39540
|
if (!nmRemoved.ok) {
|
|
39662
39541
|
report.worktree = {
|
|
39663
39542
|
path: wtPath,
|
|
@@ -39668,7 +39547,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
39668
39547
|
stageTeardown,
|
|
39669
39548
|
tmpEvidenceCount: tmpEvidence.length,
|
|
39670
39549
|
...unlinkedReparsePoints.length ? { unlinkedReparsePoints } : {},
|
|
39671
|
-
remediation: `
|
|
39550
|
+
remediation: `Close handles to '${wtPath.replace(/'/g, "''")}' and retry MMI PR landing from the primary checkout`
|
|
39672
39551
|
};
|
|
39673
39552
|
report.localBranch = { name: branch, status: "not-attempted", reason: "worktree-pre-clean-failed" };
|
|
39674
39553
|
return report;
|
|
@@ -39823,10 +39702,10 @@ function argvWantsJson2() {
|
|
|
39823
39702
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
39824
39703
|
}
|
|
39825
39704
|
function hubRoot() {
|
|
39826
|
-
const fromPkg = (0,
|
|
39705
|
+
const fromPkg = (0, import_node_path39.join)(__dirname, "..", "..");
|
|
39827
39706
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
39828
|
-
if ((0,
|
|
39829
|
-
if ((0,
|
|
39707
|
+
if ((0, import_node_fs41.existsSync)((0, import_node_path39.join)(fromPkg, marker))) return fromPkg;
|
|
39708
|
+
if ((0, import_node_fs41.existsSync)((0, import_node_path39.join)(process.cwd(), marker))) return process.cwd();
|
|
39830
39709
|
return null;
|
|
39831
39710
|
}
|
|
39832
39711
|
function ciAuditDeps() {
|
|
@@ -39838,8 +39717,8 @@ function ciAuditDeps() {
|
|
|
39838
39717
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
39839
39718
|
readSeedFile: (path2) => {
|
|
39840
39719
|
if (!root) return null;
|
|
39841
|
-
const fullPath = (0,
|
|
39842
|
-
return (0,
|
|
39720
|
+
const fullPath = (0, import_node_path39.join)(root, path2);
|
|
39721
|
+
return (0, import_node_fs41.existsSync)(fullPath) ? (0, import_node_fs41.readFileSync)(fullPath, "utf8") : null;
|
|
39843
39722
|
}
|
|
39844
39723
|
};
|
|
39845
39724
|
}
|
|
@@ -39921,7 +39800,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
39921
39800
|
try {
|
|
39922
39801
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
39923
39802
|
if (o.repo) args.push("--repo", o.repo);
|
|
39924
|
-
spawnDetachedSelf(args, { spawn:
|
|
39803
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
39925
39804
|
} catch {
|
|
39926
39805
|
}
|
|
39927
39806
|
}
|
|
@@ -40131,7 +40010,6 @@ function registerCollaborationCommands(program3) {
|
|
|
40131
40010
|
}
|
|
40132
40011
|
}
|
|
40133
40012
|
if (o.related !== false) scheduleRelatedDiscovery({ repo: o.repo, number: created.number, title, body });
|
|
40134
|
-
invalidateStatuslineBoardCache();
|
|
40135
40013
|
console.log(JSON.stringify({
|
|
40136
40014
|
...created,
|
|
40137
40015
|
label: issueType,
|
|
@@ -40374,7 +40252,6 @@ ${list}`);
|
|
|
40374
40252
|
}
|
|
40375
40253
|
}
|
|
40376
40254
|
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) => {
|
|
40377
|
-
warnSkillLessonDeprecated();
|
|
40378
40255
|
const targetRepo2 = o.repo ?? HUB_REPO;
|
|
40379
40256
|
const sourceRepo = await resolveRepo(void 0);
|
|
40380
40257
|
const pluginSha = await resolvePluginSha();
|
|
@@ -40475,7 +40352,6 @@ ${list}`);
|
|
|
40475
40352
|
process.exitCode = 1;
|
|
40476
40353
|
return;
|
|
40477
40354
|
}
|
|
40478
|
-
invalidateStatuslineBoardCache();
|
|
40479
40355
|
console.log(JSON.stringify(created));
|
|
40480
40356
|
}), [
|
|
40481
40357
|
'mmi-cli devops pr create --title "Add the schema" --body "Closes #2680"',
|
|
@@ -40513,11 +40389,11 @@ ${list}`);
|
|
|
40513
40389
|
}
|
|
40514
40390
|
});
|
|
40515
40391
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
40516
|
-
const wfDir = (0,
|
|
40517
|
-
if (!(0,
|
|
40518
|
-
return (0,
|
|
40392
|
+
const wfDir = (0, import_node_path39.join)(cwd, ".github", "workflows");
|
|
40393
|
+
if (!(0, import_node_fs41.existsSync)(wfDir)) return [];
|
|
40394
|
+
return (0, import_node_fs41.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
40519
40395
|
try {
|
|
40520
|
-
return workflowReportsPrChecks((0,
|
|
40396
|
+
return workflowReportsPrChecks((0, import_node_fs41.readFileSync)((0, import_node_path39.join)(wfDir, name), "utf8"));
|
|
40521
40397
|
} catch {
|
|
40522
40398
|
return true;
|
|
40523
40399
|
}
|
|
@@ -40713,7 +40589,7 @@ ${list}`);
|
|
|
40713
40589
|
jsonParity(pr.command("review-verdict <number>").description("post an explicit review verdict comment: computes the PR diff patch-id and head sha, renders the v1 contract body, and posts it as a PR comment").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) => {
|
|
40714
40590
|
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
40715
40591
|
const verdict = o.verdict;
|
|
40716
|
-
const findings = o.findingsFile ? (0,
|
|
40592
|
+
const findings = o.findingsFile ? (0, import_node_fs41.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
40717
40593
|
let patch;
|
|
40718
40594
|
let head;
|
|
40719
40595
|
try {
|
|
@@ -40907,7 +40783,6 @@ ${list}`);
|
|
|
40907
40783
|
...crossRepoFilingIssue.status !== "not-applicable" ? { crossRepoFilingIssue } : {},
|
|
40908
40784
|
...recovery ? { postMergeReconRecovery: recovery } : {}
|
|
40909
40785
|
});
|
|
40910
|
-
invalidateStatuslineBoardCache();
|
|
40911
40786
|
for (const line of postMergeReconWarnings({
|
|
40912
40787
|
context: "pr land",
|
|
40913
40788
|
boardAdvance,
|
|
@@ -40961,7 +40836,7 @@ ${list}`);
|
|
|
40961
40836
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
40962
40837
|
closingGuardInput,
|
|
40963
40838
|
method === "--squash",
|
|
40964
|
-
o.squashBodyFile ? (0,
|
|
40839
|
+
o.squashBodyFile ? (0, import_node_fs41.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
40965
40840
|
);
|
|
40966
40841
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
40967
40842
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -40993,7 +40868,7 @@ ${list}`);
|
|
|
40993
40868
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
40994
40869
|
let foreignCheckout;
|
|
40995
40870
|
if (foreignCwd) {
|
|
40996
|
-
const sibling = (0,
|
|
40871
|
+
const sibling = (0, import_node_path39.join)((0, import_node_path39.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
40997
40872
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
40998
40873
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
40999
40874
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -41091,7 +40966,7 @@ ${list}`);
|
|
|
41091
40966
|
}
|
|
41092
40967
|
if (!repoForPostCleanup) throw e;
|
|
41093
40968
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
41094
|
-
const commitMessage = bodyFile ? (0,
|
|
40969
|
+
const commitMessage = bodyFile ? (0, import_node_fs41.readFileSync)(bodyFile, "utf8") : void 0;
|
|
41095
40970
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
41096
40971
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
41097
40972
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -41211,7 +41086,7 @@ ${list}`);
|
|
|
41211
41086
|
preserveWorktree: o.preserveWorktree,
|
|
41212
41087
|
gcAcknowledged: o.gc,
|
|
41213
41088
|
expectedHeadOid: headRefOid,
|
|
41214
|
-
pathExists: (p) => (0,
|
|
41089
|
+
pathExists: (p) => (0, import_node_fs41.existsSync)(p),
|
|
41215
41090
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
41216
41091
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
41217
41092
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -41276,7 +41151,6 @@ ${list}`);
|
|
|
41276
41151
|
} else if (repoForPostCleanup) {
|
|
41277
41152
|
clearPostMergeReconRecovery(process.cwd(), repoForPostCleanup, number);
|
|
41278
41153
|
}
|
|
41279
|
-
invalidateStatuslineBoardCache();
|
|
41280
41154
|
const devDeploy = devDeployPlan.applicable && devDeployDeps && remoteNotAttemptedReason !== "pr-already-merged" ? await dispatchDevDeploy(repoForPostCleanup, devDeployDeps) : void 0;
|
|
41281
41155
|
const devDeployManual = devDeployPlan.applicable ? devDeployPlan.manualPointer : void 0;
|
|
41282
41156
|
let observedMethod;
|
|
@@ -41333,7 +41207,7 @@ ${list}`);
|
|
|
41333
41207
|
}
|
|
41334
41208
|
|
|
41335
41209
|
// src/command-register-developer.ts
|
|
41336
|
-
var
|
|
41210
|
+
var import_node_fs46 = require("node:fs");
|
|
41337
41211
|
|
|
41338
41212
|
// src/whoami.ts
|
|
41339
41213
|
async function resolveWhoami(deps) {
|
|
@@ -41363,7 +41237,7 @@ async function resolveWhoami(deps) {
|
|
|
41363
41237
|
}
|
|
41364
41238
|
|
|
41365
41239
|
// src/command-register-developer.ts
|
|
41366
|
-
var
|
|
41240
|
+
var import_node_path44 = require("node:path");
|
|
41367
41241
|
|
|
41368
41242
|
// src/wave-land.ts
|
|
41369
41243
|
function planWaveLand(prs) {
|
|
@@ -41394,7 +41268,7 @@ async function executeWaveLand(plan, deps) {
|
|
|
41394
41268
|
}
|
|
41395
41269
|
|
|
41396
41270
|
// src/box-commands.ts
|
|
41397
|
-
var
|
|
41271
|
+
var import_node_fs42 = require("node:fs");
|
|
41398
41272
|
|
|
41399
41273
|
// src/box.ts
|
|
41400
41274
|
var BOX_KEYS = {
|
|
@@ -41596,7 +41470,7 @@ function registerBoxCommands(program3) {
|
|
|
41596
41470
|
return;
|
|
41597
41471
|
}
|
|
41598
41472
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
41599
|
-
if (wroteScript) (0,
|
|
41473
|
+
if (wroteScript) (0, import_node_fs42.writeFileSync)(wroteScript, sshRecipeScript(found), "utf8");
|
|
41600
41474
|
if (o.json) {
|
|
41601
41475
|
console.log(JSON.stringify({
|
|
41602
41476
|
box: found,
|
|
@@ -41616,21 +41490,21 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
41616
41490
|
}
|
|
41617
41491
|
|
|
41618
41492
|
// src/dist-drift.ts
|
|
41619
|
-
var
|
|
41493
|
+
var import_node_child_process17 = require("node:child_process");
|
|
41620
41494
|
var import_node_crypto13 = require("node:crypto");
|
|
41621
|
-
var
|
|
41622
|
-
var
|
|
41623
|
-
var
|
|
41495
|
+
var import_node_fs44 = require("node:fs");
|
|
41496
|
+
var import_node_os20 = require("node:os");
|
|
41497
|
+
var import_node_path41 = require("node:path");
|
|
41624
41498
|
|
|
41625
41499
|
// ../scripts/distribution-digest.mjs
|
|
41626
41500
|
var import_node_crypto12 = require("node:crypto");
|
|
41627
|
-
var
|
|
41628
|
-
var
|
|
41501
|
+
var import_node_fs43 = require("node:fs");
|
|
41502
|
+
var import_node_path40 = require("node:path");
|
|
41629
41503
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
41630
41504
|
function repoPath(root, declaredPath, label) {
|
|
41631
|
-
const absoluteRoot = (0,
|
|
41632
|
-
const target = (0,
|
|
41633
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
41505
|
+
const absoluteRoot = (0, import_node_path40.resolve)(root);
|
|
41506
|
+
const target = (0, import_node_path40.resolve)(root, declaredPath);
|
|
41507
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path40.sep}`)) {
|
|
41634
41508
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
41635
41509
|
}
|
|
41636
41510
|
return target;
|
|
@@ -41638,7 +41512,7 @@ function repoPath(root, declaredPath, label) {
|
|
|
41638
41512
|
function digestFiles(files) {
|
|
41639
41513
|
const hash = (0, import_node_crypto12.createHash)("sha256");
|
|
41640
41514
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
41641
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
41515
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs43.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs43.readFileSync)(file.absolute);
|
|
41642
41516
|
hash.update(file.relative, "utf8");
|
|
41643
41517
|
hash.update("\0");
|
|
41644
41518
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -41653,8 +41527,8 @@ function digestFiles(files) {
|
|
|
41653
41527
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
41654
41528
|
return digestFiles(packedFiles.map((path2) => {
|
|
41655
41529
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
41656
|
-
if (!(0,
|
|
41657
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
41530
|
+
if (!(0, import_node_fs43.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
41531
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs43.lstatSync)(absolute) };
|
|
41658
41532
|
}));
|
|
41659
41533
|
}
|
|
41660
41534
|
|
|
@@ -41738,13 +41612,13 @@ function renderDistDriftReceipt(receipt) {
|
|
|
41738
41612
|
return lines2;
|
|
41739
41613
|
}
|
|
41740
41614
|
function readOrNull(path2) {
|
|
41741
|
-
return (0,
|
|
41615
|
+
return (0, import_node_fs44.existsSync)(path2) ? (0, import_node_fs44.readFileSync)(path2) : null;
|
|
41742
41616
|
}
|
|
41743
41617
|
function walkFiles(root) {
|
|
41744
41618
|
const files = [];
|
|
41745
41619
|
const walk2 = (directory) => {
|
|
41746
|
-
for (const entry of (0,
|
|
41747
|
-
const child2 = (0,
|
|
41620
|
+
for (const entry of (0, import_node_fs44.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
41621
|
+
const child2 = (0, import_node_path41.join)(directory, entry.name);
|
|
41748
41622
|
if (entry.isDirectory()) walk2(child2);
|
|
41749
41623
|
else files.push(child2);
|
|
41750
41624
|
}
|
|
@@ -41754,14 +41628,14 @@ function walkFiles(root) {
|
|
|
41754
41628
|
}
|
|
41755
41629
|
function bomPathFor(root) {
|
|
41756
41630
|
try {
|
|
41757
|
-
const registry2 = JSON.parse((0,
|
|
41758
|
-
return (0,
|
|
41631
|
+
const registry2 = JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path41.join)(root, "surfaces.json"), "utf8"));
|
|
41632
|
+
return (0, import_node_path41.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
41759
41633
|
} catch {
|
|
41760
|
-
return (0,
|
|
41634
|
+
return (0, import_node_path41.join)(root, "distribution-bom.json");
|
|
41761
41635
|
}
|
|
41762
41636
|
}
|
|
41763
41637
|
function rebuildTo(packageRoot, outDir) {
|
|
41764
|
-
(0,
|
|
41638
|
+
(0, import_node_child_process17.execFileSync)(process.execPath, ["build.mjs"], {
|
|
41765
41639
|
cwd: packageRoot,
|
|
41766
41640
|
env: { ...process.env, MMI_DIST_OUTDIR: outDir },
|
|
41767
41641
|
windowsHide: true,
|
|
@@ -41770,35 +41644,35 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
41770
41644
|
});
|
|
41771
41645
|
}
|
|
41772
41646
|
function runDistStatus(root) {
|
|
41773
|
-
const stage = (0,
|
|
41647
|
+
const stage = (0, import_node_fs44.mkdtempSync)((0, import_node_path41.join)((0, import_node_os20.tmpdir)(), "mmi-dist-drift-"));
|
|
41774
41648
|
let overlayCount = 0;
|
|
41775
41649
|
try {
|
|
41776
|
-
const cliOut = (0,
|
|
41777
|
-
const hubOut = (0,
|
|
41778
|
-
rebuildTo((0,
|
|
41779
|
-
rebuildTo((0,
|
|
41650
|
+
const cliOut = (0, import_node_path41.join)(stage, "cli-dist");
|
|
41651
|
+
const hubOut = (0, import_node_path41.join)(stage, "hub-dist");
|
|
41652
|
+
rebuildTo((0, import_node_path41.join)(root, "cli"), cliOut);
|
|
41653
|
+
rebuildTo((0, import_node_path41.join)(root, "updater"), hubOut);
|
|
41780
41654
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
41781
41655
|
const rebuilt = (path2) => {
|
|
41782
41656
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
41783
|
-
return spec ? readOrNull((0,
|
|
41657
|
+
return spec ? readOrNull((0, import_node_path41.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
41784
41658
|
};
|
|
41785
|
-
const committed = (path2) => readOrNull((0,
|
|
41786
|
-
const tree = (path2) => readOrNull((0,
|
|
41787
|
-
const distRoot = (0,
|
|
41788
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
41789
|
-
const bom = JSON.parse((0,
|
|
41659
|
+
const committed = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
|
|
41660
|
+
const tree = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
|
|
41661
|
+
const distRoot = (0, import_node_path41.join)(root, "cli", "dist");
|
|
41662
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path41.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
41663
|
+
const bom = JSON.parse((0, import_node_fs44.readFileSync)(bomPathFor(root), "utf8"));
|
|
41790
41664
|
const digest = (entries) => {
|
|
41791
|
-
const overlay = (0,
|
|
41665
|
+
const overlay = (0, import_node_path41.join)(stage, `overlay-${overlayCount++}`);
|
|
41792
41666
|
for (const entry of entries) {
|
|
41793
|
-
const target = (0,
|
|
41794
|
-
(0,
|
|
41795
|
-
(0,
|
|
41667
|
+
const target = (0, import_node_path41.join)(overlay, entry.path);
|
|
41668
|
+
(0, import_node_fs44.mkdirSync)((0, import_node_path41.dirname)(target), { recursive: true });
|
|
41669
|
+
(0, import_node_fs44.writeFileSync)(target, entry.bytes);
|
|
41796
41670
|
}
|
|
41797
41671
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
41798
41672
|
};
|
|
41799
41673
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
41800
41674
|
} finally {
|
|
41801
|
-
(0,
|
|
41675
|
+
(0, import_node_fs44.rmSync)(stage, { recursive: true, force: true });
|
|
41802
41676
|
}
|
|
41803
41677
|
}
|
|
41804
41678
|
|
|
@@ -41870,7 +41744,7 @@ function registerEdgeCommands(program3) {
|
|
|
41870
41744
|
|
|
41871
41745
|
// src/schedules-lift-command.ts
|
|
41872
41746
|
var import_promises8 = require("node:fs/promises");
|
|
41873
|
-
var
|
|
41747
|
+
var import_node_path42 = require("node:path");
|
|
41874
41748
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
41875
41749
|
var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
|
|
41876
41750
|
var SchedulesLiftUsageError = class extends Error {
|
|
@@ -41897,7 +41771,7 @@ async function readWorkflowFiles(dir) {
|
|
|
41897
41771
|
const files = [];
|
|
41898
41772
|
for (const name of names.sort()) {
|
|
41899
41773
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
41900
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0,
|
|
41774
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0, import_node_path42.join)(dir, name), "utf8") });
|
|
41901
41775
|
}
|
|
41902
41776
|
return files;
|
|
41903
41777
|
}
|
|
@@ -41979,9 +41853,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
41979
41853
|
}
|
|
41980
41854
|
|
|
41981
41855
|
// src/spawn-policy-core.ts
|
|
41982
|
-
var
|
|
41983
|
-
var
|
|
41984
|
-
var
|
|
41856
|
+
var import_node_child_process18 = require("node:child_process");
|
|
41857
|
+
var import_node_fs45 = require("node:fs");
|
|
41858
|
+
var import_node_path43 = require("node:path");
|
|
41985
41859
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
41986
41860
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
41987
41861
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -42050,7 +41924,7 @@ function findViolationsInSource(raw) {
|
|
|
42050
41924
|
return found;
|
|
42051
41925
|
}
|
|
42052
41926
|
function policedFiles(root) {
|
|
42053
|
-
const r = (0,
|
|
41927
|
+
const r = (0, import_node_child_process18.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
42054
41928
|
cwd: root,
|
|
42055
41929
|
encoding: "utf8",
|
|
42056
41930
|
windowsHide: true,
|
|
@@ -42067,7 +41941,7 @@ function runSpawnPolicy(root) {
|
|
|
42067
41941
|
for (const file of files) {
|
|
42068
41942
|
let raw;
|
|
42069
41943
|
try {
|
|
42070
|
-
raw = (0,
|
|
41944
|
+
raw = (0, import_node_fs45.readFileSync)((0, import_node_path43.join)(root, file), "utf8");
|
|
42071
41945
|
} catch {
|
|
42072
41946
|
continue;
|
|
42073
41947
|
}
|
|
@@ -42087,19 +41961,19 @@ function runSpawnPolicy(root) {
|
|
|
42087
41961
|
function registerDeveloperCommands(program3) {
|
|
42088
41962
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
42089
41963
|
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) => {
|
|
42090
|
-
const path2 = (0,
|
|
42091
|
-
const current = (0,
|
|
41964
|
+
const path2 = (0, import_node_path44.join)(process.cwd(), ".gitignore");
|
|
41965
|
+
const current = (0, import_node_fs46.existsSync)(path2) ? (0, import_node_fs46.readFileSync)(path2, "utf8") : null;
|
|
42092
41966
|
const plan = planManagedGitignore(current);
|
|
42093
41967
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
42094
41968
|
if (opts.json) {
|
|
42095
|
-
if (opts.write && plan.changed) (0,
|
|
41969
|
+
if (opts.write && plan.changed) (0, import_node_fs46.writeFileSync)(path2, plan.content, "utf8");
|
|
42096
41970
|
console.log(JSON.stringify(plan, null, 2));
|
|
42097
41971
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
42098
41972
|
return;
|
|
42099
41973
|
}
|
|
42100
41974
|
if (opts.write) {
|
|
42101
41975
|
if (plan.changed) {
|
|
42102
|
-
(0,
|
|
41976
|
+
(0, import_node_fs46.writeFileSync)(path2, plan.content, "utf8");
|
|
42103
41977
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
42104
41978
|
} else {
|
|
42105
41979
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -42398,7 +42272,6 @@ async function postToAlertsChannel(deps, text) {
|
|
|
42398
42272
|
|
|
42399
42273
|
// src/release-announce.ts
|
|
42400
42274
|
var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
|
|
42401
|
-
var RELEASE_CHANNEL_KEY = "SLACK_RELEASE_CHANNEL";
|
|
42402
42275
|
var MAX_BULLETS = 6;
|
|
42403
42276
|
var MAX_SUMMARY_LINES = 8;
|
|
42404
42277
|
function neutralizeHubProductBrands(text) {
|
|
@@ -42406,13 +42279,6 @@ function neutralizeHubProductBrands(text) {
|
|
|
42406
42279
|
s = s.replace(/\bdesign-system\s+design-system\b/gi, "design-system");
|
|
42407
42280
|
return s.replace(/\s{2,}/g, " ").trim();
|
|
42408
42281
|
}
|
|
42409
|
-
function releaseChannelParam(repo) {
|
|
42410
|
-
return `/mmi-future/${slugOf(repo)}/${RELEASE_CHANNEL_KEY}`;
|
|
42411
|
-
}
|
|
42412
|
-
function isAbsentParameter(e) {
|
|
42413
|
-
const message2 = e instanceof Error ? e.message : String(e);
|
|
42414
|
-
return /ParameterNotFound/.test(message2) || /is empty$/.test(message2);
|
|
42415
|
-
}
|
|
42416
42282
|
function escapeMrkdwn(text) {
|
|
42417
42283
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
42418
42284
|
}
|
|
@@ -42467,14 +42333,20 @@ function curateReleaseNotes(notesMd, maxBullets = MAX_BULLETS, neutralize = true
|
|
|
42467
42333
|
if (dropped > 0) lines2.push(`\u2026and ${dropped} more`);
|
|
42468
42334
|
return lines2;
|
|
42469
42335
|
}
|
|
42336
|
+
var ANNOUNCE_FRAMES = {
|
|
42337
|
+
en: { released: "released", notes: "Release notes" },
|
|
42338
|
+
tr: { released: "yay\u0131nda", notes: "S\xFCr\xFCm notlar\u0131" }
|
|
42339
|
+
};
|
|
42470
42340
|
function formatAnnouncement(args) {
|
|
42471
42341
|
const name = args.repo.split("/")[1] ?? args.repo;
|
|
42342
|
+
const code = args.language ?? "en";
|
|
42343
|
+
const frame = Object.hasOwn(ANNOUNCE_FRAMES, code) ? ANNOUNCE_FRAMES[code] : ANNOUNCE_FRAMES.en;
|
|
42472
42344
|
return [
|
|
42473
|
-
`:rocket: *${name} ${args.tag} released*`,
|
|
42345
|
+
`:rocket: *${name} ${args.tag} ${frame.released}*`,
|
|
42474
42346
|
"",
|
|
42475
42347
|
...args.lines,
|
|
42476
42348
|
"",
|
|
42477
|
-
`<${args.releaseUrl}
|
|
42349
|
+
`<${args.releaseUrl}|${frame.notes}>`
|
|
42478
42350
|
].join("\n");
|
|
42479
42351
|
}
|
|
42480
42352
|
function summaryFileLines(content, neutralize = true) {
|
|
@@ -42485,18 +42357,12 @@ function summaryFileLines(content, neutralize = true) {
|
|
|
42485
42357
|
}
|
|
42486
42358
|
async function resolveAnnounceTarget(deps, repo) {
|
|
42487
42359
|
if (repo.toLowerCase() === ANNOUNCE_REPO.toLowerCase()) return { kind: "alerts" };
|
|
42488
|
-
const
|
|
42489
|
-
|
|
42490
|
-
const channel = await readSsmParameter(deps, param, false);
|
|
42491
|
-
return { kind: "channel", channel };
|
|
42492
|
-
} catch (e) {
|
|
42493
|
-
if (isAbsentParameter(e)) return { kind: "none", param };
|
|
42494
|
-
throw e;
|
|
42495
|
-
}
|
|
42360
|
+
const target = deps.fetchReleaseTarget ? await deps.fetchReleaseTarget(slugOf(repo)) : { channel: null, language: "en" };
|
|
42361
|
+
return target.channel ? { kind: "channel", channel: target.channel, language: target.language } : { kind: "none" };
|
|
42496
42362
|
}
|
|
42497
42363
|
function missingChannelNote(repo) {
|
|
42498
42364
|
const slug = slugOf(repo);
|
|
42499
|
-
return `no
|
|
42365
|
+
return `no releaseChannel configured for ${slug} \u2014 enable release notes with \`mmi-cli oracle org project set --var releaseChannel=<slack channel id> ${repo}\``;
|
|
42500
42366
|
}
|
|
42501
42367
|
async function announceRelease(deps, args) {
|
|
42502
42368
|
const neutralize = args.repo.toLowerCase() === ANNOUNCE_REPO.toLowerCase();
|
|
@@ -42517,9 +42383,20 @@ async function announceRelease(deps, args) {
|
|
|
42517
42383
|
}
|
|
42518
42384
|
}
|
|
42519
42385
|
}
|
|
42520
|
-
if (lines2.length === 0)
|
|
42386
|
+
if (lines2.length === 0) {
|
|
42387
|
+
if (target.kind !== "alerts") {
|
|
42388
|
+
return { status: "skipped", note: "no --announce-summary-file given \u2014 raw release notes are never posted to a project channel" };
|
|
42389
|
+
}
|
|
42390
|
+
lines2 = curateReleaseNotes(view.body ?? "", MAX_BULLETS, neutralize);
|
|
42391
|
+
}
|
|
42521
42392
|
if (lines2.length === 0) lines2 = ["\u2022 maintenance release \u2014 see the notes for details"];
|
|
42522
|
-
const text = formatAnnouncement({
|
|
42393
|
+
const text = formatAnnouncement({
|
|
42394
|
+
repo: args.repo,
|
|
42395
|
+
tag: args.tag,
|
|
42396
|
+
lines: lines2,
|
|
42397
|
+
releaseUrl,
|
|
42398
|
+
...target.kind === "channel" ? { language: target.language } : {}
|
|
42399
|
+
});
|
|
42523
42400
|
if (target.kind === "alerts") {
|
|
42524
42401
|
await postToAlertsChannel(deps, text);
|
|
42525
42402
|
return { status: "announced", note: `announced ${args.tag} to the alerts channel` };
|
|
@@ -43649,12 +43526,12 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = H
|
|
|
43649
43526
|
}
|
|
43650
43527
|
|
|
43651
43528
|
// src/hotfix-coverage.ts
|
|
43652
|
-
var
|
|
43529
|
+
var import_node_child_process19 = require("node:child_process");
|
|
43653
43530
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
43654
43531
|
function checkHotfixCoverage(options = {}) {
|
|
43655
43532
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
43656
43533
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
43657
|
-
const git3 = options.git ?? ((args, opts) => (0,
|
|
43534
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
43658
43535
|
const revList = (range) => {
|
|
43659
43536
|
const out = git3(["rev-list", "--no-merges", range]).trim();
|
|
43660
43537
|
return out ? out.split("\n") : [];
|
|
@@ -43722,7 +43599,7 @@ function checkHotfixCoverage(options = {}) {
|
|
|
43722
43599
|
}
|
|
43723
43600
|
function checkHotfixCarries(options) {
|
|
43724
43601
|
const { cwd = process.cwd(), branch, baseRef, targets } = options;
|
|
43725
|
-
const git3 = options.git ?? ((args, opts) => (0,
|
|
43602
|
+
const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
43726
43603
|
const isAncestor = (sha, ref) => {
|
|
43727
43604
|
try {
|
|
43728
43605
|
git3(["merge-base", "--is-ancestor", sha, ref]);
|
|
@@ -43754,8 +43631,8 @@ function checkHotfixCarries(options) {
|
|
|
43754
43631
|
}
|
|
43755
43632
|
|
|
43756
43633
|
// src/train-commands.ts
|
|
43757
|
-
var
|
|
43758
|
-
var
|
|
43634
|
+
var import_node_fs47 = require("node:fs");
|
|
43635
|
+
var import_node_path45 = require("node:path");
|
|
43759
43636
|
var INVOKED_ARGV = process.argv.slice(2);
|
|
43760
43637
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
43761
43638
|
function resolveReleaseBumpIntent(raw) {
|
|
@@ -43767,7 +43644,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
43767
43644
|
}
|
|
43768
43645
|
function readRepoVersion() {
|
|
43769
43646
|
try {
|
|
43770
|
-
return JSON.parse((0,
|
|
43647
|
+
return JSON.parse((0, import_node_fs47.readFileSync)((0, import_node_path45.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
43771
43648
|
} catch {
|
|
43772
43649
|
return void 0;
|
|
43773
43650
|
}
|
|
@@ -43878,6 +43755,18 @@ var NODE_PREPARE_TIMEOUT_MS = 10 * 6e4;
|
|
|
43878
43755
|
var NODE_VERIFY_TIMEOUT_MS = 5 * 6e4;
|
|
43879
43756
|
var NPM_TRAIN_TIMEOUT_MS = 6e4;
|
|
43880
43757
|
var JERV_GATEWAY_DEPLOY_TIMEOUT_MS = 20 * 6e4;
|
|
43758
|
+
async function announceSummaryRefusal(path2) {
|
|
43759
|
+
let lines2;
|
|
43760
|
+
try {
|
|
43761
|
+
lines2 = summaryFileLines(await (0, import_promises10.readFile)(path2, "utf8"));
|
|
43762
|
+
} catch (e) {
|
|
43763
|
+
return `release: could not read --announce-summary-file ${path2}: ${e.message} \u2014 see docs/Guides/train-troubleshooting.md#announce-summary-missing`;
|
|
43764
|
+
}
|
|
43765
|
+
if (lines2.length < 3 || lines2.length > 6) {
|
|
43766
|
+
return `release: --announce-summary-file must contain 3-6 non-empty LLM-curated lines (found ${lines2.length}) \u2014 see docs/Guides/train-troubleshooting.md#announce-summary-missing`;
|
|
43767
|
+
}
|
|
43768
|
+
return null;
|
|
43769
|
+
}
|
|
43881
43770
|
function trainApplyDeps() {
|
|
43882
43771
|
return {
|
|
43883
43772
|
run: async (file, args) => {
|
|
@@ -43940,12 +43829,25 @@ function trainApplyDeps() {
|
|
|
43940
43829
|
// train lands the back-merge itself instead of leaving a manual step. Same enqueue path `pr land` uses,
|
|
43941
43830
|
// but method `--merge` (NOT squash): the misalignment guard needs the merge parentage to survive.
|
|
43942
43831
|
mergeAuto: (prNumber, repo) => ghMergeAutoEnqueue(prNumber, repo, "--merge"),
|
|
43943
|
-
// Slack release announcement (#883/#
|
|
43944
|
-
//
|
|
43832
|
+
// Slack release announcement (#883/#6521/#6523): the org alerts channel for the Hub, the project's own
|
|
43833
|
+
// registry releaseChannel META for a product repo — best-effort inside announceRelease itself.
|
|
43945
43834
|
announce: (args) => announceRelease({
|
|
43946
43835
|
run: async (file, cmdArgs) => (await execFileP(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS })).stdout,
|
|
43947
43836
|
readFile: (path2) => (0, import_promises10.readFile)(path2, "utf8"),
|
|
43948
|
-
removeFile: (path2) => (0, import_promises10.unlink)(path2)
|
|
43837
|
+
removeFile: (path2) => (0, import_promises10.unlink)(path2),
|
|
43838
|
+
// #6521/#6523: the project's own release channel AND releaseLanguage, read from registry META in ONE
|
|
43839
|
+
// checked read. Same shape as fetchEdgeDomains below: a FAILED read refuses by name so the announce
|
|
43840
|
+
// note reports a failed read, never a project that configured nothing.
|
|
43841
|
+
fetchReleaseTarget: async (slug) => {
|
|
43842
|
+
const read = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
|
|
43843
|
+
if (!read.ok) {
|
|
43844
|
+
throw new Error(`release announcement: Hub registry read FAILED for ${slug} (${read.error}) \u2014 releaseChannel is UNREAD, not unset`);
|
|
43845
|
+
}
|
|
43846
|
+
return {
|
|
43847
|
+
channel: read.project?.releaseChannel?.trim() || null,
|
|
43848
|
+
language: read.project?.releaseLanguage?.trim() || "en"
|
|
43849
|
+
};
|
|
43850
|
+
}
|
|
43949
43851
|
}, args),
|
|
43950
43852
|
// #4713 (I/O-boundary census): `null` used to mean BOTH "this project configures no edge domains"
|
|
43951
43853
|
// (a real answer) and "the registry read missed" — so a release verdict printed an environments block
|
|
@@ -44077,21 +43979,6 @@ function hotfixFollowUpLegs(runs, foldPort, alignment, foldNote, deployNote) {
|
|
|
44077
43979
|
}
|
|
44078
43980
|
return legs;
|
|
44079
43981
|
}
|
|
44080
|
-
var JERV_POWERTOOLS_REPO = "mutmutco/Jerv-PowerTools";
|
|
44081
|
-
async function runPostReleaseJervDoctor(repo) {
|
|
44082
|
-
if (repo.toLowerCase() !== JERV_POWERTOOLS_REPO.toLowerCase()) return void 0;
|
|
44083
|
-
try {
|
|
44084
|
-
await execJervCli(["doctor"], { timeout: 10 * 6e4 });
|
|
44085
|
-
return { status: "completed", note: "jerv-cli doctor completed on this owner machine" };
|
|
44086
|
-
} catch (e) {
|
|
44087
|
-
const err = e;
|
|
44088
|
-
const detail = (err.stderr?.trim() || err.message || "unknown doctor failure").split(/\r?\n/)[0];
|
|
44089
|
-
return {
|
|
44090
|
-
status: "reported-issues",
|
|
44091
|
-
note: `jerv-cli doctor ran but reported issues (${detail})`
|
|
44092
|
-
};
|
|
44093
|
-
}
|
|
44094
|
-
}
|
|
44095
43982
|
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))) {
|
|
44096
43983
|
for (let attempt = 0; ; attempt++) {
|
|
44097
43984
|
try {
|
|
@@ -44156,9 +44043,6 @@ function renderTrainApply(commandName, r) {
|
|
|
44156
44043
|
if (r.projectInfoSync) {
|
|
44157
44044
|
base = `${base}; project info: ${r.projectInfoSync.note}`;
|
|
44158
44045
|
}
|
|
44159
|
-
if (r.postReleaseJervDoctor) {
|
|
44160
|
-
base = `${base}; Jerv convergence: ${r.postReleaseJervDoctor.note}`;
|
|
44161
|
-
}
|
|
44162
44046
|
if (r.announceNote) base = `${base}; announce: ${r.announceNote}`;
|
|
44163
44047
|
if (r.releaseVerdict) {
|
|
44164
44048
|
const v = r.releaseVerdict;
|
|
@@ -44205,7 +44089,7 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44205
44089
|
for (const commandName of ["rcand", "release"]) {
|
|
44206
44090
|
const trainCmd = program3.command(commandName).description(`plan ${commandName} train operations; mutations require explicit master-admin approval`).option("--json", "machine-readable output").option("--watch", "block on the deploy/publish workflow runs and report their outcomes").option("--apply", "execute the guarded master-only train after explicit approval").option("--resume", commandName === "rcand" ? "finish a candidate whose immutable public rc tag passed policy but origin/rc was not pushed (#3881)" : "finish a partial release or its protected post-release alignment without re-cutting, republishing, or redeploying (#3851/#3885)").option("--out <path>", "write the terminal result (--apply, --resume, --abort, --retry-publish) to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5983/#6068)");
|
|
44207
44091
|
const RELEASE_ONLY_FLAGS = [
|
|
44208
|
-
{ flags: "--announce-summary-file <path>", description: "agent-curated 3-6 line release Slack summary; required for a new MMI-Hub --apply (#883/#3901)
|
|
44092
|
+
{ flags: "--announce-summary-file <path>", description: "agent-curated 3-6 line release Slack summary; required for a new MMI-Hub --apply (#883/#3901) and for a product repo whose registry META sets `releaseChannel` (#6523), in its `releaseLanguage`; nothing is posted to a project channel without it" },
|
|
44209
44093
|
{ flags: "--ack <shas>", description: "comma-separated dev shas a human verified are in the candidate, overriding the hotfix-coverage guard for a conflicted port whose -x trailer was lost (#958)" },
|
|
44210
44094
|
{ flags: "--dev", description: "full-track repos release development -> main directly, skipping rc (refuses if rc carries content not in development; no-op on direct-track repos) (#1062)" },
|
|
44211
44095
|
{ flags: "--abort", description: "with --apply, delete only a proven unpublished failed release candidate on any registered deploy model and restore local main for a same-version recut (#3944/#5650/#5657)" },
|
|
@@ -44323,22 +44207,35 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44323
44207
|
const guard = planTrainApplyRepoGuard(o.repo, await resolveRepo(), rerun);
|
|
44324
44208
|
if (!guard.ok) return fail(`${commandName}: ${guard.message}`);
|
|
44325
44209
|
}
|
|
44326
|
-
if (o.apply && commandName === "release"
|
|
44327
|
-
|
|
44328
|
-
|
|
44329
|
-
|
|
44330
|
-
|
|
44331
|
-
|
|
44332
|
-
|
|
44333
|
-
|
|
44334
|
-
|
|
44335
|
-
|
|
44336
|
-
|
|
44210
|
+
if (o.apply && commandName === "release") {
|
|
44211
|
+
const applyingRepo = await resolveRepo();
|
|
44212
|
+
const slug = applyingRepo ? slugOf(applyingRepo) : "";
|
|
44213
|
+
const isHub = applyingRepo?.toLowerCase() === ANNOUNCE_REPO.toLowerCase();
|
|
44214
|
+
let announcesToChannel = isHub;
|
|
44215
|
+
let releaseLanguage2 = "en";
|
|
44216
|
+
if (!isHub && slug) {
|
|
44217
|
+
let read;
|
|
44218
|
+
try {
|
|
44219
|
+
read = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
|
|
44220
|
+
} catch (e) {
|
|
44221
|
+
read = { ok: false, error: e.message };
|
|
44222
|
+
}
|
|
44223
|
+
if (!read.ok) {
|
|
44224
|
+
return fail(
|
|
44225
|
+
`release: Hub registry read failed for ${slug} (${read.error}) \u2014 whether ${slug} announces to a project release channel is UNREAD, so the --announce-summary-file requirement is unproven. Likely transient (cold start, network, or auth blip); retry shortly.`
|
|
44226
|
+
);
|
|
44227
|
+
}
|
|
44228
|
+
announcesToChannel = Boolean(read.project?.releaseChannel?.trim());
|
|
44229
|
+
releaseLanguage2 = read.project?.releaseLanguage?.trim() || "en";
|
|
44337
44230
|
}
|
|
44338
|
-
if (
|
|
44339
|
-
|
|
44340
|
-
|
|
44341
|
-
|
|
44231
|
+
if (announcesToChannel) {
|
|
44232
|
+
if (!o.announceSummaryFile) {
|
|
44233
|
+
return fail(
|
|
44234
|
+
isHub ? "release: a new MMI-Hub release requires --announce-summary-file <path> with 3-6 short LLM-curated lines; create the summary file, then rerun the same release command \u2014 see docs/Guides/train-troubleshooting.md#announce-summary-missing" : `release: ${slug} announces to its project release channel, so a new release requires --announce-summary-file <path> with 3-6 short plain-language lines in the project's releaseLanguage (${releaseLanguage2}); create the summary file, then rerun the same release command \u2014 see docs/Guides/train-troubleshooting.md#announce-summary-missing`
|
|
44235
|
+
);
|
|
44236
|
+
}
|
|
44237
|
+
const refusal = await announceSummaryRefusal(o.announceSummaryFile);
|
|
44238
|
+
if (refusal) return fail(refusal);
|
|
44342
44239
|
}
|
|
44343
44240
|
}
|
|
44344
44241
|
if (o.apply) {
|
|
@@ -44347,7 +44244,6 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44347
44244
|
const raw = await runTrainApply(commandName, trainApplyDeps(), { watch: o.watch, announceSummaryFile: o.announceSummaryFile, ack, dev: o.dev });
|
|
44348
44245
|
const result = raw.workflowRuns ? { ...raw, workflowRuns: raw.workflowRuns.map(workflowRunWithEvidence) } : raw;
|
|
44349
44246
|
let projectInfoSync;
|
|
44350
|
-
const postReleaseJervDoctor = commandName === "release" ? await runPostReleaseJervDoctor(result.repo) : void 0;
|
|
44351
44247
|
if (commandName === "release") {
|
|
44352
44248
|
projectInfoSync = await runProjectInfoSyncLeg(runProjectInfoSyncCallback, result.repo);
|
|
44353
44249
|
}
|
|
@@ -44355,7 +44251,6 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44355
44251
|
const reported = {
|
|
44356
44252
|
...result,
|
|
44357
44253
|
...projectInfoSync ? { projectInfoSync } : {},
|
|
44358
|
-
...postReleaseJervDoctor ? { postReleaseJervDoctor } : {},
|
|
44359
44254
|
...releaseVerdict ? { releaseVerdict } : {}
|
|
44360
44255
|
};
|
|
44361
44256
|
const output = o.json ? JSON.stringify(reported, null, 2) : renderTrainApply(commandName, reported);
|
|
@@ -44370,6 +44265,8 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44370
44265
|
const repo = o.repo ?? await resolveRepo();
|
|
44371
44266
|
const targets = commandName === "rcand" && !o.repo ? await resolveRcandPlanTargets() : void 0;
|
|
44372
44267
|
let releaseTrack;
|
|
44268
|
+
let releaseChannel = null;
|
|
44269
|
+
let releaseLanguage = "en";
|
|
44373
44270
|
if (repo) {
|
|
44374
44271
|
let read;
|
|
44375
44272
|
try {
|
|
@@ -44384,6 +44281,8 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44384
44281
|
}
|
|
44385
44282
|
const raw = read.project?.releaseTrack;
|
|
44386
44283
|
if (isReleaseTrack(raw)) releaseTrack = raw;
|
|
44284
|
+
releaseChannel = read.project?.releaseChannel?.trim() || null;
|
|
44285
|
+
releaseLanguage = read.project?.releaseLanguage?.trim() || "en";
|
|
44387
44286
|
}
|
|
44388
44287
|
const steps = trainPlan(commandName, { ...targets ?? {}, repo, dev: o.dev, releaseTrack });
|
|
44389
44288
|
let doctor;
|
|
@@ -44395,7 +44294,7 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44395
44294
|
}
|
|
44396
44295
|
}
|
|
44397
44296
|
console.log(
|
|
44398
|
-
o.json ? JSON.stringify({ command: commandName, ...targets ?? {}, repo, steps, ...doctor ? { doctor } : {} }, null, 2) : [renderSteps(`mmi-cli ${commandName}: dry-run plan`, steps), ...doctor ? ["", formatTrainDoctor(doctor)] : []].join("\n")
|
|
44297
|
+
o.json ? JSON.stringify({ command: commandName, ...targets ?? {}, repo, steps, ...commandName === "release" ? { releaseChannel, releaseLanguage } : {}, ...doctor ? { doctor } : {} }, null, 2) : [renderSteps(`mmi-cli ${commandName}: dry-run plan`, steps), ...doctor ? ["", formatTrainDoctor(doctor)] : []].join("\n")
|
|
44399
44298
|
);
|
|
44400
44299
|
});
|
|
44401
44300
|
}
|
|
@@ -44424,7 +44323,6 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44424
44323
|
...r.checkout ? [` - checkout: ${r.checkout.note}`] : [],
|
|
44425
44324
|
...r.localSync && localTrainSyncBannerLine(r.localSync) ? [` - local sync: ${localTrainSyncBannerLine(r.localSync)}`] : [],
|
|
44426
44325
|
...r.projectInfoSync ? [` - project info: ${r.projectInfoSync.note}`] : [],
|
|
44427
|
-
...r.postReleaseJervDoctor ? [` - Jerv convergence: ${r.postReleaseJervDoctor.note}`] : [],
|
|
44428
44326
|
` - next: mmi-cli devops hotfix status ${r.tag} (no back-merge of the FIX \u2014 development already has it; the version fold above is ported for you, #4410)`,
|
|
44429
44327
|
...r.ledger ? formatReleaseLedgerReport(r.ledger).map((line) => ` ${line}`) : []
|
|
44430
44328
|
].join("\n");
|
|
@@ -44485,9 +44383,8 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
|
|
|
44485
44383
|
hotfixCmd.command("start").description("cherry-pick one or more merged development PRs (or SHAs) onto hotfix/vX.Y.Z from origin/main, bump the distribution, open the main-base PR").requiredOption("--from <pr#|sha[,pr#|sha...]>", "merged development PR number(s) or commit SHA(s) to cherry-pick, in pick order \u2014 one hotfix cycle carries as many fixes as you name (#4411)").option("--json", "machine-readable output").option("--out <path>", "write the result to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5983/#6068)").action(async (o) => runHotfixSub("start", () => runHotfixStart(trainApplyDeps(), { from: o.from }), o, renderHotfixStart));
|
|
44486
44384
|
hotfixCmd.command("release <version>").description("after the hotfix PR is merged + checks green: tag, GitHub Release, watch deploy/publish, verify distribution (idempotent)").option("--json", "machine-readable output").option("--announce-summary-file <path>", "agent-curated 3-6 line release Slack summary; required for a NEW MMI-Hub hotfix Release (#883/#3901/#6068), optional for a product repo announcing to its project release channel (#6517)").option("--carries <pr#|sha[,pr#|sha...]>", "declared fix target(s) this hotfix must carry; each must be proven present before tagging (#3056)").option("--out <path>", "write the result to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5983/#6068)").action(async (version, o) => runHotfixSub("release", async () => {
|
|
44487
44385
|
const result = await runHotfixRelease(trainApplyDeps(), version, { announceSummaryFile: o.announceSummaryFile, carries: o.carries ? [o.carries] : [] });
|
|
44488
|
-
const postReleaseJervDoctor = await runPostReleaseJervDoctor(result.repo);
|
|
44489
44386
|
const projectInfoSync = await runProjectInfoSyncLeg(runProjectInfoSyncCallback, result.repo);
|
|
44490
|
-
return { ...result, projectInfoSync
|
|
44387
|
+
return { ...result, projectInfoSync };
|
|
44491
44388
|
}, o, renderHotfixRelease));
|
|
44492
44389
|
function hotfixStatusDeps() {
|
|
44493
44390
|
const STATUS_GH_MS = 8e3;
|
|
@@ -44525,12 +44422,12 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
44525
44422
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
44526
44423
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
44527
44424
|
function envHealLockPath(home) {
|
|
44528
|
-
return (0,
|
|
44425
|
+
return (0, import_node_path46.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
44529
44426
|
}
|
|
44530
44427
|
async function withEnvHealLock(what, run) {
|
|
44531
44428
|
try {
|
|
44532
44429
|
return await withFileLock(
|
|
44533
|
-
envHealLockPath((0,
|
|
44430
|
+
envHealLockPath((0, import_node_os21.homedir)()),
|
|
44534
44431
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
44535
44432
|
run
|
|
44536
44433
|
);
|
|
@@ -44626,7 +44523,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
44626
44523
|
const configRoot = surfaceConfigRoot(surface);
|
|
44627
44524
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
44628
44525
|
const plan = buildPluginCachePlan(
|
|
44629
|
-
(0,
|
|
44526
|
+
(0, import_node_os21.homedir)(),
|
|
44630
44527
|
running,
|
|
44631
44528
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
44632
44529
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -44654,14 +44551,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
44654
44551
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
44655
44552
|
const installed = installedActivePluginVersion(surface);
|
|
44656
44553
|
const plan = buildPluginCachePlan(
|
|
44657
|
-
(0,
|
|
44554
|
+
(0, import_node_os21.homedir)(),
|
|
44658
44555
|
running,
|
|
44659
44556
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
44660
44557
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
44661
44558
|
);
|
|
44662
44559
|
const result = applyPluginCachePlan(
|
|
44663
44560
|
plan,
|
|
44664
|
-
(p) => (0,
|
|
44561
|
+
(p) => (0, import_node_fs48.rmSync)(p, { recursive: true }),
|
|
44665
44562
|
stagingApplyFsGuard(configRoot)
|
|
44666
44563
|
);
|
|
44667
44564
|
return {
|
|
@@ -44696,7 +44593,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
44696
44593
|
// has no generated routing index would
|
|
44697
44594
|
// get a permanent — demanding an artifact it never asked for.
|
|
44698
44595
|
docsIndexState: (root) => {
|
|
44699
|
-
if (!(0,
|
|
44596
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
44700
44597
|
const real = createDocsIndexDeps(root);
|
|
44701
44598
|
let docs;
|
|
44702
44599
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -44705,7 +44602,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
44705
44602
|
},
|
|
44706
44603
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
44707
44604
|
healDocsIndex: (root) => {
|
|
44708
|
-
if (!(0,
|
|
44605
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
44709
44606
|
const real = createDocsIndexDeps(root);
|
|
44710
44607
|
let docs;
|
|
44711
44608
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -45315,7 +45212,7 @@ projectDeploy.command("doctor").description("read-only estate scan for duplicate
|
|
|
45315
45212
|
if (report.error && report.status !== 403) return failGraceful(`org project deploy doctor: ${report.error}`);
|
|
45316
45213
|
if (!report.ok && !report.error) process.exitCode = 1;
|
|
45317
45214
|
});
|
|
45318
|
-
project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").addOption(new Option("--class <class>", "deployable | content").choices(["deployable", "content"])).addOption(new Option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (capability shape)`).choices([...PROJECT_TYPES])).addOption(new Option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).choices([...DEPLOY_MODELS])).addOption(new Option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).choices([...RELEASE_TRACKS])).option("--var <KEY=VALUE...>", settableVarHelp()).option("--set <KEY=VALUE...>", "alias of --var (one KEY=VALUE per flag; repeat the flag to set several)").option("--secrets-file <path>", 'read the #2244 secrets catalog map (JSON) from a file and merge it per entry into the existing catalog (clear all with --unset secrets). SHAPE: a JSON object keyed by env name, each entry {"key":"UPPER_SNAKE" (repeat the env name), "purpose":"<non-empty>", "group":"<e.g. auth|database>", "owner":"<github login>", "stages":[] (empty = the one stageless shared value; else any of dev/rc/main), "consumers":["runtime"|"box"|?], "provider":"<optional>"}').option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, requiredBuildSecrets, secrets, edgeDomains, requiredGcpApis, publishRequired").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
45215
|
+
project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").addOption(new Option("--class <class>", "deployable | content").choices(["deployable", "content"])).addOption(new Option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (capability shape)`).choices([...PROJECT_TYPES])).addOption(new Option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).choices([...DEPLOY_MODELS])).addOption(new Option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).choices([...RELEASE_TRACKS])).option("--var <KEY=VALUE...>", settableVarHelp()).option("--set <KEY=VALUE...>", "alias of --var (one KEY=VALUE per flag; repeat the flag to set several)").option("--secrets-file <path>", 'read the #2244 secrets catalog map (JSON) from a file and merge it per entry into the existing catalog (clear all with --unset secrets). SHAPE: a JSON object keyed by env name, each entry {"key":"UPPER_SNAKE" (repeat the env name), "purpose":"<non-empty>", "group":"<e.g. auth|database>", "owner":"<github login>", "stages":[] (empty = the one stageless shared value; else any of dev/rc/main), "consumers":["runtime"|"box"|?], "provider":"<optional>"}').option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, requiredBuildSecrets, secrets, edgeDomains, requiredGcpApis, publishRequired, releaseChannel, releaseLanguage").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
45319
45216
|
const cfg = await loadConfig();
|
|
45320
45217
|
let target;
|
|
45321
45218
|
try {
|
|
@@ -45331,7 +45228,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
45331
45228
|
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`);
|
|
45332
45229
|
if (o.secretsFile) {
|
|
45333
45230
|
try {
|
|
45334
|
-
vars.push(`secrets=${(0,
|
|
45231
|
+
vars.push(`secrets=${(0, import_node_fs48.readFileSync)(o.secretsFile, "utf8")}`);
|
|
45335
45232
|
} catch (e) {
|
|
45336
45233
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
45337
45234
|
}
|
|
@@ -45653,16 +45550,16 @@ function ciAuditDeps2() {
|
|
|
45653
45550
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
45654
45551
|
readSeedFile: (path2) => {
|
|
45655
45552
|
if (!root) return null;
|
|
45656
|
-
const fullPath = (0,
|
|
45657
|
-
return (0,
|
|
45553
|
+
const fullPath = (0, import_node_path46.join)(root, path2);
|
|
45554
|
+
return (0, import_node_fs48.existsSync)(fullPath) ? (0, import_node_fs48.readFileSync)(fullPath, "utf8") : null;
|
|
45658
45555
|
}
|
|
45659
45556
|
};
|
|
45660
45557
|
}
|
|
45661
45558
|
function hubRoot2() {
|
|
45662
|
-
const fromPkg = (0,
|
|
45559
|
+
const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
|
|
45663
45560
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
45664
|
-
if ((0,
|
|
45665
|
-
if ((0,
|
|
45561
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
|
|
45562
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), marker))) return process.cwd();
|
|
45666
45563
|
return null;
|
|
45667
45564
|
}
|
|
45668
45565
|
registerQueryCommands(program2);
|
|
@@ -45762,10 +45659,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
45762
45659
|
targets = resolution.targets;
|
|
45763
45660
|
}
|
|
45764
45661
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
45765
|
-
const fileMatrix = (0,
|
|
45662
|
+
const fileMatrix = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
45766
45663
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
45767
45664
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
45768
|
-
const sanctioned = (0,
|
|
45665
|
+
const sanctioned = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
45769
45666
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
45770
45667
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
45771
45668
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -45796,16 +45693,16 @@ function directoryBytes(path2) {
|
|
|
45796
45693
|
let total = 0;
|
|
45797
45694
|
let entries;
|
|
45798
45695
|
try {
|
|
45799
|
-
entries = (0,
|
|
45696
|
+
entries = (0, import_node_fs48.readdirSync)(path2, { withFileTypes: true });
|
|
45800
45697
|
} catch {
|
|
45801
45698
|
return 0;
|
|
45802
45699
|
}
|
|
45803
45700
|
for (const entry of entries) {
|
|
45804
|
-
const child2 = (0,
|
|
45701
|
+
const child2 = (0, import_node_path46.join)(path2, entry.name);
|
|
45805
45702
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
45806
45703
|
else {
|
|
45807
45704
|
try {
|
|
45808
|
-
total += (0,
|
|
45705
|
+
total += (0, import_node_fs48.statSync)(child2).size;
|
|
45809
45706
|
} catch {
|
|
45810
45707
|
}
|
|
45811
45708
|
}
|
|
@@ -45813,25 +45710,25 @@ function directoryBytes(path2) {
|
|
|
45813
45710
|
return total;
|
|
45814
45711
|
}
|
|
45815
45712
|
function listDirEntries(dir) {
|
|
45816
|
-
return (0,
|
|
45713
|
+
return (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
45817
45714
|
}
|
|
45818
45715
|
function readInstalledPluginRefs(configRoot) {
|
|
45819
45716
|
const p = installedPluginsPathForConfig(configRoot);
|
|
45820
|
-
if (!(0,
|
|
45717
|
+
if (!(0, import_node_fs48.existsSync)(p)) return [];
|
|
45821
45718
|
try {
|
|
45822
|
-
return installedPluginPaths((0,
|
|
45719
|
+
return installedPluginPaths((0, import_node_fs48.readFileSync)(p, "utf8"));
|
|
45823
45720
|
} catch {
|
|
45824
45721
|
return null;
|
|
45825
45722
|
}
|
|
45826
45723
|
}
|
|
45827
45724
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
45828
45725
|
return {
|
|
45829
|
-
exists: (p) => (0,
|
|
45830
|
-
listVersionDirs: (root) => (0,
|
|
45726
|
+
exists: (p) => (0, import_node_fs48.existsSync)(p),
|
|
45727
|
+
listVersionDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
45831
45728
|
dirBytes,
|
|
45832
|
-
listStagingDirs: (root) => (0,
|
|
45729
|
+
listStagingDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
45833
45730
|
try {
|
|
45834
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
45731
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path46.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs48.statSync)(p).mtimeMs) };
|
|
45835
45732
|
} catch {
|
|
45836
45733
|
return { name: d.name, mtimeMs: Date.now() };
|
|
45837
45734
|
}
|
|
@@ -45845,10 +45742,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
45845
45742
|
return {
|
|
45846
45743
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
45847
45744
|
mtimeMs: (name) => {
|
|
45848
|
-
const p = (0,
|
|
45849
|
-
if (!(0,
|
|
45745
|
+
const p = (0, import_node_path46.join)(stagingRoot, name);
|
|
45746
|
+
if (!(0, import_node_fs48.existsSync)(p)) return null;
|
|
45850
45747
|
try {
|
|
45851
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
45748
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs48.statSync)(q).mtimeMs);
|
|
45852
45749
|
} catch {
|
|
45853
45750
|
return null;
|
|
45854
45751
|
}
|
|
@@ -45868,13 +45765,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
45868
45765
|
return;
|
|
45869
45766
|
}
|
|
45870
45767
|
const plan = buildPluginCachePlan(
|
|
45871
|
-
(0,
|
|
45768
|
+
(0, import_node_os21.homedir)(),
|
|
45872
45769
|
running,
|
|
45873
45770
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
45874
45771
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
45875
45772
|
);
|
|
45876
45773
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
45877
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
45774
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs48.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
45878
45775
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
45879
45776
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
45880
45777
|
else console.log(renderPluginCachePlan(plan, result));
|