@mutmutco/cli 3.76.0 → 3.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +304 -151
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3416,7 +3416,7 @@ var program = new Command();
|
|
|
3416
3416
|
|
|
3417
3417
|
// src/index.ts
|
|
3418
3418
|
var import_promises10 = require("node:fs/promises");
|
|
3419
|
-
var
|
|
3419
|
+
var import_node_fs34 = require("node:fs");
|
|
3420
3420
|
var import_node_child_process15 = require("node:child_process");
|
|
3421
3421
|
|
|
3422
3422
|
// src/cli-shared.ts
|
|
@@ -6602,7 +6602,7 @@ function commandLadderHint() {
|
|
|
6602
6602
|
}
|
|
6603
6603
|
|
|
6604
6604
|
// src/index.ts
|
|
6605
|
-
var
|
|
6605
|
+
var import_node_path33 = require("node:path");
|
|
6606
6606
|
|
|
6607
6607
|
// src/merge-ci-policy.ts
|
|
6608
6608
|
function resolveMergeCiPolicy(input) {
|
|
@@ -14866,7 +14866,8 @@ function trainPlan(command, options = {}) {
|
|
|
14866
14866
|
{ label: "fold the version bump into the release commit (Hub: full distribution set; app repos: root package manifest) \u2014 runs inside the apply step, no separate bump PR", gated: true },
|
|
14867
14867
|
{ label: "tag release and publish GitHub Release", gated: true },
|
|
14868
14868
|
{ label: "trigger the repo deploy path from the release event", command: "hub-serverless: deploy.yml + publish.yml auto-fire on the release; registry-publish: own publish.yml auto-fires, watched on that repo, never a central dispatch (#2428); other models deploy via their own workflow", gated: true },
|
|
14869
|
-
{ label: "roll development forward", gated: true }
|
|
14869
|
+
{ label: "roll development forward", gated: true },
|
|
14870
|
+
{ label: "synchronize the owning Project short description + thin README from the repo docs and current member repos", command: "mmi-cli org project sync-info --apply", gated: true }
|
|
14870
14871
|
];
|
|
14871
14872
|
}
|
|
14872
14873
|
if (options.dev) {
|
|
@@ -14881,7 +14882,8 @@ function trainPlan(command, options = {}) {
|
|
|
14881
14882
|
{ label: "tag release and publish GitHub Release", gated: true },
|
|
14882
14883
|
{ label: "trigger the deploy path for this repo model, returning Hub Actions run id/url data (and, with --watch, its outcome)", command: "tenant-container: gh workflow run tenant-deploy.yml ... then gh run list/watch", gated: true },
|
|
14883
14884
|
{ label: "retire the rc runtime (rc is ephemeral \u2014 non-fatal, reported as rcRetirement)", command: "mmi-cli runtime tenant control <owner/repo> rc retire", gated: true },
|
|
14884
|
-
{ label: "roll development forward and align rc to the released main", gated: true }
|
|
14885
|
+
{ label: "roll development forward and align rc to the released main", gated: true },
|
|
14886
|
+
{ label: "synchronize the owning Project short description + thin README from the repo docs and current member repos", command: "mmi-cli org project sync-info --apply", gated: true }
|
|
14885
14887
|
];
|
|
14886
14888
|
}
|
|
14887
14889
|
return [
|
|
@@ -14894,7 +14896,8 @@ function trainPlan(command, options = {}) {
|
|
|
14894
14896
|
{ label: "fold the version bump into the release commit (app repos: root package manifest) \u2014 runs inside the apply step, no separate bump PR", gated: true },
|
|
14895
14897
|
{ label: "tag release and publish GitHub Release", gated: true },
|
|
14896
14898
|
{ label: "trigger the deploy path for this repo model, returning Hub Actions run id/url data (and, with --watch, its outcome)", command: "tenant-container: gh workflow run tenant-deploy.yml ... then gh run list/watch; hub-serverless: no manual dispatch, deploy.yml + publish.yml auto-fire on the release, correlate/watch those runs; registry-publish: no manual dispatch, own publish.yml auto-fires on the release, correlate/watch that run on the product repo (#2428)", gated: true },
|
|
14897
|
-
{ label: "roll development forward", gated: true }
|
|
14899
|
+
{ label: "roll development forward", gated: true },
|
|
14900
|
+
{ label: "synchronize the owning Project short description + thin README from the repo docs and current member repos", command: "mmi-cli org project sync-info --apply", gated: true }
|
|
14898
14901
|
];
|
|
14899
14902
|
}
|
|
14900
14903
|
return [
|
|
@@ -18623,6 +18626,101 @@ function docsAuditStatus(fetch2, opts) {
|
|
|
18623
18626
|
return { ok: true, state: "clean", line: `docs audit: ${opts.repo} ${verdict.outcome} (${verdict.date}, ${verdict.checkerVendor})` };
|
|
18624
18627
|
}
|
|
18625
18628
|
|
|
18629
|
+
// src/project-info-sync.ts
|
|
18630
|
+
var import_node_fs22 = require("node:fs");
|
|
18631
|
+
var import_node_path20 = require("node:path");
|
|
18632
|
+
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
18633
|
+
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
18634
|
+
projectV2 { id }
|
|
18635
|
+
}
|
|
18636
|
+
}`;
|
|
18637
|
+
function shortDescriptionFromReadme(markdown) {
|
|
18638
|
+
const lines = markdown.replace(/\r/g, "").split("\n");
|
|
18639
|
+
const h1 = lines.findIndex((line) => /^#\s+\S/.test(line.trim()));
|
|
18640
|
+
const paragraph = [];
|
|
18641
|
+
for (const raw of lines.slice(h1 >= 0 ? h1 + 1 : 0)) {
|
|
18642
|
+
const line = raw.trim();
|
|
18643
|
+
if (!line) {
|
|
18644
|
+
if (paragraph.length) break;
|
|
18645
|
+
continue;
|
|
18646
|
+
}
|
|
18647
|
+
if (/^(?:#|<!--|!\[|\[!\[|<img\b)/i.test(line)) {
|
|
18648
|
+
if (paragraph.length) break;
|
|
18649
|
+
continue;
|
|
18650
|
+
}
|
|
18651
|
+
paragraph.push(line);
|
|
18652
|
+
}
|
|
18653
|
+
const text = paragraph.join(" ").replace(/!\[[^\]]*]\([^)]*\)/g, "").replace(/\[([^\]]+)]\([^)]*\)/g, "$1").replace(/[*_`~]|<[^>]+>/g, "").replace(/\s+/g, " ").trim();
|
|
18654
|
+
if (!text) throw new Error("org project sync-info: README.md has no reader-facing description below its H1");
|
|
18655
|
+
const sentence = text.match(/^.*?[.!?](?=\s|$)/)?.[0] ?? text;
|
|
18656
|
+
return sentence.length <= 240 ? sentence : `${sentence.slice(0, 237).trimEnd()}...`;
|
|
18657
|
+
}
|
|
18658
|
+
function entriesFor(project2, projects) {
|
|
18659
|
+
return project2.projectId ? projects.filter((entry) => entry.projectId === project2.projectId) : [project2];
|
|
18660
|
+
}
|
|
18661
|
+
function branchFor(repo, projects) {
|
|
18662
|
+
const entry = projects.find((p) => (p.repos ?? []).some((r) => r.toLowerCase() === repo.toLowerCase()));
|
|
18663
|
+
return typeof entry?.branch === "string" && entry.branch.trim() ? entry.branch.trim() : entry?.releaseTrack === "trunk" || entry?.class === "content" ? "main" : "development";
|
|
18664
|
+
}
|
|
18665
|
+
function sharedName(entries, fallback) {
|
|
18666
|
+
const names = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
18667
|
+
if (names.length <= 1) return names[0] ?? fallback;
|
|
18668
|
+
let prefix = names[0];
|
|
18669
|
+
for (const name of names.slice(1)) {
|
|
18670
|
+
while (prefix && !name.toLowerCase().startsWith(prefix.toLowerCase())) prefix = prefix.slice(0, -1);
|
|
18671
|
+
}
|
|
18672
|
+
return prefix.replace(/[-_\s]+$/, "") || fallback;
|
|
18673
|
+
}
|
|
18674
|
+
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
18675
|
+
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
18676
|
+
const readmePath = (0, import_node_path20.join)(repoRoot2, "README.md");
|
|
18677
|
+
if (!(0, import_node_fs22.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
18678
|
+
const entries = entriesFor(project2, projects);
|
|
18679
|
+
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
18680
|
+
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
18681
|
+
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
18682
|
+
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
18683
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs22.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
18684
|
+
const lines = [
|
|
18685
|
+
`# ${projectName}`,
|
|
18686
|
+
"",
|
|
18687
|
+
shortDescription,
|
|
18688
|
+
"",
|
|
18689
|
+
"## Member repos",
|
|
18690
|
+
"",
|
|
18691
|
+
...memberRepos.map((repo) => {
|
|
18692
|
+
const entry = projects.find((p) => (p.repos ?? []).some((r) => r.toLowerCase() === repo.toLowerCase()));
|
|
18693
|
+
const name = entry?.name?.trim() || repo.split("/")[1];
|
|
18694
|
+
const base = `https://github.com/${repo}`;
|
|
18695
|
+
const branch = branchFor(repo, projects);
|
|
18696
|
+
return `- [${name}](${base}) \u2014 [README](${base}/blob/${branch}/README.md) \xB7 [architecture](${base}/blob/${branch}/architecture.md)`;
|
|
18697
|
+
})
|
|
18698
|
+
];
|
|
18699
|
+
const targetBase = `https://github.com/${targetRepo2}`;
|
|
18700
|
+
const targetBranch = branchFor(targetRepo2, projects);
|
|
18701
|
+
const orgDocs = [
|
|
18702
|
+
(0, import_node_fs22.existsSync)((0, import_node_path20.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
18703
|
+
(0, import_node_fs22.existsSync)((0, import_node_path20.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
18704
|
+
].filter(Boolean);
|
|
18705
|
+
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
18706
|
+
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
18707
|
+
` };
|
|
18708
|
+
}
|
|
18709
|
+
async function syncProjectInfo(plan, client, apply) {
|
|
18710
|
+
if (apply) {
|
|
18711
|
+
await client.graphql(UPDATE_PROJECT_INFO, {
|
|
18712
|
+
projectId: plan.projectId,
|
|
18713
|
+
shortDescription: plan.shortDescription,
|
|
18714
|
+
readme: plan.readme
|
|
18715
|
+
});
|
|
18716
|
+
}
|
|
18717
|
+
return {
|
|
18718
|
+
...plan,
|
|
18719
|
+
applied: apply,
|
|
18720
|
+
note: apply ? `Project ${plan.projectName} information synchronized` : `Project ${plan.projectName} information would be synchronized (dry-run; pass --apply)`
|
|
18721
|
+
};
|
|
18722
|
+
}
|
|
18723
|
+
|
|
18626
18724
|
// src/oauth.ts
|
|
18627
18725
|
var DEFAULT_DOMAINS = ["mutatismutandis.co", "mutmut.co"];
|
|
18628
18726
|
var DEFAULT_CALLBACK_PATH = "/api/auth/callback";
|
|
@@ -19461,8 +19559,8 @@ function writeError(res) {
|
|
|
19461
19559
|
}
|
|
19462
19560
|
|
|
19463
19561
|
// src/secrets-commands.ts
|
|
19464
|
-
var
|
|
19465
|
-
var
|
|
19562
|
+
var import_node_fs23 = require("node:fs");
|
|
19563
|
+
var import_node_path21 = require("node:path");
|
|
19466
19564
|
var import_node_os8 = require("node:os");
|
|
19467
19565
|
|
|
19468
19566
|
// src/project-runtime.ts
|
|
@@ -19586,18 +19684,18 @@ function collectMap(value, previous = []) {
|
|
|
19586
19684
|
return [...previous, value];
|
|
19587
19685
|
}
|
|
19588
19686
|
async function decryptRailsCredentials(input) {
|
|
19589
|
-
const appDir = (0,
|
|
19687
|
+
const appDir = (0, import_node_path21.resolve)(input.appDir ?? process.cwd());
|
|
19590
19688
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
19591
19689
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
19592
|
-
const credentialsPath = (0,
|
|
19593
|
-
const masterKeyPath = (0,
|
|
19690
|
+
const credentialsPath = (0, import_node_path21.resolve)(appDir, credentialsFile);
|
|
19691
|
+
const masterKeyPath = (0, import_node_path21.resolve)(appDir, masterKeyFile);
|
|
19594
19692
|
const env = {
|
|
19595
19693
|
...process.env,
|
|
19596
19694
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
19597
19695
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
19598
19696
|
};
|
|
19599
|
-
if ((0,
|
|
19600
|
-
env.RAILS_MASTER_KEY = (0,
|
|
19697
|
+
if ((0, import_node_fs23.existsSync)(masterKeyPath)) {
|
|
19698
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs23.readFileSync)(masterKeyPath, "utf8").trim();
|
|
19601
19699
|
}
|
|
19602
19700
|
const script = [
|
|
19603
19701
|
'require "json"',
|
|
@@ -19607,9 +19705,9 @@ async function decryptRailsCredentials(input) {
|
|
|
19607
19705
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
19608
19706
|
"puts JSON.generate(config.config)"
|
|
19609
19707
|
].join("\n");
|
|
19610
|
-
const scriptDir = (0,
|
|
19611
|
-
const scriptPath = (0,
|
|
19612
|
-
(0,
|
|
19708
|
+
const scriptDir = (0, import_node_fs23.mkdtempSync)((0, import_node_path21.join)((0, import_node_os8.tmpdir)(), "mmi-rails-decrypt-"));
|
|
19709
|
+
const scriptPath = (0, import_node_path21.join)(scriptDir, "decrypt.rb");
|
|
19710
|
+
(0, import_node_fs23.writeFileSync)(scriptPath, script, "utf8");
|
|
19613
19711
|
try {
|
|
19614
19712
|
const args = ["exec", "ruby", scriptPath];
|
|
19615
19713
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -19621,7 +19719,7 @@ async function decryptRailsCredentials(input) {
|
|
|
19621
19719
|
});
|
|
19622
19720
|
return JSON.parse(stdout);
|
|
19623
19721
|
} finally {
|
|
19624
|
-
(0,
|
|
19722
|
+
(0, import_node_fs23.rmSync)(scriptDir, { recursive: true, force: true });
|
|
19625
19723
|
}
|
|
19626
19724
|
}
|
|
19627
19725
|
async function readSecretStdin() {
|
|
@@ -19711,7 +19809,7 @@ function registerSecretsCommands(program3) {
|
|
|
19711
19809
|
let body;
|
|
19712
19810
|
if (o.file) {
|
|
19713
19811
|
try {
|
|
19714
|
-
body = (0,
|
|
19812
|
+
body = (0, import_node_fs23.readFileSync)((0, import_node_path21.resolve)(o.file), "utf8");
|
|
19715
19813
|
} catch (e) {
|
|
19716
19814
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
19717
19815
|
}
|
|
@@ -19816,7 +19914,7 @@ function registerSecretsCommands(program3) {
|
|
|
19816
19914
|
{
|
|
19817
19915
|
...d,
|
|
19818
19916
|
decryptRailsCredentials,
|
|
19819
|
-
removeFile: (path2) => (0,
|
|
19917
|
+
removeFile: (path2) => (0, import_node_fs23.unlinkSync)((0, import_node_path21.resolve)(o.appDir ?? process.cwd(), path2))
|
|
19820
19918
|
},
|
|
19821
19919
|
{
|
|
19822
19920
|
repo: o.repo,
|
|
@@ -20010,7 +20108,7 @@ function checkGithubPools(probe) {
|
|
|
20010
20108
|
}
|
|
20011
20109
|
|
|
20012
20110
|
// src/box-commands.ts
|
|
20013
|
-
var
|
|
20111
|
+
var import_node_fs24 = require("node:fs");
|
|
20014
20112
|
|
|
20015
20113
|
// src/box.ts
|
|
20016
20114
|
var BOX_KEYS = {
|
|
@@ -20213,7 +20311,7 @@ function registerBoxCommands(program3) {
|
|
|
20213
20311
|
}
|
|
20214
20312
|
if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
|
|
20215
20313
|
else if (o.ssh && o.script) {
|
|
20216
|
-
(0,
|
|
20314
|
+
(0, import_node_fs24.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
|
|
20217
20315
|
console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
|
|
20218
20316
|
} else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
20219
20317
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
@@ -20905,7 +21003,7 @@ function registerSchedulesCommands(program3) {
|
|
|
20905
21003
|
|
|
20906
21004
|
// src/file-lock.ts
|
|
20907
21005
|
var import_promises4 = require("node:fs/promises");
|
|
20908
|
-
var
|
|
21006
|
+
var import_node_path22 = require("node:path");
|
|
20909
21007
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
20910
21008
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
20911
21009
|
var FileLockBusyError = class extends Error {
|
|
@@ -20990,7 +21088,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
20990
21088
|
}
|
|
20991
21089
|
async function withFileLock(lockPath, opts, fn) {
|
|
20992
21090
|
const resolved = resolveFileLockOpts(opts);
|
|
20993
|
-
await (0, import_promises4.mkdir)((0,
|
|
21091
|
+
await (0, import_promises4.mkdir)((0, import_node_path22.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
20994
21092
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
20995
21093
|
try {
|
|
20996
21094
|
return await fn();
|
|
@@ -21001,7 +21099,7 @@ async function withFileLock(lockPath, opts, fn) {
|
|
|
21001
21099
|
|
|
21002
21100
|
// src/schedules-lift-command.ts
|
|
21003
21101
|
var import_promises5 = require("node:fs/promises");
|
|
21004
|
-
var
|
|
21102
|
+
var import_node_path23 = require("node:path");
|
|
21005
21103
|
|
|
21006
21104
|
// src/schedules-lift.ts
|
|
21007
21105
|
var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
|
|
@@ -21107,7 +21205,7 @@ async function readWorkflowFiles(dir) {
|
|
|
21107
21205
|
const files = [];
|
|
21108
21206
|
for (const name of names.sort()) {
|
|
21109
21207
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
21110
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0,
|
|
21208
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path23.join)(dir, name), "utf8") });
|
|
21111
21209
|
}
|
|
21112
21210
|
return files;
|
|
21113
21211
|
}
|
|
@@ -21649,9 +21747,9 @@ function registerQueryCommands(program3) {
|
|
|
21649
21747
|
}
|
|
21650
21748
|
|
|
21651
21749
|
// src/bootstrap-commands.ts
|
|
21652
|
-
var
|
|
21750
|
+
var import_node_fs25 = require("node:fs");
|
|
21653
21751
|
var import_node_os9 = require("node:os");
|
|
21654
|
-
var
|
|
21752
|
+
var import_node_path24 = require("node:path");
|
|
21655
21753
|
|
|
21656
21754
|
// src/bootstrap-drift.ts
|
|
21657
21755
|
function byteComparableSeeds(manifest, cls) {
|
|
@@ -22265,13 +22363,13 @@ function registerBootstrapCommands(program3) {
|
|
|
22265
22363
|
client: defaultGitHubClient(),
|
|
22266
22364
|
projectMeta: meta,
|
|
22267
22365
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
22268
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
22366
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null,
|
|
22269
22367
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
22270
22368
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
22271
22369
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
22272
22370
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
22273
22371
|
// sanction, which is the pre-#3664 behaviour.
|
|
22274
|
-
sanctionedAdmins: (0,
|
|
22372
|
+
sanctionedAdmins: (0, import_node_fs25.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs25.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
22275
22373
|
requiredGcpApis: (() => {
|
|
22276
22374
|
const v = meta?.requiredGcpApis;
|
|
22277
22375
|
if (Array.isArray(v)) return v;
|
|
@@ -22309,12 +22407,12 @@ function registerBootstrapCommands(program3) {
|
|
|
22309
22407
|
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 () => {
|
|
22310
22408
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
22311
22409
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
22312
|
-
if (!(0,
|
|
22313
|
-
const manifest = loadBootstrapSeeds((0,
|
|
22410
|
+
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`);
|
|
22411
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
|
|
22314
22412
|
const hubContents = /* @__PURE__ */ new Map();
|
|
22315
22413
|
for (const s of manifest.seeds) {
|
|
22316
22414
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
22317
|
-
hubContents.set(s.target, (0,
|
|
22415
|
+
hubContents.set(s.target, (0, import_node_fs25.existsSync)(s.target) ? (0, import_node_fs25.readFileSync)(s.target, "utf8") : null);
|
|
22318
22416
|
}
|
|
22319
22417
|
let targets;
|
|
22320
22418
|
let classOf = (_repo) => "deployable";
|
|
@@ -22391,8 +22489,8 @@ function registerBootstrapCommands(program3) {
|
|
|
22391
22489
|
return fail(`bootstrap apply: ${e.message}`);
|
|
22392
22490
|
}
|
|
22393
22491
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
22394
|
-
if (!(0,
|
|
22395
|
-
const manifest = loadBootstrapSeeds((0,
|
|
22492
|
+
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`);
|
|
22493
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
|
|
22396
22494
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
22397
22495
|
const slug = parsedRepo.slug;
|
|
22398
22496
|
const onlyTarget = o.only.trim();
|
|
@@ -22403,16 +22501,16 @@ function registerBootstrapCommands(program3) {
|
|
|
22403
22501
|
${known}`);
|
|
22404
22502
|
}
|
|
22405
22503
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
22406
|
-
const readFile9 = (p) => (0,
|
|
22504
|
+
const readFile9 = (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null;
|
|
22407
22505
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
22408
22506
|
const putSeed = async (target, content, ref, sha) => {
|
|
22409
|
-
const tmp = (0,
|
|
22410
|
-
(0,
|
|
22507
|
+
const tmp = (0, import_node_path24.join)((0, import_node_os9.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
22508
|
+
(0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
22411
22509
|
try {
|
|
22412
22510
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
22413
22511
|
} finally {
|
|
22414
22512
|
try {
|
|
22415
|
-
(0,
|
|
22513
|
+
(0, import_node_fs25.unlinkSync)(tmp);
|
|
22416
22514
|
} catch {
|
|
22417
22515
|
}
|
|
22418
22516
|
}
|
|
@@ -22664,12 +22762,12 @@ LIVE apply to ${repo}:
|
|
|
22664
22762
|
}
|
|
22665
22763
|
|
|
22666
22764
|
// src/stage-commands.ts
|
|
22667
|
-
var
|
|
22668
|
-
var
|
|
22765
|
+
var import_node_fs27 = require("node:fs");
|
|
22766
|
+
var import_node_path26 = require("node:path");
|
|
22669
22767
|
|
|
22670
22768
|
// src/port-registry.ts
|
|
22671
|
-
var
|
|
22672
|
-
var
|
|
22769
|
+
var import_node_fs26 = require("node:fs");
|
|
22770
|
+
var import_node_path25 = require("node:path");
|
|
22673
22771
|
|
|
22674
22772
|
// ../infra/port-geometry.mjs
|
|
22675
22773
|
var PORT_BLOCK = 100;
|
|
@@ -22683,8 +22781,8 @@ function nextPortBlock(registry2) {
|
|
|
22683
22781
|
return [base, base + PORT_SPAN];
|
|
22684
22782
|
}
|
|
22685
22783
|
function loadPortRegistry(path2) {
|
|
22686
|
-
if (!(0,
|
|
22687
|
-
const raw = JSON.parse((0,
|
|
22784
|
+
if (!(0, import_node_fs26.existsSync)(path2)) return {};
|
|
22785
|
+
const raw = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
22688
22786
|
const out = {};
|
|
22689
22787
|
for (const [key, value] of Object.entries(raw)) {
|
|
22690
22788
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -22698,9 +22796,9 @@ function ensurePortRange(repo, path2) {
|
|
|
22698
22796
|
const existing = registry2[repo];
|
|
22699
22797
|
if (existing) return existing;
|
|
22700
22798
|
const range = nextPortBlock(registry2);
|
|
22701
|
-
const raw = (0,
|
|
22799
|
+
const raw = (0, import_node_fs26.existsSync)(path2) ? JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8")) : {};
|
|
22702
22800
|
raw[repo] = range;
|
|
22703
|
-
(0,
|
|
22801
|
+
(0, import_node_fs26.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
22704
22802
|
return range;
|
|
22705
22803
|
}
|
|
22706
22804
|
function portCursorSeed(registry2) {
|
|
@@ -22722,22 +22820,22 @@ function existingPortRange(repo, registry2) {
|
|
|
22722
22820
|
return registry2[repo] ?? null;
|
|
22723
22821
|
}
|
|
22724
22822
|
function portRangeInfraAt(root, source) {
|
|
22725
|
-
const registryPath = (0,
|
|
22726
|
-
const ddbScriptPath = (0,
|
|
22727
|
-
if (!(0,
|
|
22823
|
+
const registryPath = (0, import_node_path25.join)(root, "infra", "port-ranges.json");
|
|
22824
|
+
const ddbScriptPath = (0, import_node_path25.join)(root, "infra", "port-ddb.mjs");
|
|
22825
|
+
if (!(0, import_node_fs26.existsSync)(registryPath) || !(0, import_node_fs26.existsSync)(ddbScriptPath)) return null;
|
|
22728
22826
|
return { root, source, registryPath, ddbScriptPath };
|
|
22729
22827
|
}
|
|
22730
22828
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
22731
22829
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
22732
22830
|
if (direct) return direct;
|
|
22733
|
-
for (let dir = cwd; ; dir = (0,
|
|
22734
|
-
const sibling = portRangeInfraAt((0,
|
|
22831
|
+
for (let dir = cwd; ; dir = (0, import_node_path25.dirname)(dir)) {
|
|
22832
|
+
const sibling = portRangeInfraAt((0, import_node_path25.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
22735
22833
|
if (sibling) return sibling;
|
|
22736
|
-
const parent = (0,
|
|
22834
|
+
const parent = (0, import_node_path25.dirname)(dir);
|
|
22737
22835
|
if (parent === dir) break;
|
|
22738
22836
|
}
|
|
22739
22837
|
if (packageDir) {
|
|
22740
|
-
const pkgRoot = (0,
|
|
22838
|
+
const pkgRoot = (0, import_node_path25.join)(packageDir, "..", "..");
|
|
22741
22839
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
22742
22840
|
if (pkgFrom) return pkgFrom;
|
|
22743
22841
|
}
|
|
@@ -22913,8 +23011,8 @@ function registerStageCommands(program3) {
|
|
|
22913
23011
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
22914
23012
|
return decideStage({
|
|
22915
23013
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
22916
|
-
hasCompose: (0,
|
|
22917
|
-
hasEnvExample: (0,
|
|
23014
|
+
hasCompose: (0, import_node_fs27.existsSync)((0, import_node_path26.join)(process.cwd(), "docker-compose.yml")),
|
|
23015
|
+
hasEnvExample: (0, import_node_fs27.existsSync)((0, import_node_path26.join)(process.cwd(), ".env.example"))
|
|
22918
23016
|
});
|
|
22919
23017
|
}
|
|
22920
23018
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -23352,9 +23450,9 @@ function registerBoardCommands(program3) {
|
|
|
23352
23450
|
}
|
|
23353
23451
|
|
|
23354
23452
|
// src/merge-cleanup.ts
|
|
23355
|
-
var
|
|
23453
|
+
var import_node_fs28 = require("node:fs");
|
|
23356
23454
|
var import_promises7 = require("node:fs/promises");
|
|
23357
|
-
var
|
|
23455
|
+
var import_node_path28 = require("node:path");
|
|
23358
23456
|
var import_node_os10 = require("node:os");
|
|
23359
23457
|
var import_node_child_process13 = require("node:child_process");
|
|
23360
23458
|
|
|
@@ -23442,7 +23540,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
23442
23540
|
|
|
23443
23541
|
// src/deferred-registry-store.ts
|
|
23444
23542
|
var import_promises6 = require("node:fs/promises");
|
|
23445
|
-
var
|
|
23543
|
+
var import_node_path27 = require("node:path");
|
|
23446
23544
|
var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
23447
23545
|
async function atomicWrite(target, contents) {
|
|
23448
23546
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -23493,12 +23591,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
23493
23591
|
},
|
|
23494
23592
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
23495
23593
|
write: async (entries) => {
|
|
23496
|
-
await (0, import_promises6.mkdir)((0,
|
|
23594
|
+
await (0, import_promises6.mkdir)((0, import_node_path27.dirname)(registryPath), { recursive: true });
|
|
23497
23595
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
23498
23596
|
},
|
|
23499
23597
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
23500
23598
|
update: async (mutate) => {
|
|
23501
|
-
await (0, import_promises6.mkdir)((0,
|
|
23599
|
+
await (0, import_promises6.mkdir)((0, import_node_path27.dirname)(registryPath), { recursive: true });
|
|
23502
23600
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
23503
23601
|
for (; ; ) {
|
|
23504
23602
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -23659,7 +23757,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
23659
23757
|
);
|
|
23660
23758
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
23661
23759
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
23662
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
23760
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path28.dirname)((0, import_node_path28.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
23663
23761
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
23664
23762
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
23665
23763
|
const removalNow = Date.now();
|
|
@@ -23690,7 +23788,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
23690
23788
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
23691
23789
|
beforeWorktrees,
|
|
23692
23790
|
startingPath: branch.worktreePath,
|
|
23693
|
-
pathExists: (p) => (0,
|
|
23791
|
+
pathExists: (p) => (0, import_node_fs28.existsSync)(p),
|
|
23694
23792
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
23695
23793
|
teardownWorktreeStage,
|
|
23696
23794
|
deferredStore,
|
|
@@ -23718,7 +23816,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
23718
23816
|
for (const wt of worktreeDirsToRemove) {
|
|
23719
23817
|
try {
|
|
23720
23818
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
23721
|
-
realpath: (path2) => (0,
|
|
23819
|
+
realpath: (path2) => (0, import_node_fs28.realpathSync)(path2)
|
|
23722
23820
|
});
|
|
23723
23821
|
if (!cleanupTarget.ok) {
|
|
23724
23822
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -23783,13 +23881,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
23783
23881
|
const commits = JSON.parse(raw).commits ?? [];
|
|
23784
23882
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
23785
23883
|
if (!body) return void 0;
|
|
23786
|
-
const dir = (0,
|
|
23787
|
-
const path2 = (0,
|
|
23788
|
-
(0,
|
|
23884
|
+
const dir = (0, import_node_fs28.mkdtempSync)((0, import_node_path28.join)((0, import_node_os10.tmpdir)(), "mmi-squash-body-"));
|
|
23885
|
+
const path2 = (0, import_node_path28.join)(dir, "body.txt");
|
|
23886
|
+
(0, import_node_fs28.writeFileSync)(path2, `${body}
|
|
23789
23887
|
`, "utf8");
|
|
23790
23888
|
return { path: path2, cleanup: () => {
|
|
23791
23889
|
try {
|
|
23792
|
-
(0,
|
|
23890
|
+
(0, import_node_fs28.rmSync)(dir, { recursive: true, force: true });
|
|
23793
23891
|
} catch {
|
|
23794
23892
|
}
|
|
23795
23893
|
} };
|
|
@@ -23911,13 +24009,13 @@ var realWorktreeDirRemover = {
|
|
|
23911
24009
|
probe: (p) => {
|
|
23912
24010
|
let st;
|
|
23913
24011
|
try {
|
|
23914
|
-
st = (0,
|
|
24012
|
+
st = (0, import_node_fs28.lstatSync)(p);
|
|
23915
24013
|
} catch {
|
|
23916
24014
|
return null;
|
|
23917
24015
|
}
|
|
23918
24016
|
if (st.isSymbolicLink()) return "link";
|
|
23919
24017
|
try {
|
|
23920
|
-
(0,
|
|
24018
|
+
(0, import_node_fs28.readlinkSync)(p);
|
|
23921
24019
|
return "link";
|
|
23922
24020
|
} catch {
|
|
23923
24021
|
}
|
|
@@ -23925,7 +24023,7 @@ var realWorktreeDirRemover = {
|
|
|
23925
24023
|
},
|
|
23926
24024
|
readdir: (p) => {
|
|
23927
24025
|
try {
|
|
23928
|
-
return (0,
|
|
24026
|
+
return (0, import_node_fs28.readdirSync)(p);
|
|
23929
24027
|
} catch {
|
|
23930
24028
|
return [];
|
|
23931
24029
|
}
|
|
@@ -23934,9 +24032,9 @@ var realWorktreeDirRemover = {
|
|
|
23934
24032
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
23935
24033
|
detachLink: (p) => {
|
|
23936
24034
|
try {
|
|
23937
|
-
(0,
|
|
24035
|
+
(0, import_node_fs28.rmdirSync)(p);
|
|
23938
24036
|
} catch {
|
|
23939
|
-
(0,
|
|
24037
|
+
(0, import_node_fs28.unlinkSync)(p);
|
|
23940
24038
|
}
|
|
23941
24039
|
},
|
|
23942
24040
|
removeTree: (p) => (0, import_promises7.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -23969,9 +24067,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
23969
24067
|
}
|
|
23970
24068
|
}
|
|
23971
24069
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
23972
|
-
if (!(0,
|
|
24070
|
+
if (!(0, import_node_fs28.existsSync)(statePath)) return false;
|
|
23973
24071
|
try {
|
|
23974
|
-
const state = JSON.parse((0,
|
|
24072
|
+
const state = JSON.parse((0, import_node_fs28.readFileSync)(statePath, "utf8"));
|
|
23975
24073
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
23976
24074
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
23977
24075
|
} catch {
|
|
@@ -24178,9 +24276,9 @@ async function fetchRestCorePool(gh = defaultGhApi) {
|
|
|
24178
24276
|
}
|
|
24179
24277
|
|
|
24180
24278
|
// src/worktree-lifecycle-commands.ts
|
|
24181
|
-
var
|
|
24279
|
+
var import_node_fs29 = require("node:fs");
|
|
24182
24280
|
var import_promises8 = require("node:fs/promises");
|
|
24183
|
-
var
|
|
24281
|
+
var import_node_path29 = require("node:path");
|
|
24184
24282
|
var GH_TIMEOUT_MS = 2e4;
|
|
24185
24283
|
var DEFAULT_BASE = "origin/development";
|
|
24186
24284
|
var DEFAULT_REMOTE = "origin";
|
|
@@ -24316,7 +24414,7 @@ function classifyStaleLeaks(input) {
|
|
|
24316
24414
|
var defaultOrphanDirScanDeps = {
|
|
24317
24415
|
listDirs: (root) => {
|
|
24318
24416
|
try {
|
|
24319
|
-
return (0,
|
|
24417
|
+
return (0, import_node_fs29.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path29.join)(root, e.name));
|
|
24320
24418
|
} catch {
|
|
24321
24419
|
return [];
|
|
24322
24420
|
}
|
|
@@ -24463,13 +24561,13 @@ function registerWorktreeCommands(program3) {
|
|
|
24463
24561
|
const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
|
|
24464
24562
|
const branch = headBorn ? (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS })).stdout.trim() : (await execFileP2("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
24465
24563
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
24466
|
-
const gitFile = (0,
|
|
24467
|
-
const isLinked = (0,
|
|
24564
|
+
const gitFile = (0, import_node_path29.join)(wtPath, ".git");
|
|
24565
|
+
const isLinked = (0, import_node_fs29.existsSync)(gitFile) && (0, import_node_fs29.statSync)(gitFile).isFile();
|
|
24468
24566
|
if (apply && !isLinked) {
|
|
24469
24567
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
24470
24568
|
}
|
|
24471
24569
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
24472
|
-
const primaryCheckout = commonDir ? (0,
|
|
24570
|
+
const primaryCheckout = commonDir ? (0, import_node_path29.dirname)(commonDir) : wtPath;
|
|
24473
24571
|
const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
|
|
24474
24572
|
const orphan = classifyOrphanedWorktree({
|
|
24475
24573
|
branch,
|
|
@@ -24656,10 +24754,10 @@ async function gatherWorktreeContext() {
|
|
|
24656
24754
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
24657
24755
|
}
|
|
24658
24756
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
24659
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
24757
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path29.dirname)((0, import_node_path29.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
24660
24758
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
24661
24759
|
let orphanDirs = [];
|
|
24662
|
-
if ((0,
|
|
24760
|
+
if ((0, import_node_fs29.existsSync)(wtRoot)) {
|
|
24663
24761
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
24664
24762
|
...defaultOrphanDirScanDeps,
|
|
24665
24763
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
@@ -24682,7 +24780,7 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
|
|
|
24682
24780
|
}
|
|
24683
24781
|
|
|
24684
24782
|
// src/issue-commands.ts
|
|
24685
|
-
var
|
|
24783
|
+
var import_node_fs30 = require("node:fs");
|
|
24686
24784
|
var import_node_crypto5 = require("node:crypto");
|
|
24687
24785
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
24688
24786
|
var ReparentConflictError = class extends Error {
|
|
@@ -24700,7 +24798,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
24700
24798
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
24701
24799
|
const patch = {};
|
|
24702
24800
|
let bodyChanged = false;
|
|
24703
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
24801
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs30.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
24704
24802
|
if (options.titleFile !== void 0) {
|
|
24705
24803
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
24706
24804
|
} else if (options.title !== void 0) {
|
|
@@ -25263,7 +25361,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
25263
25361
|
if (opts.batch) {
|
|
25264
25362
|
let specs;
|
|
25265
25363
|
try {
|
|
25266
|
-
const raw = (0,
|
|
25364
|
+
const raw = (0, import_node_fs30.readFileSync)(opts.batch, "utf8");
|
|
25267
25365
|
specs = JSON.parse(raw);
|
|
25268
25366
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
25269
25367
|
} catch (e) {
|
|
@@ -25337,8 +25435,8 @@ ${lines}`, {
|
|
|
25337
25435
|
}
|
|
25338
25436
|
|
|
25339
25437
|
// src/train-commands.ts
|
|
25340
|
-
var
|
|
25341
|
-
var
|
|
25438
|
+
var import_node_fs31 = require("node:fs");
|
|
25439
|
+
var import_node_path30 = require("node:path");
|
|
25342
25440
|
|
|
25343
25441
|
// src/train-status.ts
|
|
25344
25442
|
function buildTrainStatusReport(input) {
|
|
@@ -25378,7 +25476,7 @@ function formatTrainStatus(r) {
|
|
|
25378
25476
|
// src/train-commands.ts
|
|
25379
25477
|
function readRepoVersion() {
|
|
25380
25478
|
try {
|
|
25381
|
-
return JSON.parse((0,
|
|
25479
|
+
return JSON.parse((0, import_node_fs31.readFileSync)((0, import_node_path30.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
25382
25480
|
} catch {
|
|
25383
25481
|
return void 0;
|
|
25384
25482
|
}
|
|
@@ -25524,9 +25622,9 @@ function registerDeployCommands(program3) {
|
|
|
25524
25622
|
}
|
|
25525
25623
|
|
|
25526
25624
|
// src/discovery-commands.ts
|
|
25527
|
-
var
|
|
25625
|
+
var import_node_fs32 = require("node:fs");
|
|
25528
25626
|
var import_node_os11 = require("node:os");
|
|
25529
|
-
var
|
|
25627
|
+
var import_node_path31 = require("node:path");
|
|
25530
25628
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
25531
25629
|
async function collectStatus() {
|
|
25532
25630
|
let branch = "";
|
|
@@ -25703,8 +25801,8 @@ async function collectOnboardStatus() {
|
|
|
25703
25801
|
}
|
|
25704
25802
|
const home = (0, import_node_os11.homedir)();
|
|
25705
25803
|
const plugin = onboardPluginGate({
|
|
25706
|
-
readKnown: () => readFileSyncSafe((0,
|
|
25707
|
-
readSettings: () => readFileSyncSafe((0,
|
|
25804
|
+
readKnown: () => readFileSyncSafe((0, import_node_path31.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs32.readFileSync),
|
|
25805
|
+
readSettings: () => readFileSyncSafe((0, import_node_path31.join)(home, ".claude", "settings.json"), import_node_fs32.readFileSync)
|
|
25708
25806
|
});
|
|
25709
25807
|
return { track, board, registry: registry2, secrets, plugin, nextCommand };
|
|
25710
25808
|
}
|
|
@@ -27216,17 +27314,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
27216
27314
|
}
|
|
27217
27315
|
function ghHostsConfigPath(env, platform2) {
|
|
27218
27316
|
const sep2 = platform2 === "win32" ? "\\" : "/";
|
|
27219
|
-
const
|
|
27317
|
+
const join28 = (...parts) => parts.join(sep2);
|
|
27220
27318
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
27221
|
-
if (explicit) return
|
|
27319
|
+
if (explicit) return join28(explicit, "hosts.yml");
|
|
27222
27320
|
if (platform2 === "win32") {
|
|
27223
27321
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
27224
|
-
return appData ?
|
|
27322
|
+
return appData ? join28(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
27225
27323
|
}
|
|
27226
27324
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
27227
|
-
if (xdg) return
|
|
27325
|
+
if (xdg) return join28(xdg, "gh", "hosts.yml");
|
|
27228
27326
|
const home = env.HOME?.trim();
|
|
27229
|
-
return home ?
|
|
27327
|
+
return home ? join28(home, ".config", "gh", "hosts.yml") : void 0;
|
|
27230
27328
|
}
|
|
27231
27329
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
27232
27330
|
let hostIndent = null;
|
|
@@ -27276,9 +27374,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
27276
27374
|
}
|
|
27277
27375
|
|
|
27278
27376
|
// src/doctor-io.ts
|
|
27279
|
-
var
|
|
27377
|
+
var import_node_fs33 = require("node:fs");
|
|
27280
27378
|
var import_node_os12 = require("node:os");
|
|
27281
|
-
var
|
|
27379
|
+
var import_node_path32 = require("node:path");
|
|
27282
27380
|
var import_node_child_process14 = require("node:child_process");
|
|
27283
27381
|
var import_node_util8 = require("node:util");
|
|
27284
27382
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process14.execFile);
|
|
@@ -27286,7 +27384,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
27286
27384
|
function installedClaudePluginVersion() {
|
|
27287
27385
|
try {
|
|
27288
27386
|
const file = JSON.parse(
|
|
27289
|
-
(0,
|
|
27387
|
+
(0, import_node_fs33.readFileSync)((0, import_node_path32.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
27290
27388
|
);
|
|
27291
27389
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
27292
27390
|
if (versions.length === 0) return void 0;
|
|
@@ -27326,13 +27424,13 @@ function worktreeRootSync() {
|
|
|
27326
27424
|
}
|
|
27327
27425
|
var gitignorePath = () => {
|
|
27328
27426
|
const root = worktreeRootSync();
|
|
27329
|
-
return root === null ? null : (0,
|
|
27427
|
+
return root === null ? null : (0, import_node_path32.join)(root, ".gitignore");
|
|
27330
27428
|
};
|
|
27331
27429
|
function readGitignore() {
|
|
27332
27430
|
const path2 = gitignorePath();
|
|
27333
27431
|
if (path2 === null) return null;
|
|
27334
27432
|
try {
|
|
27335
|
-
return (0,
|
|
27433
|
+
return (0, import_node_fs33.readFileSync)(path2, "utf8");
|
|
27336
27434
|
} catch {
|
|
27337
27435
|
return null;
|
|
27338
27436
|
}
|
|
@@ -27341,7 +27439,7 @@ function writeGitignore(content) {
|
|
|
27341
27439
|
const path2 = gitignorePath();
|
|
27342
27440
|
if (path2 === null) return false;
|
|
27343
27441
|
try {
|
|
27344
|
-
(0,
|
|
27442
|
+
(0, import_node_fs33.writeFileSync)(path2, content, "utf8");
|
|
27345
27443
|
return true;
|
|
27346
27444
|
} catch {
|
|
27347
27445
|
return false;
|
|
@@ -27365,7 +27463,7 @@ async function repoRoot() {
|
|
|
27365
27463
|
}
|
|
27366
27464
|
function hasRepoLocalWorktrees() {
|
|
27367
27465
|
const root = worktreeRootSync();
|
|
27368
|
-
return root !== null && (0,
|
|
27466
|
+
return root !== null && (0, import_node_fs33.existsSync)((0, import_node_path32.join)(root, ".worktrees"));
|
|
27369
27467
|
}
|
|
27370
27468
|
|
|
27371
27469
|
// src/index.ts
|
|
@@ -27401,8 +27499,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
27401
27499
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
27402
27500
|
try {
|
|
27403
27501
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
27404
|
-
if (!hostsPath || !(0,
|
|
27405
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
27502
|
+
if (!hostsPath || !(0, import_node_fs34.existsSync)(hostsPath)) return void 0;
|
|
27503
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs34.readFileSync)(hostsPath, "utf8")));
|
|
27406
27504
|
} catch {
|
|
27407
27505
|
return void 0;
|
|
27408
27506
|
}
|
|
@@ -27410,7 +27508,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
27410
27508
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
27411
27509
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
27412
27510
|
function envHealLockPath(home) {
|
|
27413
|
-
return (0,
|
|
27511
|
+
return (0, import_node_path33.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
27414
27512
|
}
|
|
27415
27513
|
async function withEnvHealLock(what, run) {
|
|
27416
27514
|
try {
|
|
@@ -27574,8 +27672,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
27574
27672
|
const home = (0, import_node_os13.homedir)();
|
|
27575
27673
|
return marketplaceRows(
|
|
27576
27674
|
MMI_MARKETPLACE_NAME,
|
|
27577
|
-
readFileSyncSafe((0,
|
|
27578
|
-
readFileSyncSafe((0,
|
|
27675
|
+
readFileSyncSafe((0, import_node_path33.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs34.readFileSync),
|
|
27676
|
+
readFileSyncSafe((0, import_node_path33.join)(home, ".claude", "settings.json"), import_node_fs34.readFileSync)
|
|
27579
27677
|
);
|
|
27580
27678
|
} catch {
|
|
27581
27679
|
return [];
|
|
@@ -27781,19 +27879,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
27781
27879
|
});
|
|
27782
27880
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
27783
27881
|
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) => {
|
|
27784
|
-
const path2 = (0,
|
|
27785
|
-
const current = (0,
|
|
27882
|
+
const path2 = (0, import_node_path33.join)(process.cwd(), ".gitignore");
|
|
27883
|
+
const current = (0, import_node_fs34.existsSync)(path2) ? (0, import_node_fs34.readFileSync)(path2, "utf8") : null;
|
|
27786
27884
|
const plan = planManagedGitignore(current);
|
|
27787
27885
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
27788
27886
|
if (opts.json) {
|
|
27789
|
-
if (opts.write && plan.changed) (0,
|
|
27887
|
+
if (opts.write && plan.changed) (0, import_node_fs34.writeFileSync)(path2, plan.content, "utf8");
|
|
27790
27888
|
console.log(JSON.stringify(plan, null, 2));
|
|
27791
27889
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
27792
27890
|
return;
|
|
27793
27891
|
}
|
|
27794
27892
|
if (opts.write) {
|
|
27795
27893
|
if (plan.changed) {
|
|
27796
|
-
(0,
|
|
27894
|
+
(0, import_node_fs34.writeFileSync)(path2, plan.content, "utf8");
|
|
27797
27895
|
console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
|
|
27798
27896
|
} else {
|
|
27799
27897
|
console.log("mmi-cli org rules gitignore: up to date");
|
|
@@ -27948,8 +28046,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
27948
28046
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
27949
28047
|
let root;
|
|
27950
28048
|
if (o.root !== void 0) {
|
|
27951
|
-
root = (0,
|
|
27952
|
-
if (!(0,
|
|
28049
|
+
root = (0, import_node_path33.resolve)(o.root);
|
|
28050
|
+
if (!(0, import_node_fs34.existsSync)(root) || !(0, import_node_fs34.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
27953
28051
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
27954
28052
|
if (isPathUnderDirectory(gcRepoRoot, root)) {
|
|
27955
28053
|
return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
|
|
@@ -28014,7 +28112,7 @@ async function primaryCheckoutRoot(from) {
|
|
|
28014
28112
|
return primaryCheckoutRootOf(async (args) => (await execFileP2("git", ["-C", from, ...args], { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
28015
28113
|
}
|
|
28016
28114
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
28017
|
-
if (!(0,
|
|
28115
|
+
if (!(0, import_node_fs34.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
28018
28116
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
28019
28117
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
28020
28118
|
if (!registered.length) {
|
|
@@ -28035,26 +28133,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
28035
28133
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
28036
28134
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
28037
28135
|
const take = () => {
|
|
28038
|
-
const fd = (0,
|
|
28136
|
+
const fd = (0, import_node_fs34.openSync)(lockPath, "wx");
|
|
28039
28137
|
try {
|
|
28040
|
-
(0,
|
|
28138
|
+
(0, import_node_fs34.writeSync)(fd, String(Date.now()));
|
|
28041
28139
|
} finally {
|
|
28042
|
-
(0,
|
|
28140
|
+
(0, import_node_fs34.closeSync)(fd);
|
|
28043
28141
|
}
|
|
28044
28142
|
return () => {
|
|
28045
28143
|
try {
|
|
28046
|
-
(0,
|
|
28144
|
+
(0, import_node_fs34.rmSync)(lockPath, { force: true });
|
|
28047
28145
|
} catch {
|
|
28048
28146
|
}
|
|
28049
28147
|
};
|
|
28050
28148
|
};
|
|
28051
28149
|
try {
|
|
28052
|
-
(0,
|
|
28150
|
+
(0, import_node_fs34.mkdirSync)((0, import_node_path33.dirname)(lockPath), { recursive: true });
|
|
28053
28151
|
return take();
|
|
28054
28152
|
} catch {
|
|
28055
28153
|
try {
|
|
28056
|
-
if (Date.now() - (0,
|
|
28057
|
-
(0,
|
|
28154
|
+
if (Date.now() - (0, import_node_fs34.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
28155
|
+
(0, import_node_fs34.rmSync)(lockPath, { force: true });
|
|
28058
28156
|
return take();
|
|
28059
28157
|
}
|
|
28060
28158
|
} catch {
|
|
@@ -28520,6 +28618,34 @@ var project = program2.command("project").description("the DDB org registry \u20
|
|
|
28520
28618
|
async function projectTarget(commandName, explicitTarget) {
|
|
28521
28619
|
return requireProjectTarget(commandName, explicitTarget, explicitTarget ? void 0 : await resolveRepo());
|
|
28522
28620
|
}
|
|
28621
|
+
async function runProjectInfoSync(target, apply) {
|
|
28622
|
+
const currentRepo = await resolveRepo();
|
|
28623
|
+
if (!currentRepo) throw new Error("org project sync-info: run from the repository whose Project information is being synchronized");
|
|
28624
|
+
if (target.toLowerCase() !== currentRepo.toLowerCase() && slugOf(target) !== slugOf(currentRepo)) {
|
|
28625
|
+
throw new Error(`org project sync-info: ${target} is not the current checkout (${currentRepo}); run from the target repository`);
|
|
28626
|
+
}
|
|
28627
|
+
const targetRepo2 = currentRepo;
|
|
28628
|
+
const cfg = await loadConfig();
|
|
28629
|
+
const registry2 = registryClientDeps(cfg);
|
|
28630
|
+
const [read, projects] = await Promise.all([
|
|
28631
|
+
fetchProjectBySlugChecked(slugOf(targetRepo2), registry2),
|
|
28632
|
+
fetchProjectsList(registry2)
|
|
28633
|
+
]);
|
|
28634
|
+
if (!read.ok) throw new Error(`org project sync-info: Hub registry read failed (${read.error})`);
|
|
28635
|
+
if (!read.project) throw new Error(`org project sync-info: no registry META for ${targetRepo2}`);
|
|
28636
|
+
if (!projects) throw new Error("org project sync-info: Hub project list unavailable");
|
|
28637
|
+
if (apply) {
|
|
28638
|
+
const authority = await fetchTrainAuthority(targetRepo2, registry2);
|
|
28639
|
+
if (!authority.ok) throw new Error(`org project sync-info: train authority unverified (${authority.error})`);
|
|
28640
|
+
if (!authority.authority.train) {
|
|
28641
|
+
throw new Error(`org project sync-info: ${authority.authority.login} has no train authority for ${targetRepo2}`);
|
|
28642
|
+
}
|
|
28643
|
+
}
|
|
28644
|
+
const repoRoot2 = await gitOut(["rev-parse", "--show-toplevel"]);
|
|
28645
|
+
if (!repoRoot2) throw new Error("org project sync-info: cannot resolve the current repository root");
|
|
28646
|
+
const plan = buildProjectInfoSyncPlan(targetRepo2, read.project, projects, repoRoot2);
|
|
28647
|
+
return syncProjectInfo(plan, defaultGitHubClient(), apply);
|
|
28648
|
+
}
|
|
28523
28649
|
project.command("list").description("list all projects (identity + board, never deploy coords)").option("--json", "machine-readable output").action(async (o) => {
|
|
28524
28650
|
const cfg = await loadConfig();
|
|
28525
28651
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -28560,6 +28686,18 @@ deploys run centrally (tenant-deploy.yml); product repos carry no deploy files.
|
|
|
28560
28686
|
);
|
|
28561
28687
|
}
|
|
28562
28688
|
});
|
|
28689
|
+
project.command("sync-info [owner/repo]").description("synchronize the owning GitHub Project's short description + thin README from this repo's README and the registry's current member repos; dry-run by default").option("--apply", "write the synchronized Project information (train-authority gated)").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
28690
|
+
let target;
|
|
28691
|
+
try {
|
|
28692
|
+
target = await projectTarget("org project sync-info", repoOrSlug);
|
|
28693
|
+
const result = await runProjectInfoSync(target, Boolean(o.apply));
|
|
28694
|
+
if (o.json) return printLine(JSON.stringify(result, null, 2));
|
|
28695
|
+
printLine(`org project sync-info: ${result.note}`);
|
|
28696
|
+
printLine(` members: ${result.memberRepos.join(", ")}`);
|
|
28697
|
+
} catch (e) {
|
|
28698
|
+
return failGraceful(e.message);
|
|
28699
|
+
}
|
|
28700
|
+
});
|
|
28563
28701
|
var projectDeploy = project.command("deploy").description("read nonsecret DEPLOY# facts (domain, port, deploy path, substrate, host presence)");
|
|
28564
28702
|
projectDeploy.command("get [owner/repo]").description("read nonsecret DEPLOY# facts for one project; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").choices(["dev", "rc", "main"])).option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
28565
28703
|
const cfg = await loadConfig();
|
|
@@ -28592,7 +28730,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
28592
28730
|
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`);
|
|
28593
28731
|
if (o.secretsFile) {
|
|
28594
28732
|
try {
|
|
28595
|
-
vars.push(`secrets=${(0,
|
|
28733
|
+
vars.push(`secrets=${(0, import_node_fs34.readFileSync)(o.secretsFile, "utf8")}`);
|
|
28596
28734
|
} catch (e) {
|
|
28597
28735
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
28598
28736
|
}
|
|
@@ -29328,11 +29466,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
29328
29466
|
}
|
|
29329
29467
|
});
|
|
29330
29468
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
29331
|
-
const wfDir = (0,
|
|
29332
|
-
if (!(0,
|
|
29333
|
-
return (0,
|
|
29469
|
+
const wfDir = (0, import_node_path33.join)(cwd, ".github", "workflows");
|
|
29470
|
+
if (!(0, import_node_fs34.existsSync)(wfDir)) return [];
|
|
29471
|
+
return (0, import_node_fs34.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
29334
29472
|
try {
|
|
29335
|
-
return workflowReportsPrChecks((0,
|
|
29473
|
+
return workflowReportsPrChecks((0, import_node_fs34.readFileSync)((0, import_node_path33.join)(wfDir, name), "utf8"));
|
|
29336
29474
|
} catch {
|
|
29337
29475
|
return true;
|
|
29338
29476
|
}
|
|
@@ -29364,16 +29502,16 @@ function ciAuditDeps() {
|
|
|
29364
29502
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
29365
29503
|
readSeedFile: (path2) => {
|
|
29366
29504
|
if (!root) return null;
|
|
29367
|
-
const fullPath = (0,
|
|
29368
|
-
return (0,
|
|
29505
|
+
const fullPath = (0, import_node_path33.join)(root, path2);
|
|
29506
|
+
return (0, import_node_fs34.existsSync)(fullPath) ? (0, import_node_fs34.readFileSync)(fullPath, "utf8") : null;
|
|
29369
29507
|
}
|
|
29370
29508
|
};
|
|
29371
29509
|
}
|
|
29372
29510
|
function hubRoot() {
|
|
29373
|
-
const fromPkg = (0,
|
|
29511
|
+
const fromPkg = (0, import_node_path33.join)(__dirname, "..", "..");
|
|
29374
29512
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
29375
|
-
if ((0,
|
|
29376
|
-
if ((0,
|
|
29513
|
+
if ((0, import_node_fs34.existsSync)((0, import_node_path33.join)(fromPkg, marker))) return fromPkg;
|
|
29514
|
+
if ((0, import_node_fs34.existsSync)((0, import_node_path33.join)(process.cwd(), marker))) return process.cwd();
|
|
29377
29515
|
return null;
|
|
29378
29516
|
}
|
|
29379
29517
|
pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
|
|
@@ -29676,7 +29814,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
29676
29814
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
29677
29815
|
beforeWorktrees,
|
|
29678
29816
|
startingPath,
|
|
29679
|
-
pathExists: (p) => (0,
|
|
29817
|
+
pathExists: (p) => (0, import_node_fs34.existsSync)(p),
|
|
29680
29818
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
29681
29819
|
teardownWorktreeStage,
|
|
29682
29820
|
deferredStore,
|
|
@@ -29842,6 +29980,9 @@ function renderTrainApply(commandName, r) {
|
|
|
29842
29980
|
if (r.checkout) {
|
|
29843
29981
|
base = `${base}; checkout: ${r.checkout.note}`;
|
|
29844
29982
|
}
|
|
29983
|
+
if (r.projectInfoSync) {
|
|
29984
|
+
base = `${base}; project info: ${r.projectInfoSync.note}`;
|
|
29985
|
+
}
|
|
29845
29986
|
return r.announceNote ? `${base}; announce: ${r.announceNote}` : base;
|
|
29846
29987
|
}
|
|
29847
29988
|
function renderTenantRedeploy(r) {
|
|
@@ -29910,7 +30051,19 @@ for (const commandName of ["rcand", "release"]) {
|
|
|
29910
30051
|
try {
|
|
29911
30052
|
const ack = (o.ack ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
29912
30053
|
const result = await runTrainApply(commandName, trainApplyDeps(), { watch: o.watch, announceSummaryFile: o.announceSummaryFile, ack, dev: o.dev });
|
|
29913
|
-
|
|
30054
|
+
let projectInfoSync;
|
|
30055
|
+
if (commandName === "release") {
|
|
30056
|
+
try {
|
|
30057
|
+
projectInfoSync = await runProjectInfoSync(result.repo, true);
|
|
30058
|
+
} catch (e) {
|
|
30059
|
+
const error = e.message;
|
|
30060
|
+
projectInfoSync = { applied: false, note: `FAILED \u2014 ${error}`, error };
|
|
30061
|
+
}
|
|
30062
|
+
}
|
|
30063
|
+
const reported = { ...result, ...projectInfoSync ? { projectInfoSync } : {} };
|
|
30064
|
+
printLine(o.json ? JSON.stringify(reported, null, 2) : renderTrainApply(commandName, reported));
|
|
30065
|
+
if (projectInfoSync && "error" in projectInfoSync) process.exitCode = 1;
|
|
30066
|
+
return;
|
|
29914
30067
|
} catch (e) {
|
|
29915
30068
|
return failGraceful(`${commandName}: ${e.message}`);
|
|
29916
30069
|
}
|
|
@@ -30063,12 +30216,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
30063
30216
|
targets = resolution.targets;
|
|
30064
30217
|
}
|
|
30065
30218
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
30066
|
-
const fileMatrix = (0,
|
|
30219
|
+
const fileMatrix = (0, import_node_fs34.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs34.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
30067
30220
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
30068
30221
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
30069
|
-
const fileContracts = (0,
|
|
30222
|
+
const fileContracts = (0, import_node_fs34.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs34.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
30070
30223
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
30071
|
-
const sanctioned = (0,
|
|
30224
|
+
const sanctioned = (0, import_node_fs34.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs34.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
30072
30225
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
30073
30226
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
30074
30227
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -30101,16 +30254,16 @@ function directoryBytes(path2) {
|
|
|
30101
30254
|
let total = 0;
|
|
30102
30255
|
let entries;
|
|
30103
30256
|
try {
|
|
30104
|
-
entries = (0,
|
|
30257
|
+
entries = (0, import_node_fs34.readdirSync)(path2, { withFileTypes: true });
|
|
30105
30258
|
} catch {
|
|
30106
30259
|
return 0;
|
|
30107
30260
|
}
|
|
30108
30261
|
for (const entry of entries) {
|
|
30109
|
-
const child2 = (0,
|
|
30262
|
+
const child2 = (0, import_node_path33.join)(path2, entry.name);
|
|
30110
30263
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
30111
30264
|
else {
|
|
30112
30265
|
try {
|
|
30113
|
-
total += (0,
|
|
30266
|
+
total += (0, import_node_fs34.statSync)(child2).size;
|
|
30114
30267
|
} catch {
|
|
30115
30268
|
}
|
|
30116
30269
|
}
|
|
@@ -30118,25 +30271,25 @@ function directoryBytes(path2) {
|
|
|
30118
30271
|
return total;
|
|
30119
30272
|
}
|
|
30120
30273
|
function listDirEntries(dir) {
|
|
30121
|
-
return (0,
|
|
30274
|
+
return (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
30122
30275
|
}
|
|
30123
30276
|
function readInstalledPluginRefs(configRoot) {
|
|
30124
30277
|
const p = installedPluginsPathForConfig(configRoot);
|
|
30125
|
-
if (!(0,
|
|
30278
|
+
if (!(0, import_node_fs34.existsSync)(p)) return [];
|
|
30126
30279
|
try {
|
|
30127
|
-
return installedPluginPaths((0,
|
|
30280
|
+
return installedPluginPaths((0, import_node_fs34.readFileSync)(p, "utf8"));
|
|
30128
30281
|
} catch {
|
|
30129
30282
|
return null;
|
|
30130
30283
|
}
|
|
30131
30284
|
}
|
|
30132
30285
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
30133
30286
|
return {
|
|
30134
|
-
exists: (p) => (0,
|
|
30135
|
-
listVersionDirs: (root) => (0,
|
|
30287
|
+
exists: (p) => (0, import_node_fs34.existsSync)(p),
|
|
30288
|
+
listVersionDirs: (root) => (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
30136
30289
|
dirBytes,
|
|
30137
|
-
listStagingDirs: (root) => (0,
|
|
30290
|
+
listStagingDirs: (root) => (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
30138
30291
|
try {
|
|
30139
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
30292
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path33.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs34.statSync)(p).mtimeMs) };
|
|
30140
30293
|
} catch {
|
|
30141
30294
|
return { name: d.name, mtimeMs: Date.now() };
|
|
30142
30295
|
}
|
|
@@ -30150,10 +30303,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
30150
30303
|
return {
|
|
30151
30304
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
30152
30305
|
mtimeMs: (name) => {
|
|
30153
|
-
const p = (0,
|
|
30154
|
-
if (!(0,
|
|
30306
|
+
const p = (0, import_node_path33.join)(stagingRoot, name);
|
|
30307
|
+
if (!(0, import_node_fs34.existsSync)(p)) return null;
|
|
30155
30308
|
try {
|
|
30156
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
30309
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs34.statSync)(q).mtimeMs);
|
|
30157
30310
|
} catch {
|
|
30158
30311
|
return null;
|
|
30159
30312
|
}
|
|
@@ -30179,7 +30332,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
30179
30332
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
30180
30333
|
);
|
|
30181
30334
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
30182
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
30335
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs34.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
30183
30336
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
30184
30337
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
30185
30338
|
else console.log(renderPluginCachePlan(plan, result));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.77.0",
|
|
4
4
|
"description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the MMI plugin's skills and gates drive on Claude, Codex, and Kimi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|