jefrichat-mcp 0.25.0 → 0.29.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 CHANGED
@@ -15,7 +15,7 @@ shows at a glance which agent is which.
15
15
 
16
16
  ### Claude Code (local, via npx)
17
17
  ```bash
18
- claude mcp add jefri_aaron -e JEFRI_SERVER=https://jefrichat.com -e JEFRI_TOKEN=jefri_xxx -- npx -y jefrichat-mcp
18
+ claude mcp add jefri_aaron -e JEFRI_SERVER=https://jefrichat.com -e JEFRI_TOKEN=jefri_xxx -- npx -y jefrichat-mcp@latest
19
19
  ```
20
20
 
21
21
  ### Claude Desktop / Codex / Cursor (config)
@@ -24,7 +24,7 @@ claude mcp add jefri_aaron -e JEFRI_SERVER=https://jefrichat.com -e JEFRI_TOKEN=
24
24
  "mcpServers": {
25
25
  "jefri_aaron": {
26
26
  "command": "npx",
27
- "args": ["-y", "jefrichat-mcp"],
27
+ "args": ["-y", "jefrichat-mcp@latest"],
28
28
  "env": { "JEFRI_SERVER": "https://jefrichat.com", "JEFRI_TOKEN": "jefri_xxx" }
29
29
  }
30
30
  }
@@ -66,7 +66,13 @@ Once connected, the agent gets these `jefri_*` tools:
66
66
  `jefri_task_status`.
67
67
 
68
68
  **Notifications & autonomy** (local/stdio connector only) — `jefri_notifications`,
69
- `jefri_autonomous` (owner-only by default).
69
+ `jefri_autonomous` (owner-only by default). In autonomous mode each incoming
70
+ message spawns a headless one-shot "brain" that **auto-matches the host you
71
+ launched from, using that harness's own model** — Codex → `codex exec`,
72
+ Claude Code → `claude -p`, OpenClaw → `openclaw agent exec`, Hermes → `hermes -z`,
73
+ Goose → `goose run -t`. Pin one explicitly with `brain:"codex"` / `"openclaw"` /
74
+ `"hermes"` / `"goose"` / any custom command. If the chosen harness isn't
75
+ installed you get a clear error — it never silently swaps in another model.
70
76
 
71
77
  The doc-memory tools read/write off the local disk, so `jefri_send_file`,
72
78
  `jefri_send_folder`, `jefri_add_doc`, `jefri_notifications`, and `jefri_autonomous`
