@mutmutco/cli 2.44.0 → 2.45.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 (4) hide show
  1. package/README.md +1 -1
  2. package/dist/main.cjs +10778 -9160
  3. package/dist/saga.cjs +90 -53
  4. package/package.json +1 -1
package/dist/saga.cjs CHANGED
@@ -3679,6 +3679,41 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
3679
3679
  throw lastErr;
3680
3680
  }
3681
3681
 
3682
+ // src/continuity.ts
3683
+ var import_node_fs4 = require("node:fs");
3684
+ var import_node_path4 = require("node:path");
3685
+ var CONTINUITY_FILE = (0, import_node_path4.join)(".mmi", "continuity.json");
3686
+ function parseContinuityStamp(raw) {
3687
+ if (!raw) return {};
3688
+ try {
3689
+ const parsed = JSON.parse(raw);
3690
+ if (!parsed || typeof parsed !== "object") return {};
3691
+ const stamp = parsed;
3692
+ return typeof stamp.lastSagaNoteAt === "string" ? { lastSagaNoteAt: stamp.lastSagaNoteAt } : {};
3693
+ } catch {
3694
+ return {};
3695
+ }
3696
+ }
3697
+ function serializeContinuityStamp(stamp) {
3698
+ return JSON.stringify(stamp, null, 2) + "\n";
3699
+ }
3700
+ function readContinuityStamp(path2 = CONTINUITY_FILE) {
3701
+ if (!(0, import_node_fs4.existsSync)(path2)) return {};
3702
+ try {
3703
+ return parseContinuityStamp((0, import_node_fs4.readFileSync)(path2, "utf8"));
3704
+ } catch {
3705
+ return {};
3706
+ }
3707
+ }
3708
+ function stampSagaNoteContinuity(now = (/* @__PURE__ */ new Date()).toISOString(), path2 = CONTINUITY_FILE) {
3709
+ try {
3710
+ const current = readContinuityStamp(path2);
3711
+ (0, import_node_fs4.mkdirSync)((0, import_node_path4.dirname)(path2), { recursive: true });
3712
+ (0, import_node_fs4.writeFileSync)(path2, serializeContinuityStamp({ ...current, lastSagaNoteAt: now }), "utf8");
3713
+ } catch {
3714
+ }
3715
+ }
3716
+
3682
3717
  // src/saga-note.ts
3683
3718
  var AGENT_SURFACE_TOKENS = ["claude", "codex", "cursor", "gemini"];
3684
3719
  var ROUTE_LEVEL_403 = "saga API route-level 403 from HubSessionAuthorizer/session policy";
@@ -4027,8 +4062,8 @@ function formatSnapshotHuman(snapshot) {
4027
4062
  }
4028
4063
 
4029
4064
  // src/saga-pending.ts
