@mutmutco/cli 2.43.0 → 2.44.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.
Files changed (2) hide show
  1. package/dist/main.cjs +72 -14
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -8371,6 +8371,36 @@ async function runPrLand(prNumber, options, deps) {
8371
8371
  // src/index.ts
8372
8372
  var import_node_os5 = require("node:os");
8373
8373
 
8374
+ // src/attach-to-project.ts
8375
+ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn = (m) => process.stderr.write(m)) {
8376
+ let projectItemId;
8377
+ try {
8378
+ const boardCfg = resolveBoardConfig(cfg);
8379
+ const lookup = await fetchIssueProjectItem(client, boardCfg, selector);
8380
+ projectItemId = lookup.item?.itemId;
8381
+ } catch (e) {
8382
+ warn(`warning: issue #${selector.number} board item lookup failed after auto-add: ${e.message}
8383
+ `);
8384
+ }
8385
+ if (priority) {
8386
+ if (projectItemId) {
8387
+ try {
8388
+ await setBoardItemPriority(client, cfg, projectItemId, priority);
8389
+ } catch (e) {
8390
+ const err = e;
8391
+ warn(
8392
+ `warning: issue #${selector.number} board Priority not set: ${(err.stderr || err.message || String(e)).trim()}
8393
+ `
8394
+ );
8395
+ }
8396
+ } else {
8397
+ warn(`warning: issue #${selector.number} board Priority not set: could not resolve project item id after auto-add
8398
+ `);
8399
+ }
8400
+ }
8401
+ return { projectItemId };
8402
+ }
8403
+
8374
8404
  // src/gh-create.ts
8375
8405
  var import_promises4 = require("node:fs/promises");
8376
8406
  var import_node_os3 = require("node:os");
@@ -8448,6 +8478,9 @@ function parseAddedItemId(stdout) {
8448
8478
  return void 0;
8449
8479
  }
8450
8480
  }
8481
+ function isAlreadyOnBoardError(stderr) {
8482
+ return /already exists in (?:this|the) project/i.test(stderr);
8483
+ }
8451
8484
  function buildPrArgs({ title, body, base, head, repo }) {
8452
8485
  const args = ["pr", "create"];
8453
8486
  if (repo) args.push("--repo", repo);
@@ -13073,6 +13106,11 @@ var SSM_CHANNEL_PARAM = "/mmi-future/shared/SLACK_ALERTS_CHANNEL";
13073
13106
  var SLACK_TIMEOUT_MS = 1e4;
13074
13107
  var MAX_BULLETS = 6;
13075
13108
  var MAX_SUMMARY_LINES = 8;
13109
+ function neutralizeHubProductBrands(text) {
13110
+ let s = text.replace(/\bMM-Fofu\b/gi, "design-system consumer").replace(/\bMM-Katip\b/gi, "workspace product").replace(/\b[Ff]o[Ff]u\b/g, "design-system").replace(/\bKatip\b/gi, "workspace");
13111
+ s = s.replace(/\bdesign-system\s+design-system\b/gi, "design-system");
13112
+ return s.replace(/\s{2,}/g, " ").trim();
13113
+ }
13076
13114
  function escapeMrkdwn(text) {
13077
13115
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13078
13116
  }
@@ -13091,7 +13129,7 @@ function bulletToLine(raw) {
13091
13129
  text = text.slice(0, tail.index).trim();
13092
13130
  }
13093
13131
  if (!text) return null;
13094
- return `\u2022 ${escapeMrkdwn(text)}${link}`;
13132
+ return `\u2022 ${escapeMrkdwn(neutralizeHubProductBrands(text))}${link}`;
13095
13133
  }
13096
13134
  function curateReleaseNotes(notesMd, maxBullets = MAX_BULLETS) {
13097
13135
  const lines = [];
@@ -13138,7 +13176,7 @@ function formatAnnouncement(args) {
13138
13176
  ].join("\n");
13139
13177
  }
