@mutmutco/cli 4.0.2 → 4.0.3

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 +80 -39
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -9155,6 +9155,7 @@ var OWNER2 = "mutmutco";
9155
9155
  var SSM_ROOT = "/mmi-future";
9156
9156
  var PROJECT_TIER_SEGMENT = "dev";
9157
9157
  var ORG_INFRA_SLUG = "_org";
9158
+ var JERV_VAULT_POINTER = "Not in the MMI vault? Jerv service, personal API and model-provider credentials live in the separate Jerv AWS Vault \u2014 enumerate: `jerv-cli vault list`; consume keyless: `jerv-cli vault use <KEY> -- <cmd>` (values are never printed).";
9158
9159
  var KEY_MAX_PATH_SEGMENTS = 4;
9159
9160
  var KEY_RE = new RegExp(`^(?:[a-z0-9][a-z0-9-]*/){0,${KEY_MAX_PATH_SEGMENTS}}[A-Za-z][A-Za-z0-9_]*$`);
9160
9161
  function isValidSecretKey(key) {
@@ -9171,7 +9172,7 @@ function secretParamName(slug, key) {
9171
9172
  return `${SSM_ROOT}/${slug}/${key}`;
9172
9173
  }
9173
9174
  function formatSecretList(items) {
9174
- if (!items.length) return "no secrets";
9175
+ if (!items.length) return `no secrets \u2014 ${JERV_VAULT_POINTER}`;
9175
9176
  const width = Math.max(...items.map((i) => i.key.length));
9176
9177
  return items.map((i) => {
9177
9178
  const head = `${i.canManage ? "*" : " "} ${i.key.padEnd(width)} ${i.tier}`;
@@ -9362,7 +9363,7 @@ function formatCapabilities(r) {
9362
9363
  const items = [...r.capabilities ?? []].sort((a, b) => a.scope.localeCompare(b.scope));
9363
9364
  if (!items.length) return `${head}
9364
9365
 
9365
- no vault credentials visible`;
9366
+ no vault credentials visible \u2014 ${JERV_VAULT_POINTER}`;
9366
9367
  const width = Math.max(...items.map((i) => i.scope.length));
9367
9368
  const lines = items.map(
9368
9369
  (i) => `${i.accessible ? "+" : " "} ${i.scope.padEnd(width)} ${i.tier.padEnd(7)} ${i.grantScope ? `${i.reason} (${i.grantScope})` : i.reason}`
@@ -9485,7 +9486,8 @@ function resolveNotFoundGuidance(input) {
9485
9486
  const lines = [
9486
9487
  `Secret ${key} was not found at ${tried}, and no key named ${leaf} is visible to you.`,
9487
9488
  `Locate it by intent first: \`mmi-cli vault secrets find "<what it is for>"\` \u2014 it returns the exact keyless-use command.`,
9488
- mnemonic
9489
+ mnemonic,
9490
+ JERV_VAULT_POINTER
9489
9491
  ];
9490
9492
  if (isMaster) {
9491
9493
  lines.push("You are master: if it truly does not exist yet, create it with `mmi-cli vault secrets set` rather than requesting a grant from yourself.");
@@ -9767,6 +9769,7 @@ async function secretsVerify(deps, key, opts) {
9767
9769
  if (!value) {
9768
9770
  deps.err(`${key}: ABSENT or unreachable at this coordinate \u2014 no value could be read`);
9769
9771
  deps.err(`Locate a key across every vault you can reach: mmi-cli oracle org access capabilities${opts.repo ? ` --repo ${opts.repo}` : ""}`);
9772
+ deps.err(JERV_VAULT_POINTER);
9770
9773
  return false;
9771
9774
  }
9772
9775
  deps.log(`${key}: PRESENT (${value.length} chars) \u2014 reachability only; no provider verifier is configured for ${secretLeafName(key)}, so the credential itself was not validated`);
@@ -10422,6 +10425,7 @@ async function fetchSecretForUse(deps, { repo, key, slug }) {
10422
10425
  // src/report.ts
10423
10426
  var HUB_REPO = "mutmutco/MMI-Hub";
10424
10427
  var REPORT_LABEL = "report";
10428
+ var LEARNING_LABEL = "learning";
10425
10429
  var REPORT_LOOP_KIND = "friction";
10426
10430
  var REPORT_SURFACE_WAIVER_REASON = "Hub-only friction filing; attribution repo is footer-only; no honest single surface";
10427
10431
  function preflightReportSurface() {
@@ -10819,10 +10823,10 @@ var rollout_plan_default = {
10819
10823
  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)."
10820
10824
  },
10821
10825
  baseline: {
10822
- version: "4.0.2",
10823
- tag: "v4.0.2",
10824
- commit: "e703fd090be7",
10825
- npm: "@mutmutco/cli@4.0.2"
10826
+ version: "4.0.3",
10827
+ tag: "v4.0.3",
10828
+ commit: "3a588e3fa3a3",
10829
+ npm: "@mutmutco/cli@4.0.3"
10826
10830
  },
10827
10831
  exitCriterion: "fleet-n-of-n",
10828
10832
  hubOnlyShortcut: "forbidden",
@@ -10839,14 +10843,14 @@ var rollout_plan_default = {
10839
10843
  repo: "mutmutco/mmi-hub",
10840
10844
  role: "canary",
10841
10845
  schedule: "train",
10842
- v3Target: "v4.0.2"
10846
+ v3Target: "v4.0.3"
10843
10847
  }
10844
10848
  ],
10845
10849
  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.",
10846
10850
  rollback: {
10847
10851
  independent: true,
10848
- mechanism: "npm dist-tag latest -> 4.0.2 and redeploy the Hub Lambda from tag v4.0.2 (e703fd090be7); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
10849
- v3Target: "v4.0.2 (@mutmutco/cli@4.0.2, tag commit e703fd090be7 \u2014 last known-good release carrying the repo-index v4-only contract)"
10852
+ mechanism: "npm dist-tag latest -> 4.0.3 and redeploy the Hub Lambda from tag v4.0.3 (3a588e3fa3a3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
10853
+ v3Target: "v4.0.3 (@mutmutco/cli@4.0.3, tag commit 3a588e3fa3a3 \u2014 last known-good release carrying the repo-index v4-only contract)"
10850
10854
  }
10851
10855
  },
10852
10856
  {
@@ -17606,6 +17610,7 @@ async function postIssueComment(client, input) {
17606
17610
 
17607
17611
  // src/skill-lesson.ts
17608
17612
  var SKILL_LESSON_LABEL = "skill-lesson";
17613
+ var SKILL_LESSON_FILE_LABELS = [SKILL_LESSON_LABEL, LEARNING_LABEL];
17609
17614
  var SKILL_LESSON_LOOP_KIND = "lesson";
17610
17615
  var SKILL_NAMES = ["bootstrap", "browser-automation", "doctor", "epic", "hotfix", "mmi", "onboard", "rcand", "release", "resume", "secrets", "stage"];
17611
17616
  function assertSkillName(name) {
@@ -17638,6 +17643,7 @@ function findDuplicateLesson(source, openLessons) {
17638
17643
  }
17639
17644
 
17640
17645
  // src/session-identity.ts
17646
+ var import_node_crypto4 = require("node:crypto");
17641
17647
  var import_node_os7 = require("node:os");
17642
17648
 
17643
17649
  // src/plugin-guard-io.ts
@@ -18625,12 +18631,19 @@ async function runPluginHeal(surface = detectSurface(process.env)) {
18625
18631
  // src/session-identity.ts
18626
18632
  var SESSION_ID_ENV_VARS = [
18627
18633
  "MMI_SESSION_ID",
18634
+ "MMI_GATE_SESSION_ID",
18628
18635
  "CLAUDE_SESSION_ID",
18629
18636
  "CLAUDE_CODE_SESSION_ID",
18630
18637
  "CODEX_SESSION_ID",
18638
+ "CODEX_THREAD_ID",
18631
18639
  "CURSOR_SESSION_ID",
18640
+ "HERMES_SESSION_ID",
18632
18641
  "PI_SESSION_ID"
18633
18642
  ];
18643
+ var SYNTH_SESSION_PREFIX = "synth-";
18644
+ function isHostSessionId(session) {
18645
+ return Boolean(session) && !session.startsWith(SYNTH_SESSION_PREFIX);
18646
+ }
18634
18647
  function readSessionId(env) {
18635
18648
  for (const key of SESSION_ID_ENV_VARS) {
18636
18649
  const value = env[key]?.trim();
@@ -18638,11 +18651,18 @@ function readSessionId(env) {
18638
18651
  }
18639
18652
  return void 0;
18640
18653
  }
18654
+ var synthesizedSessionId;
18655
+ function fallbackSessionId(surface) {
18656
+ if (!synthesizedSessionId) {
18657
+ synthesizedSessionId = `${SYNTH_SESSION_PREFIX}${surface}-${process.pid}-${(0, import_node_crypto4.randomBytes)(3).toString("hex")}`;
18658
+ }
18659
+ return synthesizedSessionId;
18660
+ }
18641
18661
  function describeSessionIdentity(env = process.env) {
18642
- const session = readSessionId(env);
18662
+ const surface = detectSurface(env);
18643
18663
  return {
18644
- ...session ? { session } : {},
18645
- surface: detectSurface(env),
18664
+ session: readSessionId(env) ?? fallbackSessionId(surface),
18665
+ surface,
18646
18666
  host: (0, import_node_os7.hostname)()
18647
18667
  };
18648
18668
  }
@@ -19910,7 +19930,7 @@ async function postClaimMarkerComment(client, item) {
19910
19930
  const actor = describeSessionIdentity();
19911
19931
  const marker = {
19912
19932
  v: 1,
19913
- ...actor.session ? { session: actor.session } : {},
19933
+ session: actor.session,
19914
19934
  surface: actor.surface,
19915
19935
  host: actor.host,
19916
19936
  ts: (/* @__PURE__ */ new Date()).toISOString()
@@ -20051,7 +20071,7 @@ function sameClaimHost(a, b) {
20051
20071
  }
20052
20072
  function laneOwnership(marker, current) {
20053
20073
  if (!marker) return "unknown";
20054
- if (marker.session && current.session) {
20074
+ if (isHostSessionId(marker.session) && isHostSessionId(current.session)) {
20055
20075
  return marker.session === current.session ? "mine" : "other";
20056
20076
  }
20057
20077
  if (marker.surface && current.surface && marker.surface !== current.surface) return "other";
@@ -21727,14 +21747,14 @@ function renderVerifyBroker(input) {
21727
21747
  }
21728
21748
 
21729
21749
  // src/tenant-artifact.ts
21730
- var import_node_crypto4 = require("node:crypto");
21750
+ var import_node_crypto5 = require("node:crypto");
21731
21751
  var import_node_fs22 = require("node:fs");
21732
21752
  var import_promises3 = require("node:fs/promises");
21733
21753
  var import_node_path20 = require("node:path");
21734
21754
  var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
21735
21755
  var MAX_BYTES = 5 * 1024 * 1024 * 1024;
21736
21756
  async function sha256File(path2) {
21737
- const hash = (0, import_node_crypto4.createHash)("sha256");
21757
+ const hash = (0, import_node_crypto5.createHash)("sha256");
21738
21758
  for await (const chunk of (0, import_node_fs22.createReadStream)(path2)) hash.update(chunk);
21739
21759
  return hash.digest("hex");
21740
21760
  }
@@ -22874,7 +22894,7 @@ async function announceRelease(deps, args) {
22874
22894
  }
22875
22895
 
22876
22896
  // src/repo-index.ts
22877
- var import_node_crypto5 = require("node:crypto");
22897
+ var import_node_crypto6 = require("node:crypto");
22878
22898
  var import_node_child_process12 = require("node:child_process");
22879
22899
  var import_node_fs23 = require("node:fs");
22880
22900
  var import_node_path21 = require("node:path");
@@ -23064,7 +23084,7 @@ function rebuildRepoIndex(cwd, repoSlug3) {
23064
23084
  continue;
23065
23085
  }
23066
23086
  if (text.length > 15e5) continue;
23067
- const hash = (0, import_node_crypto5.createHash)("sha256").update(text).digest("hex").slice(0, 16);
23087
+ const hash = (0, import_node_crypto6.createHash)("sha256").update(text).digest("hex").slice(0, 16);
23068
23088
  const symbols = extractSymbols(text);
23069
23089
  const docBlurb = extractModuleBlurb(text);
23070
23090
  const top = rel.includes("/") ? rel.split("/")[0] : "";
@@ -23169,16 +23189,16 @@ function inferRepoSlug(cwd, exec = import_node_child_process12.execFileSync) {
23169
23189
  }
23170
23190
 
23171
23191
  // src/repo-index-cloud-client.ts
23172
- var import_node_crypto8 = require("node:crypto");
23192
+ var import_node_crypto9 = require("node:crypto");
23173
23193
 
23174
23194
  // src/repo-index-v4/builder.ts
23175
- var import_node_crypto7 = require("node:crypto");
23195
+ var import_node_crypto8 = require("node:crypto");
23176
23196
  var import_node_child_process13 = require("node:child_process");
23177
23197
  var import_node_fs25 = require("node:fs");
23178
23198
  var import_node_path23 = require("node:path");
23179
23199
 
23180
23200
  // src/repo-index-v4/chunks.ts
23181
- var import_node_crypto6 = require("node:crypto");
23201
+ var import_node_crypto7 = require("node:crypto");
23182
23202
  var import_node_fs24 = require("node:fs");
23183
23203
  var import_node_path22 = require("node:path");
23184
23204
 
@@ -23245,7 +23265,7 @@ async function parserFor(language) {
23245
23265
  return parser;
23246
23266
  }
23247
23267
  function sha256(value) {
23248
- return (0, import_node_crypto6.createHash)("sha256").update(value).digest("hex");
23268
+ return (0, import_node_crypto7.createHash)("sha256").update(value).digest("hex");
23249
23269
  }
23250
23270
  function pointerId(path2, kind, contentHash, start, end, symbol) {
23251
23271
  return sha256(["repo-index-v4", path2, kind, contentHash, String(start), String(end), symbol ?? ""].join("\0"));
@@ -23365,7 +23385,7 @@ function canonicalJson(value) {
23365
23385
  return `{${Object.keys(record).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson(record[k])}`).join(",")}}`;
23366
23386
  }
23367
23387
  function sha2562(value) {
23368
- return (0, import_node_crypto7.createHash)("sha256").update(canonicalJson(value)).digest("hex");
23388
+ return (0, import_node_crypto8.createHash)("sha256").update(canonicalJson(value)).digest("hex");
23369
23389
  }
23370
23390
  function quantizeV4Vector(vector) {
23371
23391
  return vector.map((value) => Number(value.toFixed(V4_VECTOR_DECIMAL_PLACES)));
@@ -23692,7 +23712,7 @@ async function searchRepoIndexCloud(query, opts, deps) {
23692
23712
  const body = await res.json().catch(() => ({}));
23693
23713
  if (!res.ok) return { ok: false, error: body.error ?? `search HTTP ${res.status}`, status: res.status };
23694
23714
  const shadowRate = Math.max(0, Math.min(1, Number(process.env.MMI_REPO_INDEX_V4_SHADOW_RATE ?? 0.1) || 0));
23695
- if ((0, import_node_crypto8.createHash)("sha256").update(query).digest()[0] / 256 < shadowRate) {
23715
+ if ((0, import_node_crypto9.createHash)("sha256").update(query).digest()[0] / 256 < shadowRate) {
23696
23716
  void fetchWithRetry(
23697
23717
  deps.fetch ?? fetch,
23698
23718
  `${deps.baseUrl.replace(/\/$/, "")}/repo-index/v4/shadow`,
@@ -26031,7 +26051,7 @@ function registerSecretsCommands(program3) {
26031
26051
  }
26032
26052
 
26033
26053
  // src/app-actor.ts
26034
- var import_node_crypto9 = require("node:crypto");
26054
+ var import_node_crypto10 = require("node:crypto");
26035
26055
  var APP_ACTOR_ENV = "MMI_ACTOR";
26036
26056
  var APP_VAULT_REPO = "mutmutco/MMI-Hub";
26037
26057
  var APP_VAULT_KEYS = ["GITHUB_APP_ID", "GITHUB_APP_INSTALLATION_ID", "GITHUB_APP_PRIVATE_KEY"];
@@ -26075,7 +26095,7 @@ function mintAppJwt(appId, privateKeyPem, nowSec) {
26075
26095
  exp: now + APP_JWT_TTL_S,
26076
26096
  iss: appId
26077
26097
  }));
26078
- const signer = (0, import_node_crypto9.createSign)("RSA-SHA256");
26098
+ const signer = (0, import_node_crypto10.createSign)("RSA-SHA256");
26079
26099
  signer.update(`${header}.${payload}`);
26080
26100
  return `${header}.${payload}.${signer.sign(privateKeyPem, "base64url")}`;
26081
26101
  }
@@ -27003,7 +27023,7 @@ var import_node_os14 = require("node:os");
27003
27023
  var import_node_path32 = require("node:path");
27004
27024
 
27005
27025
  // src/bootstrap-drift.ts
27006
- var import_node_crypto10 = require("node:crypto");
27026
+ var import_node_crypto11 = require("node:crypto");
27007
27027
  function byteComparableSeeds(manifest, cls) {
27008
27028
  return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
27009
27029
  }
@@ -27023,7 +27043,7 @@ function compareSeedBytes(hubContent, repoContent) {
27023
27043
  return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
27024
27044
  }
27025
27045
  function seedContentHash(content) {
27026
- return (0, import_node_crypto10.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
27046
+ return (0, import_node_crypto11.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
27027
27047
  }
27028
27048
  function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
27029
27049
  const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
@@ -27186,7 +27206,7 @@ function renderPropagationReport(plan) {
27186
27206
  }
27187
27207
 
27188
27208
  // src/bootstrap-propagation-identity.ts
27189
- var import_node_crypto11 = require("node:crypto");
27209
+ var import_node_crypto12 = require("node:crypto");
27190
27210
  var PROPAGATION_BRANCH_PREFIX = "seed-propagate-";
27191
27211
  var TARGET_MARKER_NAME = "mmi-bootstrap-propagation-target";
27192
27212
  function safeBranchPart(value, maxLength, fallback) {
@@ -27199,7 +27219,7 @@ function repoSlug2(repo) {
27199
27219
  function propagationBranch(repo, target) {
27200
27220
  const repoPart = safeBranchPart(repoSlug2(repo), 32, "repo");
27201
27221
  const targetPart = safeBranchPart(target, 48, "target");
27202
- const hash = (0, import_node_crypto11.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
27222
+ const hash = (0, import_node_crypto12.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
27203
27223
  return `${PROPAGATION_BRANCH_PREFIX}${repoPart}-${targetPart}-${hash}`;
27204
27224
  }
27205
27225
  function legacyPropagationBranch(repo) {
@@ -29739,7 +29759,7 @@ function registerBoardCommands(program3) {
29739
29759
  const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
29740
29760
  board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--allow-partial applies to the paginated path and detail reads.\n").action((o) => runBoardRead(o));
29741
29761
  withExamples(mutating(
29742
- board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
29762
+ board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").addHelpText("after", "\nevery claim stamps a lane-identity marker comment on the issue (`<!-- mmi-claim: \u2026 -->`,\nsurface/session@host) so other agents can attribute the hold (#3727). The session is the\nhost-exported id when the surface provides one, otherwise a per-process `synth-` fallback \u2014\na claim is never anonymous (#5245). `board show`, doctor and unclaim read the latest marker.\n").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
29743
29763
  (_opts, args) => ({ command: "board claim", issues: args[0] ?? [] })
29744
29764
  ).action(async (issueRefs, o) => {
29745
29765
  if (issueRefs.length === 1) {
@@ -30360,7 +30380,7 @@ async function checkDocsIndexAtHead(opts, deps) {
30360
30380
 
30361
30381
  // src/issue-commands.ts
30362
30382
  var import_node_fs38 = require("node:fs");
30363
- var import_node_crypto12 = require("node:crypto");
30383
+ var import_node_crypto13 = require("node:crypto");
30364
30384
  var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
30365
30385
  var ReparentConflictError = class extends Error {
30366
30386
  constructor(message, payload) {
@@ -30651,7 +30671,7 @@ function rowIdempotencyKey(batchKey, spec) {
30651
30671
  const identity = `${spec.type}
30652
30672
  ${spec.title.trim()}
30653
30673
  ${spec.body ?? ""}`;
30654
- const hash = (0, import_node_crypto12.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
30674
+ const hash = (0, import_node_crypto13.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
30655
30675
  return `${batchKey}:${hash}`;
30656
30676
  }
30657
30677
  var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
@@ -31283,7 +31303,23 @@ async function collectStatus() {
31283
31303
  } catch {
31284
31304
  stage = { running: false };
31285
31305
  }
31286
- return { repo, branch, worktrees, myOpenPrs, claimedItems, stage };
31306
+ let releaseTrack;
31307
+ let branches;
31308
+ try {
31309
+ const cfg = await loadConfigOrDiscover();
31310
+ if (cfg.sagaApiUrl) {
31311
+ const slug = await repoSlug();
31312
+ const deps = registryClientDeps(cfg);
31313
+ const read = await fetchProjectBySlugChecked(slug, deps);
31314
+ if (read.ok && read.project) {
31315
+ const trackRepo = repo ?? `mutmutco/${slug}`;
31316
+ releaseTrack = resolveReleaseTrack(read.project, void 0, trackRepo);
31317
+ branches = branchesForTrack(releaseTrack);
31318
+ }
31319
+ }
31320
+ } catch {
31321
+ }
31322
+ return { repo, branch, releaseTrack, branches, worktrees, myOpenPrs, claimedItems, stage };
31287
31323
  }
31288
31324
  var PRIORITY_RANK = {
31289
31325
  Urgent: 0,
@@ -31428,6 +31464,9 @@ function registerDiscoveryCommands(program3) {
31428
31464
  } else {
31429
31465
  console.log(`repo: ${report.repo ?? "unknown"}`);
31430
31466
  console.log(`branch: ${report.branch}`);
31467
+ if (report.releaseTrack) {
31468
+ console.log(`release track: ${report.releaseTrack} (${(report.branches ?? []).join(" -> ")})`);
31469
+ }
31431
31470
  console.log(`worktrees: ${report.worktrees.length} linked`);
31432
31471
  console.log(`my open PRs: ${report.myOpenPrs.length}`);
31433
31472
  console.log(`claimed board items: ${report.claimedItems.length}`);
@@ -37568,7 +37607,7 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
37568
37607
  title = buildSkillLessonTitle(skill, rawTitle);
37569
37608
  priority = resolveCreatePriority(o.priority, "skill-lesson");
37570
37609
  body = buildSkillLessonBody(rawBody, sourceRepo, pluginSha);
37571
- args = buildIssueArgs({ type: "task", title, body, priority, repo: targetRepo3, labels: [SKILL_LESSON_LABEL] });
37610
+ args = buildIssueArgs({ type: "task", title, body, priority, repo: targetRepo3, labels: [...SKILL_LESSON_FILE_LABELS] });
37572
37611
  } catch (e) {
37573
37612
  return fail(`skill-lesson: ${e.message}`);
37574
37613
  }
@@ -37610,9 +37649,11 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
37610
37649
  });
37611
37650
  if (surfaceWarn) process.stderr.write(`${surfaceWarn}
37612
37651
  `);
37613
- try {
37614
- await execFileP2("gh", ["label", "create", SKILL_LESSON_LABEL, "--color", "c2e0c6", "--repo", targetRepo3], { timeout: GH_MUTATION_TIMEOUT_MS });
37615
- } catch {
37652
+ for (const [label, color] of [[SKILL_LESSON_LABEL, "c2e0c6"], [LEARNING_LABEL, "ededed"]]) {
37653
+ try {
37654
+ await execFileP2("gh", ["label", "create", label, "--color", color, "--repo", targetRepo3], { timeout: GH_MUTATION_TIMEOUT_MS });
37655
+ } catch {
37656
+ }
37616
37657
  }
37617
37658
  const created = await ghCreate(args);
37618
37659
  const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo3, priority);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
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",