4030
- var import_node_fs4 = require("node:fs");
4031
- var import_node_path4 = require("node:path");
4065
+ var import_node_fs5 = require("node:fs");
4066
+ var import_node_path5 = require("node:path");
4032
4067
  function classifyCaptureOutcome(r) {
4033
4068
  if (r.threw) return "queued";
4034
4069
  return r.ok ? "confirmed" : "failed";
@@ -4039,16 +4074,16 @@ var PENDING_FILE = "saga-pending.jsonl";
4039
4074
  var FLUSH_LOCK_FILE = "saga-flush.lock";
4040
4075
  var FLUSH_LOCK_STALE_MS = 5 * 6e4;
4041
4076
  function pendingPath(dir = ".mmi") {
4042
- return (0, import_node_path4.join)(dir, PENDING_FILE);
4077
+ return (0, import_node_path5.join)(dir, PENDING_FILE);
4043
4078
  }
4044
4079
  var PENDING_TMP_STALE_MS = 6e4;
4045
4080
  function sweepStaleTmp(dir = ".mmi", now = Date.now()) {
4046
4081
  try {
4047
- for (const name of (0, import_node_fs4.readdirSync)(dir)) {
4082
+ for (const name of (0, import_node_fs5.readdirSync)(dir)) {
4048
4083
  if (!name.startsWith(`${PENDING_FILE}.`) || !name.endsWith(".tmp")) continue;
4049
- const path2 = (0, import_node_path4.join)(dir, name);
4084
+ const path2 = (0, import_node_path5.join)(dir, name);
4050
4085
  try {
4051
- if (now - (0, import_node_fs4.statSync)(path2).mtimeMs > PENDING_TMP_STALE_MS) (0, import_node_fs4.unlinkSync)(path2);
4086
+ if (now - (0, import_node_fs5.statSync)(path2).mtimeMs > PENDING_TMP_STALE_MS) (0, import_node_fs5.unlinkSync)(path2);
4052
4087
  } catch {
4053
4088
  }
4054
4089
  }
@@ -4056,19 +4091,19 @@ function sweepStaleTmp(dir = ".mmi", now = Date.now()) {
4056
4091
  }
4057
4092
  }
4058
4093
  function flushLockPath(dir = ".mmi") {
4059
- return (0, import_node_path4.join)(dir, FLUSH_LOCK_FILE);
4094
+ return (0, import_node_path5.join)(dir, FLUSH_LOCK_FILE);
4060
4095
  }
4061
4096
  function acquireFlushLock(dir = ".mmi", now = Date.now()) {
4062
4097
  const path2 = flushLockPath(dir);
4063
4098
  try {
4064
- (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
4065
- (0, import_node_fs4.writeFileSync)(path2, String(process.pid), { encoding: "utf8", flag: "wx" });
4099
+ (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
4100
+ (0, import_node_fs5.writeFileSync)(path2, String(process.pid), { encoding: "utf8", flag: "wx" });
4066
4101
  return true;
4067
4102
  } catch {
4068
4103
  try {
4069
- if (now - (0, import_node_fs4.statSync)(path2).mtimeMs > FLUSH_LOCK_STALE_MS) {
4070
- (0, import_node_fs4.unlinkSync)(path2);
4071
- (0, import_node_fs4.writeFileSync)(path2, String(process.pid), { encoding: "utf8", flag: "wx" });
4104
+ if (now - (0, import_node_fs5.statSync)(path2).mtimeMs > FLUSH_LOCK_STALE_MS) {
4105
+ (0, import_node_fs5.unlinkSync)(path2);
4106
+ (0, import_node_fs5.writeFileSync)(path2, String(process.pid), { encoding: "utf8", flag: "wx" });
4072
4107
  return true;
4073
4108
  }
4074
4109
  } catch {
@@ -4078,15 +4113,15 @@ function acquireFlushLock(dir = ".mmi", now = Date.now()) {
4078
4113
  }
4079
4114
  function releaseFlushLock(dir = ".mmi") {
4080
4115
  try {
4081
- (0, import_node_fs4.unlinkSync)(flushLockPath(dir));
4116
+ (0, import_node_fs5.unlinkSync)(flushLockPath(dir));
4082
4117
  } catch {
4083
4118
  }
4084
4119
  }
4085
4120
  function readPending(dir = ".mmi") {
4086
4121
  const path2 = pendingPath(dir);
4087
- if (!(0, import_node_fs4.existsSync)(path2)) return [];
4122
+ if (!(0, import_node_fs5.existsSync)(path2)) return [];
4088
4123
  const out = [];
4089
- for (const line of (0, import_node_fs4.readFileSync)(path2, "utf8").split(/\r?\n/)) {
4124
+ for (const line of (0, import_node_fs5.readFileSync)(path2, "utf8").split(/\r?\n/)) {
4090
4125
  const t = line.trim();
4091
4126
  if (!t) continue;
4092
4127
  try {
@@ -4100,16 +4135,16 @@ function readPending(dir = ".mmi") {
4100
4135
  function writePending(entries, dir = ".mmi") {
4101
4136
  const tmp = `${pendingPath(dir)}.${process.pid}.tmp`;
4102
4137
  try {
4103
- (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
4138
+ (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
4104
4139
  sweepStaleTmp(dir);
4105
4140
  const trimmed = entries.slice(-PENDING_MAX);
4106
4141
  const body = trimmed.map((e) => JSON.stringify(e)).join("\n");
4107
- (0, import_node_fs4.writeFileSync)(tmp, trimmed.length ? `${body}
4142
+ (0, import_node_fs5.writeFileSync)(tmp, trimmed.length ? `${body}
4108
4143
  ` : "", "utf8");
4109
- (0, import_node_fs4.renameSync)(tmp, pendingPath(dir));
4144
+ (0, import_node_fs5.renameSync)(tmp, pendingPath(dir));
4110
4145
  } catch {
4111
4146
  try {
4112
- (0, import_node_fs4.unlinkSync)(tmp);
4147
+ (0, import_node_fs5.unlinkSync)(tmp);
4113
4148
  } catch {
4114
4149
  }
4115
4150
  }
@@ -4123,9 +4158,9 @@ function enqueuePending(body, dir = ".mmi") {
4123
4158
  writePending([...existing, { id, body }], dir);
4124
4159
  return;
4125
4160
  }
4126
- (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
4161
+ (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
4127
4162
  sweepStaleTmp(dir);
4128
- (0, import_node_fs4.appendFileSync)(pendingPath(dir), `${JSON.stringify({ id, body })}
4163
+ (0, import_node_fs5.appendFileSync)(pendingPath(dir), `${JSON.stringify({ id, body })}
4129
4164
  `, "utf8");
4130
4165
  } catch {
4131
4166
  }
@@ -4204,8 +4239,8 @@ async function flushPending(post, dir = ".mmi") {
4204
4239
 
4205
4240
  // src/hub-auth.ts
4206
4241
  var import_node_crypto = require("node:crypto");
4207
- var import_node_fs5 = require("node:fs");
4208
- var import_node_path5 = require("node:path");
4242
+ var import_node_fs6 = require("node:fs");
4243
+ var import_node_path6 = require("node:path");
4209
4244
  var import_node_os2 = require("node:os");
4210
4245
  var REFRESH_WINDOW_MS = 10 * 60 * 1e3;
4211
4246
  var EXCHANGE_TIMEOUT_MS = 8e3;
@@ -4219,15 +4254,15 @@ function tokenFingerprint(token) {
4219
4254
  function defaultHubSessionCachePath(env = process.env) {
4220
4255
  if (env.MMI_HUB_SESSION_CACHE) return env.MMI_HUB_SESSION_CACHE;
4221
4256
  if (process.platform === "win32") {
4222
- const base2 = env.LOCALAPPDATA || (0, import_node_path5.join)((0, import_node_os2.homedir)(), "AppData", "Local");
4223
- return (0, import_node_path5.join)(base2, "MMI Future", "mmi-cli", "hub-session.json");
4257
+ const base2 = env.LOCALAPPDATA || (0, import_node_path6.join)((0, import_node_os2.homedir)(), "AppData", "Local");
4258
+ return (0, import_node_path6.join)(base2, "MMI Future", "mmi-cli", "hub-session.json");
4224
4259
  }
4225
- const base = env.XDG_STATE_HOME || (0, import_node_path5.join)((0, import_node_os2.homedir)(), ".mmi");
4226
- return (0, import_node_path5.join)(base, "mmi-cli", "hub-session.json");
4260
+ const base = env.XDG_STATE_HOME || (0, import_node_path6.join)((0, import_node_os2.homedir)(), ".mmi");
4261
+ return (0, import_node_path6.join)(base, "mmi-cli", "hub-session.json");
4227
4262
  }
4228
4263
  function readCache(path2, apiUrl, now, githubTokenFingerprint) {
4229
4264
  try {
4230
- const session = JSON.parse((0, import_node_fs5.readFileSync)(path2, "utf8"));
4265
+ const session = JSON.parse((0, import_node_fs6.readFileSync)(path2, "utf8"));
4231
4266
  if (!session.token || !session.expiresAt || session.apiUrl !== apiUrl) return null;
4232
4267
  if (session.githubTokenFingerprint !== githubTokenFingerprint) return null;
4233
4268
  if (new Date(session.expiresAt).getTime() <= now.getTime() + REFRESH_WINDOW_MS) return null;
@@ -4237,16 +4272,16 @@ function readCache(path2, apiUrl, now, githubTokenFingerprint) {
4237
4272
  }
4238
4273
  }
4239
4274
  function writeCache(path2, session) {
4240
- (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(path2), { recursive: true });
4275
+ (0, import_node_fs6.mkdirSync)((0, import_node_path6.dirname)(path2), { recursive: true });
4241
4276
  const tmp = `${path2}.${process.pid}.${Date.now()}.tmp`;
4242
- (0, import_node_fs5.writeFileSync)(tmp, JSON.stringify(session, null, 2) + "\n", { encoding: "utf8", mode: 384 });
4277
+ (0, import_node_fs6.writeFileSync)(tmp, JSON.stringify(session, null, 2) + "\n", { encoding: "utf8", mode: 384 });
4243
4278
  try {
4244
- (0, import_node_fs5.chmodSync)(tmp, 384);
4279
+ (0, import_node_fs6.chmodSync)(tmp, 384);
4245
4280
  } catch {
4246
4281
  }
4247
- (0, import_node_fs5.renameSync)(tmp, path2);
4282
+ (0, import_node_fs6.renameSync)(tmp, path2);
4248
4283
  try {
4249
- (0, import_node_fs5.chmodSync)(path2, 384);
4284
+ (0, import_node_fs6.chmodSync)(path2, 384);
4250
4285
  } catch {
4251
4286
  }
4252
4287
  }
@@ -4311,7 +4346,7 @@ function defaultHubUrl() {
4311
4346
 
4312
4347
  // src/cli-shared.ts
4313
4348
  var import_promises = require("node:fs/promises");
4314
- var import_node_fs7 = require("node:fs");
4349
+ var import_node_fs8 = require("node:fs");
4315
4350
  var import_node_crypto2 = require("node:crypto");
4316
4351
  var import_node_child_process4 = require("node:child_process");
4317
4352
  var import_node_util4 = require("node:util");
@@ -4353,12 +4388,12 @@ function hardExit(code) {
4353
4388
  }
4354
4389
 
4355
4390
  // src/stdin-inject.ts
4356
- var import_node_fs6 = require("node:fs");
4391
+ var import_node_fs7 = require("node:fs");
4357
4392
  var injectedStdin;
4358
4393
  function setInjectedStdin(payload) {
4359
4394
  injectedStdin = payload;
4360
4395
  }
4361
- function stdinHasPipedInput(statFd = () => (0, import_node_fs6.fstatSync)(0), getIsTTY = () => process.stdin.isTTY) {
4396
+ function stdinHasPipedInput(statFd = () => (0, import_node_fs7.fstatSync)(0), getIsTTY = () => process.stdin.isTTY) {
4362
4397
  try {
4363
4398
  const stat = statFd();
4364
4399
  if (stat.isFIFO() || stat.isFile()) return true;
@@ -4446,7 +4481,7 @@ function sessionDeps() {
4446
4481
  env: process.env,
4447
4482
  readPersisted: () => {
4448
4483
  try {
4449
- return (0, import_node_fs7.readFileSync)(SESSION_FILE, "utf8");
4484
+ return (0, import_node_fs8.readFileSync)(SESSION_FILE, "utf8");
4450
4485
  } catch {
4451
4486
  return null;
4452
4487
  }
@@ -4459,8 +4494,8 @@ function sessionDeps() {
4459
4494
  var resolveSessionId = () => resolveSession(sessionDeps());
4460
4495
  function persistSession(id) {
4461
4496
  try {
4462
- (0, import_node_fs7.mkdirSync)(".mmi", { recursive: true });
4463
- (0, import_node_fs7.writeFileSync)(SESSION_FILE, id, "utf8");
4497
+ (0, import_node_fs8.mkdirSync)(".mmi", { recursive: true });
4498
+ (0, import_node_fs8.writeFileSync)(SESSION_FILE, id, "utf8");
4464
4499
  } catch {
4465
4500
  }
4466
4501
  }
@@ -4539,6 +4574,7 @@ async function runNote(summary, o) {
4539
4574
  const [sha, key] = await Promise.all([gitOut(["rev-parse", "--short", "HEAD"]), sagaKey(await loadConfig())]);
4540
4575
  const capture = buildNoteCapture(summary, o, (0, import_node_crypto3.randomUUID)(), { sha: sha || void 0, branch: key.branch });
4541
4576
  await postCapture(capture);
4577
+ if (!o.diagnostic) stampSagaNoteContinuity();
4542
4578
  }
4543
4579
  function resolveSummary(summary, o) {
4544
4580
  return resolveTextArg({ value: summary, file: o.messageFile }, { readFile: import_promises2.readFile, readStdin }, {
@@ -4704,6 +4740,7 @@ async function postSnapshotNotes(plan, anchorForce) {
4704
4740
  const cap = buildNoteCapture("snapshot checklist", { queueAdd: op.text }, (0, import_node_crypto3.randomUUID)(), evidence);
4705
4741
  await postCapture(cap);
4706
4742
  }
4743
+ stampSagaNoteContinuity();
4707
4744
  }
4708
4745
  async function runSagaSnapshotShow(opts, io = consoleIo) {
4709
4746
  const head = await fetchSagaHead(io);
@@ -4862,38 +4899,38 @@ function runSagaCli(injectedStdin2) {
4862
4899
  // src/daemon-server.ts
4863
4900
  var import_node_net = require("node:net");
4864
4901
  var import_node_crypto5 = require("node:crypto");
4865
- var import_node_fs10 = require("node:fs");
4902
+ var import_node_fs11 = require("node:fs");
4866
4903
  var import_node_os4 = require("node:os");
4867
4904
 
4868
4905
  // src/daemon-client.ts
4869
- var import_node_fs9 = require("node:fs");
4870
- var import_node_path7 = require("node:path");
4906
+ var import_node_fs10 = require("node:fs");
4907
+ var import_node_path8 = require("node:path");
4871
4908
 
4872
4909
  // src/daemon-protocol.ts
4873
4910
  var import_node_crypto4 = require("node:crypto");
4874
- var import_node_fs8 = require("node:fs");
4911
+ var import_node_fs9 = require("node:fs");
4875
4912
  var import_node_os3 = require("node:os");
4876
- var import_node_path6 = require("node:path");
4913
+ var import_node_path7 = require("node:path");
4877
4914
  var DEFAULT_IDLE_EXIT_MS = 30 * 6e4;
4878
4915
  function idleExitMs(env = process.env) {
4879
4916
  const n = Number(env.MMI_CLI_DAEMON_IDLE_MS);
4880
4917
  return Number.isFinite(n) && n > 0 ? n : DEFAULT_IDLE_EXIT_MS;
4881
4918
  }
4882
4919
  function daemonDir(env = process.env) {
4883
- return env.MMI_CLI_DAEMON_DIR || (0, import_node_path6.join)((0, import_node_os3.homedir)(), ".mmi");
4920
+ return env.MMI_CLI_DAEMON_DIR || (0, import_node_path7.join)((0, import_node_os3.homedir)(), ".mmi");
4884
4921
  }
4885
4922
  function tokenPath(env = process.env) {
4886
- return (0, import_node_path6.join)(daemonDir(env), "daemon-token");
4923
+ return (0, import_node_path7.join)(daemonDir(env), "daemon-token");
4887
4924
  }
4888
4925
  function ensureDaemonDir(env = process.env) {
4889
4926
  const dir = daemonDir(env);
4890
- (0, import_node_fs8.mkdirSync)(dir, { recursive: true, mode: 448 });
4891
- if (process.platform !== "win32") (0, import_node_fs8.chmodSync)(dir, 448);
4927
+ (0, import_node_fs9.mkdirSync)(dir, { recursive: true, mode: 448 });
4928
+ if (process.platform !== "win32") (0, import_node_fs9.chmodSync)(dir, 448);
4892
4929
  }
4893
4930
  function writeDaemonToken(env = process.env, token = (0, import_node_crypto4.randomBytes)(32).toString("hex")) {
4894
4931
  ensureDaemonDir(env);
4895
- (0, import_node_fs8.writeFileSync)(tokenPath(env), token, { mode: 384 });
4896
- if (process.platform !== "win32") (0, import_node_fs8.chmodSync)(tokenPath(env), 384);
4932
+ (0, import_node_fs9.writeFileSync)(tokenPath(env), token, { mode: 384 });
4933
+ if (process.platform !== "win32") (0, import_node_fs9.chmodSync)(tokenPath(env), 384);
4897
4934
  return token;
4898
4935
  }
4899
4936
  function tokensEqual(a, b) {
@@ -4912,7 +4949,7 @@ function socketPath(env = process.env, platform2 = process.platform, user) {
4912
4949
  }
4913
4950
  }
4914
4951
  const hash = (0, import_node_crypto4.createHash)("sha256").update(name).digest("hex").slice(0, 12);
4915
- return platform2 === "win32" ? `\\\\.\\pipe\\mmi-cli-${hash}` : (0, import_node_path6.join)(daemonDir(env), `mmi-cli-${hash}.sock`);
4952
+ return platform2 === "win32" ? `\\\\.\\pipe\\mmi-cli-${hash}` : (0, import_node_path7.join)(daemonDir(env), `mmi-cli-${hash}.sock`);
4916
4953
  }
4917
4954
  var HOT_VERBS = /* @__PURE__ */ new Set(["note", "probe", "capture", "session", "head-update"]);
4918
4955
  function argvReadsStdin(args) {
@@ -4962,7 +4999,7 @@ var LineBuffer = class {
4962
4999
  function clientStamp() {
4963
5000
  let mtime = 0;
4964
5001
  try {
4965
- mtime = (0, import_node_fs9.statSync)((0, import_node_path7.join)(__dirname, "index.cjs")).mtimeMs;
5002
+ mtime = (0, import_node_fs10.statSync)((0, import_node_path8.join)(__dirname, "index.cjs")).mtimeMs;
4966
5003
  } catch {
4967
5004
  }
4968
5005
  return buildStamp(resolveClientVersion(), mtime);
@@ -5070,7 +5107,7 @@ function startDaemon(deps = {}) {
5070
5107
  const cleanupSocketFile = () => {
5071
5108
  if (process.platform !== "win32") {
5072
5109
  try {
5073
- (0, import_node_fs10.unlinkSync)(path2);
5110
+ (0, import_node_fs11.unlinkSync)(path2);
5074
5111
  } catch {
5075
5112
  }
5076
5113
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "2.44.0",
3
+ "version": "2.45.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",