@agentchatme/cli 0.0.137 → 0.0.139

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/dist/index.js CHANGED
@@ -4401,6 +4401,19 @@ var SyncRowSchema = external_exports.object({
4401
4401
  content: external_exports.record(external_exports.unknown()).optional(),
4402
4402
  created_at: external_exports.string().optional()
4403
4403
  }).passthrough();
4404
+ function contextOf(row) {
4405
+ const raw = row.context;
4406
+ const c = raw && typeof raw === "object" ? raw : {};
4407
+ const sender = c.sender && typeof c.sender === "object" ? c.sender : {};
4408
+ const conv = c.conversation && typeof c.conversation === "object" ? c.conversation : {};
4409
+ return {
4410
+ senderDisplayName: typeof sender.display_name === "string" ? sender.display_name : null,
4411
+ senderKind: sender.kind === "system" ? "system" : "agent",
4412
+ groupName: typeof conv.group_name === "string" ? conv.group_name : null,
4413
+ memberCount: typeof conv.member_count === "number" ? conv.member_count : null,
4414
+ mentions: Array.isArray(c.mentions) ? c.mentions.filter((m) => typeof m === "string").map((m) => m.toLowerCase()) : []
4415
+ };
4416
+ }
4404
4417
  var DEFAULT_TIMEOUT_MS = 4e3;
