@mutmutco/cli 2.43.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.
- package/README.md +1 -1
- package/dist/main.cjs +10850 -9174
- package/dist/saga.cjs +90 -53
- 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
|
|
4031
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
4084
|
+
const path2 = (0, import_node_path5.join)(dir, name);
|
|
4050
4085
|
try {
|
|
4051
|
-
if (now - (0,
|
|
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,
|
|
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,
|
|
4065
|
-
(0,
|
|
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,
|
|
4070
|
-
(0,
|
|
4071
|
-
(0,
|
|
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,
|
|
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,
|
|
4122
|
+
if (!(0, import_node_fs5.existsSync)(path2)) return [];
|
|
4088
4123
|
const out = [];
|
|
4089
|
-
for (const line of (0,
|
|
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,
|
|
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,
|
|
4142
|
+
(0, import_node_fs5.writeFileSync)(tmp, trimmed.length ? `${body}
|
|
4108
4143
|
` : "", "utf8");
|
|
4109
|
-
(0,
|
|
4144
|
+
(0, import_node_fs5.renameSync)(tmp, pendingPath(dir));
|
|
4110
4145
|
} catch {
|
|
4111
4146
|
try {
|
|
4112
|
-
(0,
|
|
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,
|
|
4161
|
+
(0, import_node_fs5.mkdirSync)(dir, { recursive: true });
|
|
4127
4162
|
sweepStaleTmp(dir);
|
|
4128
|
-
(0,
|
|
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
|
|
4208
|
-
var
|
|
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,
|
|
4223
|
-
return (0,
|
|
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,
|
|
4226
|
-
return (0,
|
|
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,
|
|
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,
|
|
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,
|
|
4277
|
+
(0, import_node_fs6.writeFileSync)(tmp, JSON.stringify(session, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
4243
4278
|
try {
|
|
4244
|
-
(0,
|
|
4279
|
+
(0, import_node_fs6.chmodSync)(tmp, 384);
|
|
4245
4280
|
} catch {
|
|
4246
4281
|
}
|
|
4247
|
-
(0,
|
|
4282
|
+
(0, import_node_fs6.renameSync)(tmp, path2);
|
|
4248
4283
|
try {
|
|
4249
|
-
(0,
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
4463
|
-
(0,
|
|
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
|
|
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
|
|
4870
|
-
var
|
|
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
|
|
4911
|
+
var import_node_fs9 = require("node:fs");
|
|
4875
4912
|
var import_node_os3 = require("node:os");
|
|
4876
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
4891
|
-
if (process.platform !== "win32") (0,
|
|
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,
|
|
4896
|
-
if (process.platform !== "win32") (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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.
|
|
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",
|