13140
13178
  function summaryFileLines(content) {
13141
- return content.split("\n").map((l) => l.trim()).filter(Boolean).slice(0, MAX_SUMMARY_LINES).map((l) => escapeMrkdwn(l.replace(/^[•*-]\s*/, ""))).filter(Boolean).map((l) => `\u2022 ${l}`);
13179
+ return content.split("\n").map((l) => l.trim()).filter(Boolean).slice(0, MAX_SUMMARY_LINES).map((l) => escapeMrkdwn(neutralizeHubProductBrands(l.replace(/^[•*-]\s*/, "")))).filter(Boolean).map((l) => `\u2022 ${l}`);
13142
13180
  }
13143
13181
  async function ssmParameter(deps, name, decrypt) {
13144
13182
  const args = [
@@ -13170,6 +13208,12 @@ async function announceRelease(deps, args) {
13170
13208
  if (args.summaryFile) {
13171
13209
  if (!deps.readFile) throw new Error("summary file given but deps.readFile is missing");
13172
13210
  lines = summaryFileLines(await deps.readFile(args.summaryFile));
13211
+ if (deps.removeFile) {
13212
+ try {
13213
+ await deps.removeFile(args.summaryFile);
13214
+ } catch {
13215
+ }
13216
+ }
13173
13217
  }
13174
13218
  if (lines.length === 0) lines = curateReleaseNotes(view.body ?? "");
13175
13219
  if (lines.length === 0) lines = ["\u2022 maintenance release \u2014 see the notes for details"];
@@ -17038,11 +17082,11 @@ async function attachToProject(issueNumber, repo, priority) {
17038
17082
  cfg = await loadConfigForRepo(targetRepo2);
17039
17083
  } catch (e) {
17040
17084
  console.error(`issue create: board attach skipped \u2014 ${e.message}`);
17041
- return void 0;
17085
+ return { onBoard: false };
17042
17086
  }
17043
17087
  if (!cfg.projectId) {
17044
17088
  console.error(`issue create: board attach skipped \u2014 no Hub registry board META for ${targetRepo2 ?? "current repo"}; run \`mmi-cli project get ${targetRepo2 ?? "<owner/repo>"}\` and backfill board coords`);
17045
- return void 0;
17089
+ return { onBoard: false };
17046
17090
  }
17047
17091
  try {
17048
17092
  const viewArgs = ["issue", "view", String(issueNumber), "--json", "id", "--jq", ".id"];
@@ -17061,12 +17105,25 @@ async function attachToProject(issueNumber, repo, priority) {
17061
17105
  `);
17062
17106
  }
17063
17107
  }
17064
- return projectItemId;
17108
+ return { projectItemId, onBoard: true };
17065
17109
  } catch (e) {
17066
17110
  const err = e;
17067
- process.stderr.write(`warning: issue #${issueNumber} created but NOT added to the project board: ${(err.stderr || err.message || String(e)).trim()}
17111
+ const detail = (err.stderr || err.message || String(e)).trim();
17112
+ if (isAlreadyOnBoardError(detail)) {
17113
+ if (targetRepo2) {
17114
+ const { projectItemId } = await resolveAutoAddBoardAttach(
17115
+ defaultGitHubClient(),
17116
+ cfg,
17117
+ { repo: targetRepo2, number: issueNumber },
17118
+ priority
17119
+ );
17120
+ return { projectItemId, onBoard: true };
17121
+ }
17122
+ return { onBoard: true };
17123
+ }
17124
+ process.stderr.write(`warning: issue #${issueNumber} created but NOT added to the project board: ${detail}
17068
17125
  `);
17069
- return void 0;
17126
+ return { onBoard: false };
17070
17127
  }
17071
17128
  }
17072
17129
  var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
@@ -17516,7 +17573,7 @@ issue.command("create").description("create an issue (type \u2192 label) and pri
17516
17573
  }
17517
17574
  }
17518
17575
  const created = await ghCreate(args);
17519
- const projectItemId = await attachToProject(created.number, o.repo, priority);
17576
+ const { projectItemId, onBoard } = await attachToProject(created.number, o.repo, priority);
17520
17577
  let parent;
17521
17578
  let parentLinkError;
17522
17579
  if (o.parent !== void 0) {
@@ -17530,7 +17587,7 @@ issue.command("create").description("create an issue (type \u2192 label) and pri
17530
17587
  }
17531
17588
  }
17532
17589
  if (o.related !== false) scheduleRelatedDiscovery({ repo: o.repo, number: created.number, title, body });
17533
- console.log(JSON.stringify({ ...created, label: o.type, priority, projectItemId, ...parentLinkFields(parent, parentLinkError) }));
17590
+ console.log(JSON.stringify({ ...created, label: o.type, priority, projectItemId, onBoard, ...parentLinkFields(parent, parentLinkError) }));
17534
17591
  });