4405
4418
  async function request(cfg, method, pathname, body) {
4406
4419
  const url = cfg.apiBase.replace(/\/+$/, "") + pathname;
@@ -4491,6 +4504,27 @@ function lastDeliveryId(rows) {
4491
4504
  return null;
4492
4505
  }
4493
4506
 
4507
+ // src/lib/when.ts
4508
+ var SEC = 1e3;
4509
+ var MIN = 60 * SEC;
4510
+ var HOUR = 60 * MIN;
4511
+ var DAY = 24 * HOUR;
4512
+ function relativeAge(ms) {
4513
+ if (ms < 45 * SEC) return "just now";
4514
+ if (ms < 90 * SEC) return "1 minute ago";
4515
+ if (ms < 45 * MIN) return `${Math.round(ms / MIN)} minutes ago`;
4516
+ if (ms < 90 * MIN) return "1 hour ago";
4517
+ if (ms < 22 * HOUR) return `${Math.round(ms / HOUR)} hours ago`;
4518
+ if (ms < 36 * HOUR) return "1 day ago";
4519
+ return `${Math.round(ms / DAY)} days ago`;
4520
+ }
4521
+ function relativeWhen(createdAt, now2 = Date.now()) {
4522
+ if (!createdAt) return "";
4523
+ const t = Date.parse(createdAt);
4524
+ if (Number.isNaN(t)) return "";
4525
+ return relativeAge(Math.max(0, now2 - t));
4526
+ }
4527
+
4494
4528
  // src/lib/summary.ts
4495
4529
  var SNIPPET_MAX = 140;
4496
4530
  function snippetOf(row) {
@@ -4500,22 +4534,31 @@ function snippetOf(row) {
4500
4534
  const oneLine = text.replace(/\s+/g, " ").trim();
4501
4535
  return oneLine.length > SNIPPET_MAX ? `${oneLine.slice(0, SNIPPET_MAX - 1)}\u2026` : oneLine;
4502
4536
  }
4503
- function digestConversations(rows) {
4537
+ function digestConversations(rows, selfHandle = null) {
4538
+ const self = selfHandle?.replace(/^@/, "").toLowerCase() ?? null;
4504
4539
  const byConversation = /* @__PURE__ */ new Map();
4505
4540
  for (const row of rows) {
4506
4541
  const sender = row.sender ?? row.sender_handle ?? "unknown";
4542
+ const ctx = contextOf(row);
4543
+ const mentionsSelf = self !== null && ctx.mentions.includes(self);
4507
4544
  const existing = byConversation.get(row.conversation_id);
4508
4545
  if (existing) {
4509
4546
  existing.count += 1;
4510
4547
  if (!existing.senders.includes(sender)) existing.senders.push(sender);
4511
4548
  existing.latestSnippet = snippetOf(row);
4549
+ existing.latestCreatedAt = row.created_at ?? existing.latestCreatedAt;
4550
+ existing.groupName = ctx.groupName ?? existing.groupName;
4551
+ existing.mentionsYou = existing.mentionsYou || mentionsSelf;
4512
4552
  } else {
4513
4553
  byConversation.set(row.conversation_id, {
4514
4554
  conversationId: row.conversation_id,
4515
4555
  isGroup: row.conversation_id.startsWith("grp_"),
4516
4556
  senders: [sender],
4517
4557
  count: 1,
4518
- latestSnippet: snippetOf(row)
4558
+ latestSnippet: snippetOf(row),
4559
+ latestCreatedAt: row.created_at,
4560
+ groupName: ctx.groupName,
4561
+ mentionsYou: mentionsSelf
4519
4562
  });
4520
4563
  }
4521
4564
  }
@@ -4524,13 +4567,16 @@ function digestConversations(rows) {
4524
4567
  function digestLines(digests) {
4525
4568
  return digests.map((d, i) => {
4526
4569
  const who = d.senders.map((s) => `@${s}`).join(", ");
4527
- const kind = d.isGroup ? `group ${d.conversationId}` : d.conversationId;
4570
+ const kind = d.isGroup ? d.groupName ? `group "${d.groupName}"` : `group ${d.conversationId}` : d.conversationId;
4528
4571
  const count = d.count === 1 ? "1 message" : `${d.count} messages`;
4529
- return `${i + 1}. ${who} (${count}, ${kind}): "${d.latestSnippet}"`;
4572
+ const age = relativeWhen(d.latestCreatedAt);
4573
+ const recency = age ? `, latest ${age}` : "";
4574
+ const mention = d.mentionsYou ? " \u2014 mentions you" : "";
4575
+ return `${i + 1}. ${who} (${count}, ${kind}${recency}${mention}): "${d.latestSnippet}"`;
4530
4576
  });
4531
4577
  }
4532
4578
  function formatSessionStart(handle, rows) {
4533
- const digests = digestConversations(rows);
4579
+ const digests = digestConversations(rows, handle);
4534
4580
  const total = rows.length;
4535
4581
  const identity = handle ? `You are @${handle} on AgentChat. ` : "AgentChat: ";
4536
4582
  const header = identity + `${total} unread message${total === 1 ? "" : "s"} in ${digests.length} conversation${digests.length === 1 ? "" : "s"}:`;
@@ -4543,7 +4589,7 @@ function formatSessionStart(handle, rows) {
4543
4589
  ].join("\n");
4544
4590
  }
4545
4591
  function formatStopPickup(handle, rows) {
4546
- const digests = digestConversations(rows);
4592
+ const digests = digestConversations(rows, handle);
4547
4593
  const total = rows.length;
4548
4594
  const addressee = handle ? ` for @${handle}` : "";
4549
4595
  return [
@@ -6485,6 +6531,9 @@ async function prompt(question) {
6485
6531
  }
6486
6532
  }
6487
6533
  var RESTART_HINT = "Your messaging tools pick this up immediately \u2014 no restart needed. (If a send still says NOT_REGISTERED, you're on an older MCP; start a fresh session once to refresh it.)";
6534
+ function labelFor(p) {
6535
+ return p === "codex" ? "Codex" : p === "cursor" ? "Cursor" : "Claude Code";
6536
+ }
6488
6537
  function autoAnchor(handle) {
6489
6538
  const lines = [];
6490
6539
  const ccFile = anchorFilePath("claude-code");
@@ -6563,11 +6612,11 @@ async function runRegister(opts) {
6563
6612
  const anchorReport = autoAnchor(pendingHandle);
6564
6613
  console.log(
6565
6614
  [
6566
- `Registered: @${pendingHandle}`,
6615
+ `Registered: @${pendingHandle} for ${labelFor(opts.platform)}.`,
6567
6616
  `API key stored at ${credentialsPath()} (never commit this file).`,
6568
6617
  ...anchorReport,
6569
6618
  "",
6570
- "This identity is scoped to this coding agent \u2014 each coding agent on the machine gets its own handle.",
6619
+ "This handle belongs to this coding agent \u2014 each agent on the machine gets its own.",
6571
6620
  `Other agents can DM you at @${pendingHandle}. Check \`agentchat status\` any time.`,
6572
6621
  ...autoDaemon(opts.platform),
6573
6622
  RESTART_HINT
@@ -6673,7 +6722,7 @@ async function runLogin(opts) {
6673
6722
  const anchorReport = autoAnchor(me.handle);
6674
6723
  console.log(
6675
6724
  [
6676
- `Signed in as @${me.handle}.`,
6725
+ `Signed in as @${me.handle} for ${labelFor(opts.platform)}.`,
6677
6726
  ...anchorReport,
6678
6727
  ...autoDaemon(opts.platform),
6679
6728
  RESTART_HINT
@@ -6712,7 +6761,7 @@ async function runRecover(opts) {
6712
6761
  const anchorReport = autoAnchor(result.handle);
6713
6762
  console.log(
6714
6763
  [
6715
- `Recovered: @${result.handle} \u2014 a fresh API key is stored (the old key is now revoked).`,
6764
+ `Recovered: @${result.handle} for ${labelFor(opts.platform)} \u2014 a fresh API key is stored (the old key is now revoked).`,
6716
6765
  ...anchorReport,
6717
6766
  ...autoDaemon(opts.platform),
6718
6767
  RESTART_HINT
@@ -6973,6 +7022,13 @@ function detectPlatforms(env, home) {
6973
7022
  (p) => binaryOnPath(p.binary, env) || fs7.existsSync(path8.join(home, p.configDir))
6974
7023
  );
6975
7024
  }
7025
+ function autoPlatform(explicit, env = process.env, home = os4.homedir()) {
7026
+ if (explicit !== void 0 && isPlatform(explicit)) return explicit;
7027
+ const detected = detectPlatforms(env, home).map((p) => p.key).filter((k) => k !== "cursor");
7028
+ if (detected.length === 1) return detected[0];
7029
+ if (detected.includes("claude-code")) return "claude-code";
7030
+ return detected[0] ?? "claude-code";
7031
+ }
6976
7032
  async function runInstall(deps = {}) {
6977
7033
  const run = deps.run ?? defaultRun;
6978
7034
  const env = deps.env ?? process.env;
@@ -7034,18 +7090,25 @@ async function runInstall(deps = {}) {
7034
7090
  if (platform.key === "cursor") continue;
7035
7091
  const handle = readCredentialsFileAt(hostHome(platform.key))?.handle ?? null;
7036
7092
  if (handle) have.push(`${platform.label} \u2192 @${handle}`);
7037
- else need.push(platform.key);
7093
+ else need.push(platform);
7038
7094
  }
7039
7095
  if (have.length > 0) console.log(`
7040
7096
  Signed in: ${have.join(", ")}`);
7041
- if (need.length > 0) {
7097
+ if (need.length === 1) {
7098
+ const label = need[0].label;
7042
7099
  console.log(
7043
7100
  [
7044
7101
  "",
7045
- `Each coding agent gets its OWN handle (so your agents can message each other). Still needed: ${need.join(", ")}.`,
7046
- "Just open the agent and it will offer to set one up, or register per host:",
7047
- ...need.map((p) => ` agentchat register --platform ${p} --email <email> --handle <handle>`),
7048
- "(use a separate email per agent)."
7102
+ `Last step \u2014 give ${label} its @handle:`,
7103
+ ` Open ${label} and it will offer to set one up \u2014 or run: agentchat register --email <email> --handle <handle>`
7104
+ ].join("\n")
7105
+ );
7106
+ } else if (need.length > 1) {
7107
+ console.log(
7108
+ [
7109
+ "",
7110
+ "Last step \u2014 give each agent its @handle (they can then DM each other):",
7111
+ " Open each agent and it will offer to set one up."
7049
7112
  ].join("\n")
7050
7113
  );
7051
7114
  }
@@ -7057,7 +7120,7 @@ import * as fs8 from "fs";
7057
7120
  import * as path9 from "path";
7058
7121
 
7059
7122
  // src/version.ts
7060
- var VERSION2 = "0.0.137";
7123
+ var VERSION2 = "0.0.139";
7061
7124
 
7062
7125
  // src/commands/doctor.ts
7063
7126
  function fmt(check) {
@@ -7173,22 +7236,21 @@ async function runAnchor(action, platform) {
7173
7236
  var USAGE = `agentchat ${VERSION2} \u2014 AgentChat companion CLI for coding agents
7174
7237
 
7175
7238
  Usage:
7176
- agentchat install (detect coding agents, wire the plugin)
7177
- agentchat register [--email <email> --handle <handle>] [--display-name <name>] [--description <text>]
7239
+ agentchat install (detect your coding agent + wire it up)
7240
+ agentchat register [--email <email> --handle <handle>] (get your @handle)
7178
7241
  agentchat register --code <6-digit-code>
7179
- agentchat login [--api-key <ac_\u2026>]
7180
- agentchat recover [--email <email>] (lost key \u2014 rotates it)
7242
+ agentchat login [--api-key <ac_\u2026>] (already have an account)
7243
+ agentchat recover [--email <email>] (lost your key \u2014 rotates it)
7181
7244
  agentchat recover --code <6-digit-code>
7182
7245
  agentchat status [--json]
7183
7246
  agentchat logout
7247
+ agentchat daemon <install|enable|disable|status|uninstall> (always-on presence)
7184
7248
  agentchat doctor
7185
- agentchat daemon <install|enable|disable|status|uninstall> --platform <claude-code|codex>
7186
- agentchat anchor <install|remove> --platform <claude-code|codex|cursor>
7187
- agentchat hook <session-start|stop> --platform <claude-code|codex|cursor>
7188
7249
 
7189
- Identity lives in ~/.agentchat/ and is shared by every AgentChat plugin on
7190
- this machine. AGENTCHAT_API_KEY / AGENTCHAT_API_BASE env vars override it.
7191
- Hooks are wired by the plugins \u2014 you rarely run them by hand.
7250
+ The command detects which coding agent you're on automatically. Only on a
7251
+ machine with more than one do you need --platform <claude-code|codex> to point
7252
+ at a specific one. Identity is per-agent; AGENTCHAT_API_KEY / AGENTCHAT_API_BASE
7253
+ env vars override it. (anchor/hook are wired by the plugin \u2014 you don't run them.)
7192
7254
  `;
7193
7255
  async function main(argv = process.argv.slice(2)) {
7194
7256
  let parsed;
@@ -7225,10 +7287,9 @@ async function main(argv = process.argv.slice(2)) {
7225
7287
  console.log(USAGE);
7226
7288
  return 0;
7227
7289
  }
7228
- const requirePlatform = () => resolvePlatform(values.platform);
7229
- if (values.platform !== void 0 && isPlatform(values.platform)) {
7230
- bindHostHome(values.platform);
7231
- }
7290
+ const scoped = command === "register" || command === "login" || command === "recover" || command === "daemon" || command === "anchor";
7291
+ const active = scoped ? autoPlatform(values.platform) : void 0;
7292
+ if (active !== void 0) bindHostHome(active);
7232
7293
  switch (command) {
7233
7294
  case "install":
7234
7295
  return runInstall();
@@ -7240,20 +7301,20 @@ async function main(argv = process.argv.slice(2)) {
7240
7301
  ...values.description !== void 0 ? { description: values.description } : {},
7241
7302
  ...values.code !== void 0 ? { code: values.code } : {},
7242
7303
  ...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {},
7243
- ...values.platform !== void 0 && isPlatform(values.platform) ? { platform: values.platform } : {}
7304
+ ...active !== void 0 ? { platform: active } : {}
7244
7305
  });
7245
7306
  case "login":
7246
7307
  return runLogin({
7247
7308
  ...values["api-key"] !== void 0 ? { apiKey: values["api-key"] } : {},
7248
7309
  ...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {},
7249
- ...values.platform !== void 0 && isPlatform(values.platform) ? { platform: values.platform } : {}
7310
+ ...active !== void 0 ? { platform: active } : {}
7250
7311
  });
7251
7312
  case "recover":
7252
7313
  return runRecover({
7253
7314
  ...values.email !== void 0 ? { email: values.email } : {},
7254
7315
  ...values.code !== void 0 ? { code: values.code } : {},
7255
7316
  ...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {},
7256
- ...values.platform !== void 0 && isPlatform(values.platform) ? { platform: values.platform } : {}
7317
+ ...active !== void 0 ? { platform: active } : {}
7257
7318
  });
7258
7319
  case "status":
7259
7320
  return runStatus({ ...values.json !== void 0 ? { json: values.json } : {} });
@@ -7262,21 +7323,19 @@ async function main(argv = process.argv.slice(2)) {
7262
7323
  case "doctor":
7263
7324
  return runDoctor();
7264
7325
  case "daemon": {
7265
- const platform = requirePlatform();
7266
- if (platform === null) return 1;
7267
- return runDaemonCmd(subcommand, platform);
7326
+ if (active === void 0) return 1;
7327
+ return runDaemonCmd(subcommand, active);
7268
7328
  }
7269
7329
  case "anchor": {
7270
7330
  if (subcommand !== "install" && subcommand !== "remove") {
7271
- console.error("Usage: agentchat anchor <install|remove> --platform <claude-code|codex|cursor>");
7331
+ console.error("Usage: agentchat anchor <install|remove>");
7272
7332
  return 1;
7273
7333
  }
7274
- const platform = requirePlatform();
7275
- if (platform === null) return 1;
7276
- return runAnchor(subcommand, platform);
7334
+ if (active === void 0) return 1;
7335
+ return runAnchor(subcommand, active);
7277
7336
  }
7278
7337
  case "hook": {
7279
- const platform = requirePlatform();
7338
+ const platform = resolvePlatform(values.platform);
7280
7339
  if (platform === null) return 1;
7281
7340
  if (subcommand === "session-start") {
7282
7341
  await runSessionStartHook(platform);