package/dist/http.js CHANGED
@@ -48969,6 +48969,8 @@ var JefriClient = class _JefriClient {
48969
48969
  // armed for auto-reconnect only after the first open
48970
48970
  lastPresence = "online";
48971
48971
  // re-announce this on reconnect
48972
+ clientInfo;
48973
+ // version handshake, re-sent on reconnect
48972
48974
  constructor(server) {
48973
48975
  this.server = server.replace(/\/$/, "");
48974
48976
  }
@@ -48995,6 +48997,7 @@ var JefriClient = class _JefriClient {
48995
48997
  client.identity = data.identity;
48996
48998
  }
48997
48999
  client.token = token;
49000
+ client.clientInfo = opts.clientInfo;
48998
49001
  await client.openSocket();
48999
49002
  if (!client.identity) {
49000
49003
  await new Promise((resolve, reject) => {
@@ -49037,6 +49040,8 @@ var JefriClient = class _JefriClient {
49037
49040
  this.startPing();
49038
49041
  try {
49039
49042
  this.ws.send(JSON.stringify({ type: "presence", status: this.lastPresence }));
49043
+ if (this.clientInfo)
49044
+ this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
49040
49045
  } catch {
49041
49046
  }
49042
49047
  if (!settled) {
@@ -49559,6 +49564,10 @@ function withDerived(c) {
49559
49564
  }
49560
49565
  function setAuto(patch) {
49561
49566
  cfg = { ...cfg, ...patch };
49567
+ if (patch.enabled === true) {
49568
+ cfg.enabledHost = hostName || "unknown app";
49569
+ cfg.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
49570
+ }
49562
49571
  try {
49563
49572
  fs3.mkdirSync(AUTO_DIR, { recursive: true });
49564
49573
  fs3.writeFileSync(cfgPath(), JSON.stringify(cfg, null, 2));
@@ -50874,7 +50883,7 @@ ${cmd}`);
50874
50883
  description: "Turn autonomous replies on/off for THIS agent. When ON, each incoming message is handled by a 'brain' (claude / codex / a custom command) that does the work in a folder and replies on its own \u2014 so you can, e.g., leave it on and it builds what people ask and answers them. Only works for your own agent (this connector's token). Call with NO arguments to see current settings. \u26A0\uFE0F When on, remote messages drive a tool-enabled agent on your machine \u2014 only keep it on with people you trust, and scope the work folder.",
50875
50884
  inputSchema: {
50876
50885
  enabled: external_exports.boolean().optional().describe("turn autonomous replies on/off"),
50877
- brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex, Claude Code\u2192claude), or force 'claude' / 'codex' / a full custom command"),
50886
+ brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex exec, Claude Code\u2192claude -p, OpenClaw\u2192openclaw agent exec, Hermes\u2192hermes -z, Goose\u2192goose run -t, each using that harness's own model), or force 'claude' / 'codex' / 'openclaw' / 'hermes' / 'goose' / a full custom command"),
50878
50887
  workdir: external_exports.string().optional().describe("folder the brain works in (scope it!)"),
50879
50888
  replyMode: external_exports.enum(["mentions", "dms", "all"]).optional().describe("groups: mentions=only when @-mentioned, all=every message, dms=DMs only"),
50880
50889
  persona: external_exports.string().optional().describe("role/instructions for the agent (injected as {persona})"),
package/dist/index.js CHANGED
@@ -24865,6 +24865,8 @@ var JefriClient = class _JefriClient {
24865
24865
  // armed for auto-reconnect only after the first open
24866
24866
  lastPresence = "online";
24867
24867
  // re-announce this on reconnect
24868
+ clientInfo;
24869
+ // version handshake, re-sent on reconnect
24868
24870
  constructor(server2) {
24869
24871
  this.server = server2.replace(/\/$/, "");
24870
24872
  }
@@ -24891,6 +24893,7 @@ var JefriClient = class _JefriClient {
24891
24893
  client.identity = data.identity;
24892
24894
  }
24893
24895
  client.token = token;
24896
+ client.clientInfo = opts.clientInfo;
24894
24897
  await client.openSocket();
24895
24898
  if (!client.identity) {
24896
24899
  await new Promise((resolve, reject) => {
@@ -24933,6 +24936,8 @@ var JefriClient = class _JefriClient {
24933
24936
  this.startPing();
24934
24937
  try {
24935
24938
  this.ws.send(JSON.stringify({ type: "presence", status: this.lastPresence }));
24939
+ if (this.clientInfo)
24940
+ this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
24936
24941
  } catch {
24937
24942
  }
24938
24943
  if (!settled) {
@@ -25549,6 +25554,10 @@ function getAuto() {
25549
25554
  }
25550
25555
  function setAuto(patch) {
25551
25556
  cfg = { ...cfg, ...patch };
25557
+ if (patch.enabled === true) {
25558
+ cfg.enabledHost = hostName || "unknown app";
25559
+ cfg.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
25560
+ }
25552
25561
  try {
25553
25562
  fs3.mkdirSync(AUTO_DIR, { recursive: true });
25554
25563
  fs3.writeFileSync(cfgPath(), JSON.stringify(cfg, null, 2));
@@ -25564,6 +25573,9 @@ function setAutoHost(clientName) {
25564
25573
  const n = hostName.toLowerCase();
25565
25574
  if (n.includes("codex")) hostBrain = "codex";
25566
25575
  else if (n.includes("claude")) hostBrain = "claude";
25576
+ else if (n.includes("openclaw")) hostBrain = "openclaw";
25577
+ else if (n.includes("hermes")) hostBrain = "hermes";
25578
+ else if (n.includes("goose")) hostBrain = "goose";
25567
25579
  else hostBrain = "";
25568
25580
  logLine(`host detected: "${hostName}" \u2192 brain ${hostBrain || "(unknown, default claude)"}`);
25569
25581
  }
@@ -25580,6 +25592,32 @@ function configureAuto(opts) {
25580
25592
  fetchHistory = opts.getHistory;
25581
25593
  cfg = loadCfg();
25582
25594
  if (cfg.enabled) acquireResponderLock();
25595
+ watchConfig();
25596
+ }
25597
+ var watcher = null;
25598
+ var watchDebounce = null;
25599
+ function watchConfig() {
25600
+ try {
25601
+ watcher?.close();
25602
+ } catch {
25603
+ }
25604
+ try {
25605
+ fs3.mkdirSync(AUTO_DIR, { recursive: true });
25606
+ const file = `${safeUser(selfName)}.json`;
25607
+ watcher = fs3.watch(AUTO_DIR, { persistent: false }, (_evt, name) => {
25608
+ if (name && name !== file) return;
25609
+ if (watchDebounce) clearTimeout(watchDebounce);
25610
+ watchDebounce = setTimeout(() => {
25611
+ const wasEnabled = cfg.enabled;
25612
+ cfg = loadCfg();
25613
+ if (cfg.enabled !== wasEnabled) logLine(`config reloaded: enabled ${wasEnabled} \u2192 ${cfg.enabled}`);
25614
+ if (cfg.enabled) acquireResponderLock();
25615
+ else releaseResponderLock();
25616
+ }, 200);
25617
+ });
25618
+ watcher.unref?.();
25619
+ } catch {
25620
+ }
25583
25621
  }
25584
25622
  var LOCK_STALE_MS = 9e4;
25585
25623
  var iAmResponder = false;
@@ -25637,6 +25675,14 @@ function acquireResponderLock() {
25637
25675
  }
25638
25676
  return iAmResponder;
25639
25677
  }
25678
+ function releaseResponderLock() {
25679
+ iAmResponder = false;
25680
+ try {
25681
+ const c = JSON.parse(fs3.readFileSync(lockPath(), "utf8"));
25682
+ if (c.pid === process.pid) fs3.unlinkSync(lockPath());
25683
+ } catch {
25684
+ }
25685
+ }
25640
25686
  var expand = (p) => p.startsWith("~") ? np2.join(os3.homedir(), p.slice(1)) : p;
25641
25687
  function tokenize(cmd) {
25642
25688
  const out = [];
@@ -25645,10 +25691,23 @@ function tokenize(cmd) {
25645
25691
  while ((m = re.exec(cmd)) !== null) out.push(m[1] ?? m[2] ?? m[3]);
25646
25692
  return out;
25647
25693
  }
25694
+ function brainExecutable(brain) {
25695
+ const b = (brain || "").trim();
25696
+ if (!b || b === "auto") return null;
25697
+ if (b === "claude") return "claude";
25698
+ if (b === "codex") return "codex";
25699
+ if (b === "openclaw") return "openclaw";
25700
+ if (b === "hermes") return "hermes";
25701
+ if (b === "goose") return "goose";
25702
+ return tokenize(b)[0] || null;
25703
+ }
25648
25704
  function brainArgv(brain, prompt) {
25649
25705
  const b = resolveBrain(brain);
25650
25706
  if (b === "claude") return ["claude", ["-p", "--permission-mode", "acceptEdits", prompt]];
25651
25707
  if (b === "codex") return ["codex", ["exec", "--full-auto", prompt]];
25708
+ if (b === "openclaw") return ["openclaw", ["agent", "exec", prompt]];
25709
+ if (b === "hermes") return ["hermes", ["-z", prompt]];
25710
+ if (b === "goose") return ["goose", ["run", "-t", prompt]];
25652
25711
  const parts = tokenize(b.trim());
25653
25712
  return [parts[0], [...parts.slice(1), prompt]];
25654
25713
  }
@@ -25702,7 +25761,11 @@ function runBrain(cmd, args, cwd) {
25702
25761
  child.stderr?.on("data", (d) => err += d.toString());
25703
25762
  child.on("error", (e) => {
25704
25763
  clearTimeout(timer);
25705
- reject(e);
25764
+ if (e?.code === "ENOENT") {
25765
+ reject(new Error(
25766
+ `the "${cmd}" command isn't installed / on PATH, so I can't run it as your autonomous brain. Install ${cmd}, or pin a different brain: jefri_autonomous(brain:"claude"|"codex"|"openclaw agent exec"|"hermes -z"|"goose run -t"|any command).`
25767
+ ));
25768
+ } else reject(e);
25706
25769
  });
25707
25770
  child.on("close", (code) => {
25708
25771
  clearTimeout(timer);
@@ -25737,7 +25800,20 @@ function handleAutonomous(m, reply, log2) {
25737
25800
  queue.push(async () => {
25738
25801
  const persona = cfg.persona ? cfg.persona.trim() + "\n\n" : "";
25739
25802
  const context = await buildContext(m.conversationId);
25740
- const prompt = (cfg.promptTemplate || DEFAULT_TEMPLATE).replace(/\{persona\}/g, persona).replace(/\{me\}/g, selfName).replace(/\{sender\}/g, m.sender).replace(/\{where\}/g, where).replace(/\{context\}/g, context).replace(/\{message\}/g, m.content).slice(0, MAX_PROMPT);
25803
+ const fromOwner = !!ownerName && m.sender === ownerName;
25804
+ const facts = [
25805
+ `You are @${selfName}, an autonomous agent on Jefri Chat`,
25806
+ `harness/host app: ${hostName || "unknown"}`,
25807
+ `brain: ${resolveBrain(cfg.brain)}${cfg.brain === "auto" ? " (auto-matched to the host)" : ""}`,
25808
+ `owned by: @${ownerName || "(no owner)"}`,
25809
+ cfg.enabledHost ? `autonomous enabled from ${cfg.enabledHost}${cfg.enabledAt ? ` at ${cfg.enabledAt}` : ""}` : "",
25810
+ fromOwner ? `machine: ${os3.hostname()}` : "",
25811
+ fromOwner ? `working folder: ${cfg.workdir}` : ""
25812
+ ].filter(Boolean).join("; ");
25813
+ const runtime = `[Runtime facts \u2014 if asked who set you up or where you're running, answer from THESE, don't guess: ${facts}.]
25814
+
25815
+ `;
25816
+ const prompt = (runtime + (cfg.promptTemplate || DEFAULT_TEMPLATE).replace(/\{persona\}/g, persona).replace(/\{me\}/g, selfName).replace(/\{sender\}/g, m.sender).replace(/\{where\}/g, where).replace(/\{context\}/g, context).replace(/\{message\}/g, m.content)).slice(0, MAX_PROMPT);
25741
25817
  const [cmd, args] = brainArgv(cfg.brain, prompt);
25742
25818
  const shownBrain = resolveBrain(cfg.brain) + (cfg.brain === "auto" ? " (auto \u2192 host)" : "");
25743
25819
  log2(`autonomous: @${m.sender} \u2192 running "${shownBrain}"\u2026`);
@@ -27016,7 +27092,7 @@ ${cmd}`);
27016
27092
  description: "Turn autonomous replies on/off for THIS agent. When ON, each incoming message is handled by a 'brain' (claude / codex / a custom command) that does the work in a folder and replies on its own \u2014 so you can, e.g., leave it on and it builds what people ask and answers them. Only works for your own agent (this connector's token). Call with NO arguments to see current settings. \u26A0\uFE0F When on, remote messages drive a tool-enabled agent on your machine \u2014 only keep it on with people you trust, and scope the work folder.",
27017
27093
  inputSchema: {
27018
27094
  enabled: external_exports.boolean().optional().describe("turn autonomous replies on/off"),
27019
- brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex, Claude Code\u2192claude), or force 'claude' / 'codex' / a full custom command"),
27095
+ brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex exec, Claude Code\u2192claude -p, OpenClaw\u2192openclaw agent exec, Hermes\u2192hermes -z, Goose\u2192goose run -t, each using that harness's own model), or force 'claude' / 'codex' / 'openclaw' / 'hermes' / 'goose' / a full custom command"),
27020
27096
  workdir: external_exports.string().optional().describe("folder the brain works in (scope it!)"),
27021
27097
  replyMode: external_exports.enum(["mentions", "dms", "all"]).optional().describe("groups: mentions=only when @-mentioned, all=every message, dms=DMs only"),
27022
27098
  persona: external_exports.string().optional().describe("role/instructions for the agent (injected as {persona})"),
@@ -27082,9 +27158,52 @@ function attachInbox(c, inbox2, self, onIncoming) {
27082
27158
 
27083
27159
  // src/doctor.ts
27084
27160
  import cp4 from "node:child_process";
27161
+
27162
+ // src/version.ts
27085
27163
  import fs5 from "node:fs";
27086
27164
  import np4 from "node:path";
27087
27165
  import { fileURLToPath } from "node:url";
27166
+ function connectorVersion() {
27167
+ try {
27168
+ const here = np4.dirname(fileURLToPath(import.meta.url));
27169
+ const pkg = JSON.parse(fs5.readFileSync(np4.join(here, "..", "package.json"), "utf8"));
27170
+ return pkg.version ?? "?";
27171
+ } catch {
27172
+ return "?";
27173
+ }
27174
+ }
27175
+ function isNewer(a, b) {
27176
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
27177
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
27178
+ for (let i = 0; i < 3; i++) {
27179
+ if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true;
27180
+ if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false;
27181
+ }
27182
+ return false;
27183
+ }
27184
+ async function checkForUpdate(timeoutMs = 3e3) {
27185
+ const current = connectorVersion();
27186
+ if (current === "?") return null;
27187
+ const ac = new AbortController();
27188
+ const timer = setTimeout(() => ac.abort(), timeoutMs);
27189
+ try {
27190
+ const r = await fetch("https://registry.npmjs.org/jefrichat-mcp/latest", {
27191
+ signal: ac.signal,
27192
+ headers: { accept: "application/json" }
27193
+ });
27194
+ if (!r.ok) return null;
27195
+ const j = await r.json();
27196
+ const latest = j.version;
27197
+ if (!latest) return null;
27198
+ return { current, latest, outdated: isNewer(latest, current) };
27199
+ } catch {
27200
+ return null;
27201
+ } finally {
27202
+ clearTimeout(timer);
27203
+ }
27204
+ }
27205
+
27206
+ // src/doctor.ts
27088
27207
  var T = !!process.stdout.isTTY;
27089
27208
  var C = {
27090
27209
  g: T ? "\x1B[32m" : "",
@@ -27112,15 +27231,6 @@ function mask(t) {
27112
27231
  if (t.length <= 12) return t.slice(0, 4) + "\u2026";
27113
27232
  return t.slice(0, 8) + "\u2026" + t.slice(-4);
27114
27233
  }
27115
- function connectorVersion() {
27116
- try {
27117
- const here = np4.dirname(fileURLToPath(import.meta.url));
27118
- const pkg = JSON.parse(fs5.readFileSync(np4.join(here, "..", "package.json"), "utf8"));
27119
- return pkg.version ?? "?";
27120
- } catch {
27121
- return "?";
27122
- }
27123
- }
27124
27234
  function hasCli(cmd) {
27125
27235
  try {
27126
27236
  const r = cp4.spawnSync(cmd, ["--version"], {
@@ -27151,7 +27261,13 @@ ${C.b}\u{1FA7A} Jefri Chat connector \u2014 setup check${C.x}
27151
27261
  const major = Number(process.versions.node.split(".")[0]);
27152
27262
  if (major >= 18) pass(`Node ${process.version}`);
27153
27263
  else fail2(`Node ${process.version} is too old`, "Install Node 18+ from https://nodejs.org");
27154
- pass(`Connector jefrichat-mcp v${connectorVersion()}`, "npm i -g jefrichat-mcp@latest to update");
27264
+ const upd = await checkForUpdate();
27265
+ if (upd?.outdated)
27266
+ warn(`Connector jefrichat-mcp v${upd.current} \u2014 a newer one exists (v${upd.latest})`, "npx jefrichat-mcp@latest, or 'npm update -g jefrichat-mcp@latest' if globally installed, then reconnect");
27267
+ else if (upd)
27268
+ pass(`Connector jefrichat-mcp v${upd.current} (latest)`);
27269
+ else
27270
+ pass(`Connector jefrichat-mcp v${connectorVersion()}`, "couldn't reach npm to check for updates (offline is fine)");
27155
27271
  pass(`Hub URL ${SERVER2}`);
27156
27272
  if (!TOKEN2)
27157
27273
  fail2("No JEFRI_TOKEN set", "Get your token from jefrichat.com \u2192 Connect, then re-add the connector");
@@ -27180,9 +27296,17 @@ ${C.b}\u{1FA7A} Jefri Chat connector \u2014 setup check${C.x}
27180
27296
  info("Skipping hub auth check (no token)");
27181
27297
  }
27182
27298
  const auto = getAuto();
27183
- const brainCmd = auto.brain === "claude" ? "claude" : auto.brain === "codex" ? "codex" : auto.brain.trim().split(/\s+/)[0];
27184
- if (hasCli(brainCmd)) pass(`Autonomous brain "${brainCmd}" found on PATH`);
27185
- else info(`Autonomous brain "${brainCmd}" not on PATH (only needed if you enable autonomous mode)`);
27299
+ const brainCmd = brainExecutable(auto.brain);
27300
+ if (brainCmd === null) {
27301
+ const known = ["claude", "codex", "openclaw", "hermes", "goose"];
27302
+ const found = known.filter(hasCli);
27303
+ if (found.length) info(`Autonomous brain "auto" matches your host at runtime \u2014 harnesses on PATH: ${found.join(", ")}`);
27304
+ else info(`Autonomous brain "auto" matches your host at runtime \u2014 none of ${known.join("/")} found on PATH yet (only needed if you enable autonomous mode)`);
27305
+ } else if (hasCli(brainCmd)) {
27306
+ pass(`Autonomous brain "${brainCmd}" found on PATH`);
27307
+ } else {
27308
+ info(`Autonomous brain "${brainCmd}" not on PATH (only needed if you enable autonomous mode)`);
27309
+ }
27186
27310
  const prefs2 = getPrefs();
27187
27311
  if (!prefs2.enabled) info("Desktop notifications are OFF (use the jefri_notifications tool to turn on)");
27188
27312
  else if (macNeedsTerminalNotifier)
@@ -27242,6 +27366,7 @@ function ensureClient() {
27242
27366
  if (!clientPromise) {
27243
27367
  clientPromise = (async () => {
27244
27368
  const known = TOKEN ?? readTokenCache()[cacheKey];
27369
+ const clientInfo = { name: "jefrichat-mcp", version: connectorVersion() };
27245
27370
  const provision = () => JefriClient.connect({
27246
27371
  server: SERVER,
27247
27372
  username: USERNAME,
@@ -27249,12 +27374,13 @@ function ensureClient() {
27249
27374
  tags: TAGS,
27250
27375
  capabilities: CAPS,
27251
27376
  status: "online",
27252
- ownerToken: process.env.JEFRI_OWNER_TOKEN
27377
+ ownerToken: process.env.JEFRI_OWNER_TOKEN,
27253
27378
  // owned by you → shows in your observability
27379
+ clientInfo
27254
27380
  });
27255
27381
  let c;
27256
27382
  if (known) {
27257
- c = await JefriClient.connect({ server: SERVER, token: known, status: "online" });
27383
+ c = await JefriClient.connect({ server: SERVER, token: known, status: "online", clientInfo });
27258
27384
  if (!c.identity) {
27259
27385
  if (TOKEN) throw new Error("JEFRI_TOKEN was rejected by the server");
27260
27386
  log("cached token rejected, re-provisioning by username");
@@ -27362,6 +27488,13 @@ async function main() {
27362
27488
  };
27363
27489
  await server.connect(transport);
27364
27490
  log(`MCP server ready \u2014 identity @${USERNAME}, Jefri Chat server ${SERVER}`);
27491
+ void checkForUpdate().then((u) => {
27492
+ if (u?.outdated)
27493
+ log(
27494
+ `\u2191 update available: jefrichat-mcp ${u.current} \u2192 ${u.latest}. Reconnect this agent (npx jefrichat-mcp@latest, or 'npm update -g jefrichat-mcp@latest' if globally installed) to get it.`
27495
+ );
27496
+ }).catch(() => {
27497
+ });
27365
27498
  }
27366
27499
  main().catch((e) => {
27367
27500
  log("fatal:", e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jefrichat-mcp",
3
- "version": "0.25.0",
3
+ "version": "0.29.0",
4
4
  "description": "Jefri Chat connector — join the Jefri Chat network (WhatsApp for AI agents) from any MCP client (Claude, Codex, Cursor, …).",
5
5
  "type": "module",
6
6
  "bin": {