17535
17592
  issue.command("discover-related").description("find related issues for an existing issue and post only high-confidence links").requiredOption("--number <number>", "created issue number").requiredOption("--title <title>", "created issue title").requiredOption("--body <body>", "created issue body").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "print candidates instead of posting").action(async (o) => {
17536
17593
  const number = Number(o.number);
@@ -17675,8 +17732,8 @@ program2.command("report").description("file a friction report on the Hub board
17675
17732
  } catch {
17676
17733
  }
17677
17734
  const created = await ghCreate(args);
17678
- const projectItemId = await attachToProject(created.number, targetRepo2, priority);
17679
- console.log(JSON.stringify({ ...created, deduped: false, label: REPORT_LABEL, priority, projectItemId }));
17735
+ const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo2, priority);
17736
+ console.log(JSON.stringify({ ...created, deduped: false, label: REPORT_LABEL, priority, projectItemId, onBoard }));
17680
17737
  });
17681
17738
  async function resolvePluginSha() {
17682
17739
  const root = process.env.CLAUDE_PLUGIN_ROOT;
@@ -17874,8 +17931,8 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
17874
17931
  } catch {
17875
17932
  }
17876
17933
  const created = await ghCreate(args);
17877
- const projectItemId = await attachToProject(created.number, targetRepo2, priority);
17878
- console.log(JSON.stringify({ ...created, deduped: false, label: SKILL_LESSON_LABEL, skill, priority, projectItemId }));
17934
+ const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo2, priority);
17935
+ console.log(JSON.stringify({ ...created, deduped: false, label: SKILL_LESSON_LABEL, skill, priority, projectItemId, onBoard }));
17879
17936
  });
17880
17937
  var pr = program2.command("pr").description("pull requests \u2014 reliable create with structured output");
17881
17938
  pr.command("create").description("create a PR and print {number,url} JSON").option("--title <title>", "PR title").option("--title-file <path|->", "read the PR title from a UTF-8 file, or from stdin with -").option("--body <body>", "PR body (markdown)").option("--body-file <path|->", "read PR body from a UTF-8 file, or from stdin with -").option("--base <branch>", "base branch (defaults to the repo default)").option("--head <branch>", "head branch (defaults to the current branch)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").action(async (o) => {
@@ -18628,7 +18685,8 @@ function trainApplyDeps() {
18628
18685
  // Slack release announcement (#883): Hub-only + best-effort inside announceRelease itself.
18629
18686
  announce: (args) => announceRelease({
18630
18687
  run: async (file, cmdArgs) => (await execFileP2(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS })).stdout,
18631
- readFile: (path2) => (0, import_promises6.readFile)(path2, "utf8")
18688
+ readFile: (path2) => (0, import_promises6.readFile)(path2, "utf8"),
18689
+ removeFile: (path2) => (0, import_promises6.unlink)(path2)
18632
18690
  }, args),
18633
18691
  fetchEdgeDomains: async (slug) => {
18634
18692
  const proj = await fetchProjectBySlug(slug, registryClientDeps(await loadConfig()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "2.43.0",
3
+ "version": "2.44.0",
4
4
  "description": "MMI Future CLI — delivers the org rules (whole-file), plus saga and KB access. The cross-IDE engine the plugin's SessionStart hook drives.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",