@mutmutco/cli 4.3.57 → 4.3.59

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.
Files changed (2) hide show
  1. package/dist/main.cjs +105 -264
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -6046,6 +6046,15 @@ var REGISTRY_FETCH_TIMEOUT_MS = 8e3;
6046
6046
  init_fetch_retry();
6047
6047
  init_client_version();
6048
6048
 
6049
+ // ../shared/schedule-inventory.ts
6050
+ function classifyLlmValue(raw) {
6051
+ const value = raw.trim().toLowerCase();
6052
+ if (/^no\b|^none\b/.test(value)) return "no";
6053
+ if (/^yes\b/.test(value)) return "yes";
6054
+ if (/^embeddings\b/.test(value)) return "embeddings";
6055
+ return "unknown";
6056
+ }
6057
+
6049
6058
  // src/workflow-context.ts
6050
6059
  function parseWorkflowJobs(yaml) {
6051
6060
  const lines2 = yaml.split(/\r?\n/);
@@ -6324,25 +6333,10 @@ function resolvedLabel(entry, now) {
6324
6333
  var ORG = "mutmutco";
6325
6334
  var DOC_START_MARKER = "<!-- schedules:inventory:start -->";
6326
6335
  var DOC_END_MARKER = "<!-- schedules:inventory:end -->";
6327
- function isJervResource(name) {
6328
- return /^jerv-memory(?:-|$)/i.test(name);
6329
- }
6330
6336
  function llmFromHeader(yamlText) {
6331
6337
  const m = /^#\s*llm:\s*(.+)$/im.exec(yamlText);
6332
6338
  return m ? classifyLlmValue(m[1]) : "unknown";
6333
6339
  }
6334
- function classifyLlmValue(raw) {
6335
- const value = raw.trim().toLowerCase();
6336
- if (/^no\b|^none\b/.test(value)) return "no";
6337
- if (/^yes\b/.test(value)) return "yes";
6338
- if (/^embeddings\b/.test(value)) return "embeddings";
6339
- return "unknown";
6340
- }
6341
- function readLlmDeclaration(text) {
6342
- if (!text) return "unknown";
6343
- const m = /\bllm:\s*(.+)/i.exec(text);
6344
- return m ? classifyLlmValue(m[1]) : "unknown";
6345
- }
6346
6340
  function extractWorkflowCrons(yamlText) {
6347
6341
  const crons = [];
6348
6342
  for (const rawLine of yamlText.split("\n")) {
@@ -6366,126 +6360,6 @@ function workflowEntry(repo, workflowPath, yamlText) {
6366
6360
  harbourUnmanaged: isHarbourUnmanaged(yamlText)
6367
6361
  };
6368
6362
  }
6369
- function awsRuleEntries(payload) {
6370
- if (!payload || typeof payload !== "object") {
6371
- throw new Error("aws events list-rules answered a non-object payload \u2014 refusing to read it as zero rules");
6372
- }
6373
- const { Rules: rules } = payload;
6374
- if (!Array.isArray(rules)) {
6375
- throw new Error("aws events list-rules answered no Rules array \u2014 refusing to read it as zero rules");
6376
- }
6377
- const entries = [];
6378
- for (const node of rules) {
6379
- if (!node || typeof node !== "object") {
6380
- throw new Error("aws events list-rules returned a non-object rule \u2014 refusing to read it as no rule");
6381
- }
6382
- const { Name, ScheduleExpression, State, Description } = node;
6383
- if (typeof Name !== "string") {
6384
- throw new Error("aws events list-rules returned a rule with no Name string \u2014 refusing to read it as no rule");
6385
- }
6386
- if (typeof ScheduleExpression !== "string") continue;
6387
- if (State !== "ENABLED" || isJervResource(Name)) continue;
6388
- entries.push({
6389
- name: Name,
6390
- cadence: ScheduleExpression,
6391
- executor: "aws-eventbridge-rule",
6392
- // An AWS rule declares `llm:` in its own Description (the shared convention) — a rule with no
6393
- // Description reads `unknown`, and stack-internal plumbing like the Lambda warmer should say
6394
- // `llm: no` there to go green.
6395
- llm: readLlmDeclaration(typeof Description === "string" ? Description : void 0),
6396
- resolved: "live",
6397
- source: `aws events rule ${Name} (eu-central-1)`
6398
- });
6399
- }
6400
- return entries;
6401
- }
6402
- function lambdaTargetFromArn(arn) {
6403
- const marker = ":function:";
6404
- const i = arn.indexOf(marker);
6405
- if (i < 0) return { functionName: arn.split(":").pop() ?? "" };
6406
- const rest = arn.slice(i + marker.length);
6407
- const q = rest.indexOf(":");
6408
- if (q < 0) return { functionName: rest };
6409
- return { functionName: rest.slice(0, q), qualifier: rest.slice(q + 1) };
6410
- }
6411
- function awsScheduleEntry(payload) {
6412
- if (!payload || typeof payload !== "object") {
6413
- throw new Error("aws scheduler get-schedule answered a non-object payload \u2014 refusing to read it as no schedule");
6414
- }
6415
- const { Name, ScheduleExpression, State, Target, Description, CreationDate } = payload;
6416
- if (typeof Name !== "string" || typeof ScheduleExpression !== "string") {
6417
- throw new Error("aws scheduler get-schedule answered no Name/ScheduleExpression strings \u2014 refusing to read it as no schedule");
6418
- }
6419
- if (State !== "ENABLED" || isJervResource(Name)) return null;
6420
- let target = "";
6421
- let qualifier;
6422
- let scheduleId;
6423
- if (Target && typeof Target === "object") {
6424
- const { Arn, Input } = Target;
6425
- if (typeof Arn === "string") {
6426
- const parsed = lambdaTargetFromArn(Arn);
6427
- target = parsed.functionName;
6428
- qualifier = parsed.qualifier;
6429
- }
6430
- if (typeof Input === "string") {
6431
- try {
6432
- const parsed = JSON.parse(Input);
6433
- if (typeof parsed?.scheduleId === "string" && parsed.scheduleId) scheduleId = parsed.scheduleId;
6434
- } catch {
6435
- }
6436
- }
6437
- }
6438
- const sourceBase = `aws scheduler schedule ${Name} (eu-central-1)`;
6439
- const source = qualifier && target ? `${sourceBase} \u2192 ${target}:${qualifier}` : sourceBase;
6440
- const armedAt = armedAtFromCreationDate(CreationDate);
6441
- return {
6442
- name: Name,
6443
- cadence: ScheduleExpression,
6444
- executor: target ? `aws-scheduler \u2192 ${target}` : "aws-scheduler",
6445
- llm: readLlmDeclaration(typeof Description === "string" ? Description : void 0),
6446
- resolved: "live",
6447
- source,
6448
- ...scheduleId ? { scheduleId } : {},
6449
- ...armedAt ? { armedAt } : {}
6450
- };
6451
- }
6452
- function armedAtFromCreationDate(creationDate) {
6453
- if (creationDate instanceof Date) {
6454
- return Number.isNaN(creationDate.getTime()) ? void 0 : creationDate.toISOString().slice(0, 10);
6455
- }
6456
- if (typeof creationDate === "string" && creationDate.trim()) {
6457
- const parsed = Date.parse(creationDate);
6458
- return Number.isNaN(parsed) ? void 0 : new Date(parsed).toISOString().slice(0, 10);
6459
- }
6460
- if (typeof creationDate === "number" && Number.isFinite(creationDate)) {
6461
- const ms = creationDate > 1e12 ? creationDate : creationDate * 1e3;
6462
- const date = new Date(ms);
6463
- return Number.isNaN(date.getTime()) ? void 0 : date.toISOString().slice(0, 10);
6464
- }
6465
- return void 0;
6466
- }
6467
- function awsScheduleRefs(payload) {
6468
- if (!payload || typeof payload !== "object") {
6469
- throw new Error("aws scheduler list-schedules answered a non-object payload \u2014 refusing to read it as zero schedules");
6470
- }
6471
- const { Schedules: schedules } = payload;
6472
- if (!Array.isArray(schedules)) {
6473
- throw new Error("aws scheduler list-schedules answered no Schedules array \u2014 refusing to read it as zero schedules");
6474
- }
6475
- const refs = [];
6476
- for (const s of schedules) {
6477
- if (!s || typeof s !== "object") {
6478
- throw new Error("aws scheduler list-schedules returned a non-object schedule \u2014 refusing to read it as no schedule");
6479
- }
6480
- const { Name, GroupName } = s;
6481
- if (typeof Name !== "string") {
6482
- throw new Error("aws scheduler list-schedules returned a schedule with no Name string \u2014 refusing to read it as no schedule");
6483
- }
6484
- if (isJervResource(Name)) continue;
6485
- refs.push(typeof GroupName === "string" && GroupName !== "default" && GroupName !== "" ? { name: Name, group: GroupName } : { name: Name });
6486
- }
6487
- return refs;
6488
- }
6489
6363
  function decorateAwsModel(entries, registry2, verdicts) {
6490
6364
  if (!registry2 || registry2.length === 0) return [...entries];
6491
6365
  const rowById = new Map(registry2.map((r) => [r.id, r]));
@@ -6960,6 +6834,24 @@ async function fetchDeployFactsBySlug(slug, deps) {
6960
6834
  return null;
6961
6835
  }
6962
6836
  }
6837
+ async function fetchScheduleInventory(deps) {
6838
+ try {
6839
+ if (!deps.baseUrl) return null;
6840
+ const token = await deps.token();
6841
+ if (!token) return null;
6842
+ const res = await retriedFetch(deps, `${deps.baseUrl.replace(/\/$/, "")}/schedules/inventory`, {
6843
+ method: "GET",
6844
+ headers: { Authorization: `Bearer ${token}` }
6845
+ });
6846
+ if (!res.ok) return null;
6847
+ const body = await res.json();
6848
+ if (!body || !Array.isArray(body.entries) || !Array.isArray(body.incomplete) || !body.incomplete.every((v) => typeof v === "string") || typeof body.schedulerRead !== "boolean" || !body.schedulerRead && body.incomplete.length === 0) return null;
6849
+ if (!body.entries.every((e) => e && typeof e.name === "string" && typeof e.cadence === "string" && typeof e.executor === "string" && typeof e.source === "string" && e.resolved === "live" && ["yes", "no", "embeddings", "unknown"].includes(e.llm) && (e.scheduleId === void 0 || typeof e.scheduleId === "string") && (e.armedAt === void 0 || typeof e.armedAt === "string"))) return null;
6850
+ return body;
6851
+ } catch {
6852
+ return null;
6853
+ }
6854
+ }
6963
6855
  async function fetchSchedulesList(deps) {
6964
6856
  if (!deps.baseUrl) return null;
6965
6857
  const token = await deps.token();
@@ -7071,6 +6963,9 @@ async function tenantReconcile(payload, deps) {
7071
6963
  async function tenantDeploy(payload, deps) {
7072
6964
  return postJson("/tenant-deploy", payload, deps, "POST", { noRetry: true, timeoutMs: TENANT_DEPLOY_TIMEOUT_MS });
7073
6965
  }
6966
+ async function releaseAnnouncement(payload, deps) {
6967
+ return postJson("/release-announcement", payload, deps, "POST", { noRetry: true, timeoutMs: 3e4 });
6968
+ }
7074
6969
  async function actionsCanary(payload, deps) {
7075
6970
  return postJson("/actions-canary", payload, deps, "POST", { noRetry: true });
7076
6971
  }
@@ -11324,6 +11219,22 @@ async function collectBoardItems(cfg, options, deps) {
11324
11219
  after = void 0;
11325
11220
  }
11326
11221
  } while (after);
11222
+ if (options.activeOnly && nodes.length === 0) {
11223
+ try {
11224
+ const verify = await fetchProjectPage(client, cfg, void 0, void 0);
11225
+ const survivors = (verify.organization?.projectV2?.items.nodes ?? []).filter((node) => !shouldSkipInactiveBoardNode(node, cfg));
11226
+ if (survivors.length) {
11227
+ warnings.push(
11228
+ `partial board read: the active items filter (${ACTIVE_BOARD_ITEMS_FILTER}) returned no rows while ${survivors.length} active item(s) remain on the board \u2014 served by an unfiltered verification scan; GitHub's filtered items query may lag`
11229
+ );
11230
+ partial = true;
11231
+ nodes.push(...survivors);
11232
+ }
11233
+ } catch (e) {
11234
+ warnings.push(`partial board read: the active items filter returned no rows and the unfiltered verification read failed (${e.message}) \u2014 the empty board answer is unverified`);
11235
+ partial = true;
11236
+ }
11237
+ }
11327
11238
  const read = nodesToItems(nodes, warnings, cfg);
11328
11239
  if (read.failures.length) {
11329
11240
  const message2 = `partial board read: ${read.failures.length} item(s) came back truncated and were not read \u2014 ${read.failures.join(" | ")}`;
@@ -15877,10 +15788,10 @@ var rollout_plan_default = {
15877
15788
  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)."
15878
15789
  },
15879
15790
  baseline: {
15880
- version: "4.3.57",
15881
- tag: "v4.3.57",
15882
- commit: "b988569fdfe3",
15883
- npm: "@mutmutco/cli@4.3.57"
15791
+ version: "4.3.59",
15792
+ tag: "v4.3.59",
15793
+ commit: "0f695d53cba6",
15794
+ npm: "@mutmutco/cli@4.3.59"
15884
15795
  },
15885
15796
  exitCriterion: "fleet-n-of-n",
15886
15797
  hubOnlyShortcut: "forbidden",
@@ -15897,14 +15808,14 @@ var rollout_plan_default = {
15897
15808
  repo: "mutmutco/mmi-hub",
15898
15809
  role: "canary",
15899
15810
  schedule: "train",
15900
- v3Target: "v4.3.57"
15811
+ v3Target: "v4.3.59"
15901
15812
  }
15902
15813
  ],
15903
15814
  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.",
15904
15815
  rollback: {
15905
15816
  independent: true,
15906
- mechanism: "npm dist-tag latest -> 4.3.57 and redeploy the Hub Lambda from tag v4.3.57 (b988569fdfe3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15907
- v3Target: "v4.3.57 (@mutmutco/cli@4.3.57, tag commit b988569fdfe3 \u2014 last known-good release carrying the repo-index v4-only contract)"
15817
+ mechanism: "npm dist-tag latest -> 4.3.59 and redeploy the Hub Lambda from tag v4.3.59 (0f695d53cba6); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15818
+ v3Target: "v4.3.59 (@mutmutco/cli@4.3.59, tag commit 0f695d53cba6 \u2014 last known-good release carrying the repo-index v4-only contract)"
15908
15819
  }
15909
15820
  },
15910
15821
  {
@@ -31875,10 +31786,9 @@ var surfaces_default = {
31875
31786
  certification: "Hermes Agent v0.20.1 release v2026.8.13, upstream f80f453ae0679347e38abc917c7f94f717bf96c5 (#5053)"
31876
31787
  },
31877
31788
  enforcementCeilings: [
31878
- "Hermes pre_tool_call blocks only mapped shell and edit/write/patch tool families; disabled plugins enforce nothing.",
31789
+ "MMI ships no Hermes hooks (#5908): the plugin contributes skills and the /mmi command only; enforcement is host-side.",
31879
31790
  "Hermes user plugins are opt-in through plugins.enabled; operator consent and host process isolation remain Hermes-owned.",
31880
- "npm is MMI transport only: MMI-Hub extracts @mutmutco/hermes-plugin into $HERMES_HOME/plugins/mmi and provisions the canonical skills into $HERMES_HOME/skills/mmi/; a fresh Hermes Agent process is required for discovery.",
31881
- "Final assistant output is outside Hermes pre_tool_call hook control."
31791
+ "npm is MMI transport only: MMI-Hub extracts @mutmutco/hermes-plugin into $HERMES_HOME/plugins/mmi and provisions the canonical skills into $HERMES_HOME/skills/mmi/; a fresh Hermes Agent process is required for discovery."
31882
31792
  ]
31883
31793
  }
31884
31794
  ],
@@ -35594,15 +35504,9 @@ function writeError(res) {
35594
35504
 
35595
35505
  // src/schedules-commands.ts
35596
35506
  var import_promises5 = require("node:fs/promises");
35597
- var import_node_child_process11 = require("node:child_process");
35598
- var import_node_util6 = require("node:util");
35599
35507
  init_clean_exit();
35600
35508
  init_github_client();
35601
35509
  init_cli_shared();
35602
- var execFileP4 = (0, import_node_util6.promisify)(import_node_child_process11.execFile);
35603
- var AWS_REGION = "eu-central-1";
35604
- var AWS_TIMEOUT_MS = 3e4;
35605
- var AWS_RETRY_DELAY_MS = 1500;
35606
35510
  async function listWorkflows(client, repo) {
35607
35511
  const all = [];
35608
35512
  for (let page = 1; ; page += 1) {
@@ -35699,44 +35603,11 @@ async function githubEntries(client) {
35699
35603
  drift.push(...reconciliation.map(renderDrift));
35700
35604
  return { entries, incomplete, drift, reconciliation, readRepos, workflowNames: [...workflows.map((w) => w.name), ...unreadableNames], disabledWorkflowNames };
35701
35605
  }
35702
- async function awsJson(args) {
35703
- const run = async () => {
35704
- const { stdout } = await execFileP4("aws", [...args, "--region", AWS_REGION, "--output", "json"], {
35705
- encoding: "utf8",
35706
- windowsHide: true,
35707
- timeout: AWS_TIMEOUT_MS
35708
- });
35709
- return JSON.parse(stdout);
35710
- };
35711
- try {
35712
- return await run();
35713
- } catch {
35714
- await new Promise((resolve7) => setTimeout(resolve7, AWS_RETRY_DELAY_MS));
35715
- return run();
35716
- }
35717
- }
35718
35606
  async function awsEntries() {
35719
- const entries = [];
35720
- const incomplete = [];
35721
- try {
35722
- entries.push(...awsRuleEntries(await awsJson(["events", "list-rules"])));
35723
- } catch (e) {
35724
- incomplete.push(`aws: events list-rules failed \u2014 ${e.message}`);
35725
- }
35726
- let schedulerRead = false;
35727
- try {
35728
- const refs = awsScheduleRefs(await awsJson(["scheduler", "list-schedules"]));
35729
- for (const ref of refs) {
35730
- const args = ["scheduler", "get-schedule", "--name", ref.name, ...ref.group ? ["--group-name", ref.group] : []];
35731
- const entry = awsScheduleEntry(await awsJson(args));
35732
- if (entry) entries.push(entry);
35733
- }
35734
- schedulerRead = true;
35735
- } catch (e) {
35736
- incomplete.push(`aws: scheduler listing failed \u2014 ${e.message}`);
35737
- }
35738
- const reconciliation = unlauncheredLlmDrifts(entries);
35739
- return { entries, incomplete, drift: reconciliation.map(renderDrift), reconciliation, schedulerRead };
35607
+ const inventory = await fetchScheduleInventory(registryClientDeps(await loadConfig()));
35608
+ if (!inventory) return { entries: [], incomplete: ["aws: Hub schedule inventory unavailable or unauthenticated"], drift: [], reconciliation: [], schedulerRead: false };
35609
+ const reconciliation = unlauncheredLlmDrifts(inventory.entries);
35610
+ return { ...inventory, reconciliation, drift: reconciliation.map(renderDrift) };
35740
35611
  }
35741
35612
  async function readOrFail(read) {
35742
35613
  try {
@@ -36593,8 +36464,13 @@ async function resolveStageBuildSecrets(input) {
36593
36464
  out[envKey] = fromVault;
36594
36465
  continue;
36595
36466
  }
36467
+ const fromGitHub = await input.githubToken?.();
36468
+ if (fromGitHub) {
36469
+ out[envKey] = fromGitHub;
36470
+ continue;
36471
+ }
36596
36472
  missing.push(
36597
- `${envKey}=${GITHUB_PACKAGES_TOKEN_REF} (central deploy mints this; local /stage needs process env ${envKey} or a stageless project vault secret ${envKey} \u2014 a GitHub PAT with read:packages for npm.pkg.github.com; declare+set via vault secrets, never commit the value)`
36473
+ `${envKey}=${GITHUB_PACKAGES_TOKEN_REF} (sign in with gh auth login using a package-readable GitHub identity, or provide process env ${envKey} or stageless project vault secret ${envKey}; npm.pkg.github.com still requires package read permission)`
36598
36474
  );
36599
36475
  continue;
36600
36476
  }
@@ -36628,6 +36504,7 @@ async function fetchVaultRef(fetchVault, ref) {
36628
36504
  }
36629
36505
 
36630
36506
  // src/stage-commands.ts
36507
+ init_github_client();
36631
36508
  function registerStageCommands(program3) {
36632
36509
  function stagePortFromArgv() {
36633
36510
  const raw = rawValue("--port", "");
@@ -36687,7 +36564,8 @@ function registerStageCommands(program3) {
36687
36564
  const d = makeSecretsDeps(cfg);
36688
36565
  const merge = await resolveStageBuildSecrets({
36689
36566
  requiredBuildSecrets: required,
36690
- fetchVault: (key, opts) => fetchSecretValue(d, key, opts ?? {})
36567
+ fetchVault: (key, opts) => fetchSecretValue(d, key, opts ?? {}),
36568
+ githubToken
36691
36569
  });
36692
36570
  return Object.keys(merge).length ? merge : void 0;
36693
36571
  }
@@ -37089,7 +36967,7 @@ function renderVerifySecrets(body) {
37089
36967
  }
37090
36968
 
37091
36969
  // src/command-register-collaboration.ts
37092
- var import_node_child_process16 = require("node:child_process");
36970
+ var import_node_child_process15 = require("node:child_process");
37093
36971
  var import_node_fs41 = require("node:fs");
37094
36972
  var import_promises7 = require("node:fs/promises");
37095
36973
  init_clean_exit();
@@ -37475,12 +37353,12 @@ function boardAdvanceFailureMessage(result) {
37475
37353
  }
37476
37354
 
37477
37355
  // src/test-policy-core.ts
37478
- var import_node_child_process13 = require("node:child_process");
37356
+ var import_node_child_process12 = require("node:child_process");
37479
37357
  var import_node_fs35 = require("node:fs");
37480
37358
  var import_node_path33 = require("node:path");
37481
37359
 
37482
37360
  // src/test-command-policy-shared.mjs
37483
- var import_node_child_process12 = require("node:child_process");
37361
+ var import_node_child_process11 = require("node:child_process");
37484
37362
  var TEST_COMMAND_CLASS = "test";
37485
37363
  var TRAILER_KEY = "Test-Policy-Override";
37486
37364
  var OVERRIDE_RE = /^Test-Policy-Override:\s*(.+)$/im;
@@ -37597,7 +37475,7 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true, overrid
37597
37475
  };
37598
37476
  }
37599
37477
  function git(args, cwd) {
37600
- return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
37478
+ return (0, import_node_child_process11.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
37601
37479
  }
37602
37480
  function parseScope(value) {
37603
37481
  const scoped = /^\[([^\]]*)\]\s*([\s\S]*)$/.exec(value);
@@ -38096,14 +37974,14 @@ function evaluate(changed, policy, present = () => false) {
38096
37974
  return findings;
38097
37975
  }
38098
37976
  function git2(args, cwd) {
38099
- return (0, import_node_child_process13.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
37977
+ return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
38100
37978
  }
38101
37979
  var COAUTHOR_KEY = "Co-authored-by";
38102
37980
  var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
38103
37981
  var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
38104
37982
  function parseTrailers(message2, cwd) {
38105
37983
  try {
38106
- return (0, import_node_child_process13.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
37984
+ return (0, import_node_child_process12.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
38107
37985
  windowsHide: true,
38108
37986
  cwd,
38109
37987
  input: message2,
@@ -38859,7 +38737,7 @@ function postMergeReconWarnings(input) {
38859
38737
  }
38860
38738
 
38861
38739
  // src/review-verdict.ts
38862
- var import_node_child_process14 = require("node:child_process");
38740
+ var import_node_child_process13 = require("node:child_process");
38863
38741
  var import_node_fs38 = require("node:fs");
38864
38742
  var import_node_os19 = require("node:os");
38865
38743
  var import_node_path36 = require("node:path");
@@ -38959,8 +38837,8 @@ async function checkPrReview(number, repo, head, deps = {
38959
38837
  }
38960
38838
  function computePrPatchId(number, repo) {
38961
38839
  return new Promise((resolve7, reject) => {
38962
- const gh = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
38963
- const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
38840
+ const gh = (0, import_node_child_process13.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
38841
+ const git3 = (0, import_node_child_process13.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
38964
38842
  let out = "";
38965
38843
  let ghErr = "";
38966
38844
  let gitErr = "";
@@ -39042,7 +38920,7 @@ async function postPrCommentFromFile(number, repo, body) {
39042
38920
  }
39043
38921
 
39044
38922
  // src/pr-create-docs-check.ts
39045
- var import_node_child_process15 = require("node:child_process");
38923
+ var import_node_child_process14 = require("node:child_process");
39046
38924
  init_cli_shared();
39047
38925
  var GIT_TIMEOUT_MS2 = 15e3;
39048
38926
  function catFileBatch(root, ref, paths) {
@@ -39050,7 +38928,7 @@ function catFileBatch(root, ref, paths) {
39050
38928
  return new Promise((resolve7) => {
39051
38929
  const chunks = [];
39052
38930
  let settled = false;
39053
- const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
38931
+ const child2 = (0, import_node_child_process14.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
39054
38932
  const finish = () => {
39055
38933
  if (settled) return;
39056
38934
  settled = true;
@@ -40197,7 +40075,7 @@ function scheduleRelatedDiscovery(o) {
40197
40075
  try {
40198
40076
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
40199
40077
  if (o.repo) args.push("--repo", o.repo);
40200
- spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
40078
+ spawnDetachedSelf(args, { spawn: import_node_child_process15.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
40201
40079
  } catch {
40202
40080
  }
40203
40081
  }
@@ -41893,7 +41771,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
41893
41771
  }
41894
41772
 
41895
41773
  // src/dist-drift.ts
41896
- var import_node_child_process17 = require("node:child_process");
41774
+ var import_node_child_process16 = require("node:child_process");
41897
41775
  var import_node_crypto13 = require("node:crypto");
41898
41776
  var import_node_fs44 = require("node:fs");
41899
41777
  var import_node_os20 = require("node:os");
@@ -42038,7 +41916,7 @@ function bomPathFor(root) {
42038
41916
  }
42039
41917
  }
42040
41918
  function rebuildTo(packageRoot, outDir) {
42041
- (0, import_node_child_process17.execFileSync)(process.execPath, ["build.mjs"], {
41919
+ (0, import_node_child_process16.execFileSync)(process.execPath, ["build.mjs"], {
42042
41920
  cwd: packageRoot,
42043
41921
  env: { ...process.env, MMI_DIST_OUTDIR: outDir },
42044
41922
  windowsHide: true,
@@ -42265,7 +42143,7 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
42265
42143
  }
42266
42144
 
42267
42145
  // src/spawn-policy-core.ts
42268
- var import_node_child_process18 = require("node:child_process");
42146
+ var import_node_child_process17 = require("node:child_process");
42269
42147
  var import_node_fs45 = require("node:fs");
42270
42148
  var import_node_path43 = require("node:path");
42271
42149
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
@@ -42336,7 +42214,7 @@ function findViolationsInSource(raw) {
42336
42214
  return found;
42337
42215
  }
42338
42216
  function policedFiles(root) {
42339
- const r = (0, import_node_child_process18.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
42217
+ const r = (0, import_node_child_process17.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
42340
42218
  cwd: root,
42341
42219
  encoding: "utf8",
42342
42220
  windowsHide: true,
@@ -42642,50 +42520,6 @@ init_house_map();
42642
42520
  // src/hotfix-apply.ts
42643
42521
  var import_promises9 = require("node:fs/promises");
42644
42522
 
42645
- // src/slack-alert.ts
42646
- var SSM_REGION = "eu-central-1";
42647
- var SSM_TOKEN_PARAM = "/mmi-future/_org/slack/SLACK_BOT_TOKEN";
42648
- var SSM_CHANNEL_PARAM = "/mmi-future/_org/slack/SLACK_ALERTS_CHANNEL";
42649
- var SLACK_TIMEOUT_MS = 1e4;
42650
- async function readSsmParameter(deps, name, decrypt) {
42651
- const args = [
42652
- "ssm",
42653
- "get-parameter",
42654
- "--region",
42655
- SSM_REGION,
42656
- "--name",
42657
- name,
42658
- "--query",
42659
- "Parameter.Value",
42660
- "--output",
42661
- "text",
42662
- ...decrypt ? ["--with-decryption"] : []
42663
- ];
42664
- const value = (await deps.run("aws", args)).trim();
42665
- if (!value || value === "None") throw new Error(`SSM parameter ${name} is empty`);
42666
- return value;
42667
- }
42668
- async function postToChannel(deps, channel, text) {
42669
- const resolved = channel.trim();
42670
- if (!resolved || resolved === "None") throw new Error("no Slack channel resolved");
42671
- const token = await readSsmParameter(deps, SSM_TOKEN_PARAM, true);
42672
- const fetchImpl = deps.fetchImpl ?? fetch;
42673
- const res = await fetchImpl("https://slack.com/api/chat.postMessage", {
42674
- method: "POST",
42675
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8" },
42676
- body: JSON.stringify({ channel: resolved, text, unfurl_links: false }),
42677
- signal: AbortSignal.timeout(SLACK_TIMEOUT_MS)
42678
- });
42679
- const json = await res.json().catch(() => ({}));
42680
- if (!json.ok) throw new Error(`slack postMessage failed: ${json.error ?? `http ${res.status}`}`);
42681
- return { status: "posted", note: "posted" };
42682
- }
42683
- async function postToAlertsChannel(deps, text) {
42684
- const channel = await readSsmParameter(deps, SSM_CHANNEL_PARAM, false);
42685
- await postToChannel(deps, channel, text);
42686
- return { status: "posted", note: "posted to the alerts channel" };
42687
- }
42688
-
42689
42523
  // src/release-announce.ts
42690
42524
  var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
42691
42525
  var MAX_BULLETS = 6;
@@ -42792,12 +42626,6 @@ async function announceRelease(deps, args) {
42792
42626
  if (args.summaryFile) {
42793
42627
  if (!deps.readFile) throw new Error("summary file given but deps.readFile is missing");
42794
42628
  lines2 = summaryFileLines(await deps.readFile(args.summaryFile), neutralize);
42795
- if (deps.removeFile) {
42796
- try {
42797
- await deps.removeFile(args.summaryFile);
42798
- } catch {
42799
- }
42800
- }
42801
42629
  }
42802
42630
  if (lines2.length === 0) {
42803
42631
  if (target.kind !== "alerts") {
@@ -42813,12 +42641,16 @@ async function announceRelease(deps, args) {
42813
42641
  releaseUrl,
42814
42642
  ...target.kind === "channel" ? { language: target.language } : {}
42815
42643
  });
42816
- if (target.kind === "alerts") {
42817
- await postToAlertsChannel(deps, text);
42818
- return { status: "announced", note: `announced ${args.tag} to the alerts channel` };
42644
+ const delivery = await deps.postAnnouncement({ repo: args.repo, tag: args.tag, text });
42645
+ if (delivery === "skipped") return { status: "skipped", note: missingChannelNote(args.repo) };
42646
+ if (args.summaryFile && deps.removeFile) {
42647
+ try {
42648
+ await deps.removeFile(args.summaryFile);
42649
+ } catch {
42650
+ }
42819
42651
  }
42820
- await postToChannel(deps, target.channel, text);
42821
- return { status: "announced", note: `announced ${args.tag} to the project release channel` };
42652
+ const destination = target.kind === "alerts" ? "alerts channel" : "project release channel";
42653
+ return { status: "announced", note: `${delivery === "already-posted" ? "already announced" : "announced"} ${args.tag} to the ${destination}` };
42822
42654
  } catch (e) {
42823
42655
  return { status: "failed", note: `announce failed (release unaffected): ${e.message}` };
42824
42656
  }
@@ -43945,12 +43777,12 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = H
43945
43777
  }
43946
43778
 
43947
43779
  // src/hotfix-coverage.ts
43948
- var import_node_child_process19 = require("node:child_process");
43780
+ var import_node_child_process18 = require("node:child_process");
43949
43781
  var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
43950
43782
  function checkHotfixCoverage(options = {}) {
43951
43783
  const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
43952
43784
  const ack = (options.ack ?? []).filter(Boolean);
43953
- const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
43785
+ const git3 = options.git ?? ((args, opts) => (0, import_node_child_process18.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
43954
43786
  const revList = (range) => {
43955
43787
  const out = git3(["rev-list", "--no-merges", range]).trim();
43956
43788
  return out ? out.split("\n") : [];
@@ -44018,7 +43850,7 @@ function checkHotfixCoverage(options = {}) {
44018
43850
  }
44019
43851
  function checkHotfixCarries(options) {
44020
43852
  const { cwd = process.cwd(), branch, baseRef, targets } = options;
44021
- const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
43853
+ const git3 = options.git ?? ((args, opts) => (0, import_node_child_process18.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
44022
43854
  const isAncestor = (sha, ref) => {
44023
43855
  try {
44024
43856
  git3(["merge-base", "--is-ancestor", sha, ref]);
@@ -44288,6 +44120,15 @@ function trainApplyDeps() {
44288
44120
  // registry releaseChannel META for a product repo — best-effort inside announceRelease itself.
44289
44121
  announce: (args) => announceRelease({
44290
44122
  run: async (file, cmdArgs) => (await execFileP(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS })).stdout,
44123
+ postAnnouncement: async (payload) => {
44124
+ const res = await releaseAnnouncement(payload, registryClientDeps(await loadConfig()));
44125
+ const body = res.body;
44126
+ if (!res.ok) throw new Error(body?.error ?? `Hub announcement failed (HTTP ${res.status}); verify delivery before retrying`);
44127
+ if (body?.ok !== true || body.status !== "posted" && body?.status !== "already-posted" && body?.status !== "skipped") {
44128
+ throw new Error("Hub announcement returned an unverified delivery result; do not resend");
44129
+ }
44130
+ return body.status;
44131
+ },
44291
44132
  readFile: (path2) => (0, import_promises10.readFile)(path2, "utf8"),
44292
44133
  removeFile: (path2) => (0, import_promises10.unlink)(path2),
44293
44134
  // #6521/#6523: the project's own release channel AND releaseLanguage, read from registry META in ONE
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.57",
3
+ "version": "4.3.59",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",