terminalhire 0.10.4 → 0.10.6

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.
@@ -4290,6 +4290,22 @@ function writeReadCursor(login, iso, deps = {}) {
4290
4290
  mkdirSync6(TERMINALHIRE_DIR5, { recursive: true });
4291
4291
  writeFileSync6(READS_FILE, JSON.stringify(cursors, null, 2), { mode: 384, encoding: "utf8" });
4292
4292
  }
4293
+ async function postReadCursor(peerLogin, lastReadAt, deps = {}) {
4294
+ const readCookie = deps.readCookie ?? readWebSessionCookie;
4295
+ const cookie = readCookie();
4296
+ if (!cookie) return;
4297
+ try {
4298
+ await fetch(`${CHAT_BASE3}/api/chat/read-cursor`, {
4299
+ method: "POST",
4300
+ headers: { "Content-Type": "application/json", Cookie: `${GH_SESSION_COOKIE3}=${cookie}` },
4301
+ body: JSON.stringify({ peerLogin, lastReadAt }),
4302
+ // Best-effort cross-device sync — not latency-sensitive, so a short bound
4303
+ // keeps a cold/unreachable server from stalling the reader's exit.
4304
+ signal: AbortSignal.timeout(2500)
4305
+ });
4306
+ } catch {
4307
+ }
4308
+ }
4293
4309
  function formatClock(iso) {
4294
4310
  const d = new Date(iso);
4295
4311
  if (Number.isNaN(d.getTime())) return "--:--";
@@ -4469,6 +4485,7 @@ async function runReadThread(opts = {}) {
4469
4485
  client = createChatClient(),
4470
4486
  resolveConnection = defaultResolveConnection,
4471
4487
  writeCursor = writeReadCursor,
4488
+ syncCursor = postReadCursor,
4472
4489
  ensureDisclosure = ensureChatDisclosure
4473
4490
  } = opts;
4474
4491
  const target = String(login ?? "").replace(/^@/, "").trim();
@@ -4514,6 +4531,7 @@ async function runReadThread(opts = {}) {
4514
4531
  writeCursor(peerLogin, newest.createdAt);
4515
4532
  } catch {
4516
4533
  }
4534
+ await syncCursor(peerLogin, newest.createdAt);
4517
4535
  }
4518
4536
  }
4519
4537
  return { ok: true, shown: shownMessages.length, total };
@@ -4561,12 +4579,14 @@ async function runSend(opts = {}) {
4561
4579
  );
4562
4580
  return { ok: true };
4563
4581
  }
4564
- var CHAT_BASE3, TERMINALHIRE_DIR5, READS_FILE;
4582
+ var CHAT_BASE3, GH_SESSION_COOKIE3, TERMINALHIRE_DIR5, READS_FILE;
4565
4583
  var init_jpi_chat_read = __esm({
4566
4584
  "bin/jpi-chat-read.js"() {
4567
4585
  init_chat_client();
4586
+ init_web_session();
4568
4587
  init_jpi_chat();
4569
4588
  CHAT_BASE3 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
4589
+ GH_SESSION_COOKIE3 = "__jpi_gh_session";
4570
4590
  TERMINALHIRE_DIR5 = join7(homedir6(), ".terminalhire");
4571
4591
  READS_FILE = join7(TERMINALHIRE_DIR5, "chat-reads.json");
4572
4592
  }
@@ -4575,6 +4595,7 @@ init_jpi_chat_read();
4575
4595
  export {
4576
4596
  formatClock,
4577
4597
  formatStamp,
4598
+ postReadCursor,
4578
4599
  readReadCursors,
4579
4600
  renderInbox,
4580
4601
  renderThread,
@@ -4167,6 +4167,7 @@ var jpi_chat_read_exports = {};
4167
4167
  __export(jpi_chat_read_exports, {
4168
4168
  formatClock: () => formatClock,
4169
4169
  formatStamp: () => formatStamp,
4170
+ postReadCursor: () => postReadCursor,
4170
4171
  readReadCursors: () => readReadCursors,
4171
4172
  renderInbox: () => renderInbox,
4172
4173
  renderThread: () => renderThread,
@@ -4199,6 +4200,22 @@ function writeReadCursor(login, iso, deps = {}) {
4199
4200
  mkdirSync6(TERMINALHIRE_DIR5, { recursive: true });
4200
4201
  writeFileSync6(READS_FILE, JSON.stringify(cursors, null, 2), { mode: 384, encoding: "utf8" });
4201
4202
  }
4203
+ async function postReadCursor(peerLogin, lastReadAt, deps = {}) {
4204
+ const readCookie = deps.readCookie ?? readWebSessionCookie;
4205
+ const cookie = readCookie();
4206
+ if (!cookie) return;
4207
+ try {
4208
+ await fetch(`${CHAT_BASE2}/api/chat/read-cursor`, {
4209
+ method: "POST",
4210
+ headers: { "Content-Type": "application/json", Cookie: `${GH_SESSION_COOKIE2}=${cookie}` },
4211
+ body: JSON.stringify({ peerLogin, lastReadAt }),
4212
+ // Best-effort cross-device sync — not latency-sensitive, so a short bound
4213
+ // keeps a cold/unreachable server from stalling the reader's exit.
4214
+ signal: AbortSignal.timeout(2500)
4215
+ });
4216
+ } catch {
4217
+ }
4218
+ }
4202
4219
  function formatClock(iso) {
4203
4220
  const d = new Date(iso);
4204
4221
  if (Number.isNaN(d.getTime())) return "--:--";
@@ -4378,6 +4395,7 @@ async function runReadThread(opts = {}) {
4378
4395
  client = createChatClient(),
4379
4396
  resolveConnection = defaultResolveConnection,
4380
4397
  writeCursor = writeReadCursor,
4398
+ syncCursor = postReadCursor,
4381
4399
  ensureDisclosure = ensureChatDisclosure
4382
4400
  } = opts;
4383
4401
  const target = String(login ?? "").replace(/^@/, "").trim();
@@ -4423,6 +4441,7 @@ async function runReadThread(opts = {}) {
4423
4441
  writeCursor(peerLogin, newest.createdAt);
4424
4442
  } catch {
4425
4443
  }
4444
+ await syncCursor(peerLogin, newest.createdAt);
4426
4445
  }
4427
4446
  }
4428
4447
  return { ok: true, shown: shownMessages.length, total };
@@ -4470,13 +4489,15 @@ async function runSend(opts = {}) {
4470
4489
  );
4471
4490
  return { ok: true };
4472
4491
  }
4473
- var CHAT_BASE2, TERMINALHIRE_DIR5, READS_FILE;
4492
+ var CHAT_BASE2, GH_SESSION_COOKIE2, TERMINALHIRE_DIR5, READS_FILE;
4474
4493
  var init_jpi_chat_read = __esm({
4475
4494
  "bin/jpi-chat-read.js"() {
4476
4495
  "use strict";
4477
4496
  init_chat_client();
4497
+ init_web_session();
4478
4498
  init_jpi_chat();
4479
4499
  CHAT_BASE2 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
4500
+ GH_SESSION_COOKIE2 = "__jpi_gh_session";
4480
4501
  TERMINALHIRE_DIR5 = join7(homedir6(), ".terminalhire");
4481
4502
  READS_FILE = join7(TERMINALHIRE_DIR5, "chat-reads.json");
4482
4503
  }
@@ -4538,7 +4559,7 @@ async function fetchIntroList(deps = {}) {
4538
4559
  try {
4539
4560
  res = await fetchImpl(`${CHAT_BASE3}/api/intro/list`, {
4540
4561
  method: "GET",
4541
- headers: { Cookie: `${GH_SESSION_COOKIE2}=${cookie}` },
4562
+ headers: { Cookie: `${GH_SESSION_COOKIE3}=${cookie}` },
4542
4563
  signal: AbortSignal.timeout(1e4)
4543
4564
  });
4544
4565
  } catch (err) {
@@ -5080,14 +5101,14 @@ async function run() {
5080
5101
  process.exit(1);
5081
5102
  }
5082
5103
  }
5083
- var CHAT_BASE3, GH_SESSION_COOKIE2, HIDE_CURSOR, SHOW_CURSOR, CLEAR, KEY_CTRL_C, KEY_CTRL_S, KEY_ENTER_A, KEY_ENTER_B, KEY_BACKSPACE_A, KEY_BACKSPACE_B, MAX_INPUT_LEN, ANSI_CSI, ANSI_OSC, ANSI_OTHER, C0_C1_DEL, CHAT_DISCLOSURE, CHAT_AT_REST, CHAT_CODE_OF_CONDUCT, CHAT_MIN_AGE, DEPOSIT_CTA;
5104
+ var CHAT_BASE3, GH_SESSION_COOKIE3, HIDE_CURSOR, SHOW_CURSOR, CLEAR, KEY_CTRL_C, KEY_CTRL_S, KEY_ENTER_A, KEY_ENTER_B, KEY_BACKSPACE_A, KEY_BACKSPACE_B, MAX_INPUT_LEN, ANSI_CSI, ANSI_OSC, ANSI_OTHER, C0_C1_DEL, CHAT_DISCLOSURE, CHAT_AT_REST, CHAT_CODE_OF_CONDUCT, CHAT_MIN_AGE, DEPOSIT_CTA;
5084
5105
  var init_jpi_chat = __esm({
5085
5106
  "bin/jpi-chat.js"() {
5086
5107
  init_chat_client();
5087
5108
  init_config();
5088
5109
  init_web_session();
5089
5110
  CHAT_BASE3 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
5090
- GH_SESSION_COOKIE2 = "__jpi_gh_session";
5111
+ GH_SESSION_COOKIE3 = "__jpi_gh_session";
5091
5112
  HIDE_CURSOR = "\x1B[?25l";
5092
5113
  SHOW_CURSOR = "\x1B[?25h";
5093
5114
  CLEAR = "\x1B[2J\x1B[H";
@@ -10289,6 +10289,7 @@ var jpi_chat_read_exports = {};
10289
10289
  __export(jpi_chat_read_exports, {
10290
10290
  formatClock: () => formatClock,
10291
10291
  formatStamp: () => formatStamp,
10292
+ postReadCursor: () => postReadCursor,
10292
10293
  readReadCursors: () => readReadCursors,
10293
10294
  renderInbox: () => renderInbox,
10294
10295
  renderThread: () => renderThread,
@@ -10321,6 +10322,22 @@ function writeReadCursor(login, iso, deps = {}) {
10321
10322
  mkdirSync13(TERMINALHIRE_DIR12, { recursive: true });
10322
10323
  writeFileSync13(READS_FILE, JSON.stringify(cursors, null, 2), { mode: 384, encoding: "utf8" });
10323
10324
  }
10325
+ async function postReadCursor(peerLogin, lastReadAt, deps = {}) {
10326
+ const readCookie = deps.readCookie ?? readWebSessionCookie;
10327
+ const cookie = readCookie();
10328
+ if (!cookie) return;
10329
+ try {
10330
+ await fetch(`${CHAT_BASE2}/api/chat/read-cursor`, {
10331
+ method: "POST",
10332
+ headers: { "Content-Type": "application/json", Cookie: `${GH_SESSION_COOKIE4}=${cookie}` },
10333
+ body: JSON.stringify({ peerLogin, lastReadAt }),
10334
+ // Best-effort cross-device sync — not latency-sensitive, so a short bound
10335
+ // keeps a cold/unreachable server from stalling the reader's exit.
10336
+ signal: AbortSignal.timeout(2500)
10337
+ });
10338
+ } catch {
10339
+ }
10340
+ }
10324
10341
  function formatClock(iso) {
10325
10342
  const d = new Date(iso);
10326
10343
  if (Number.isNaN(d.getTime())) return "--:--";
@@ -10500,6 +10517,7 @@ async function runReadThread(opts = {}) {
10500
10517
  client = createChatClient(),
10501
10518
  resolveConnection = defaultResolveConnection,
10502
10519
  writeCursor = writeReadCursor,
10520
+ syncCursor = postReadCursor,
10503
10521
  ensureDisclosure = ensureChatDisclosure
10504
10522
  } = opts;
10505
10523
  const target = String(login ?? "").replace(/^@/, "").trim();
@@ -10545,6 +10563,7 @@ async function runReadThread(opts = {}) {
10545
10563
  writeCursor(peerLogin, newest.createdAt);
10546
10564
  } catch {
10547
10565
  }
10566
+ await syncCursor(peerLogin, newest.createdAt);
10548
10567
  }
10549
10568
  }
10550
10569
  return { ok: true, shown: shownMessages.length, total };
@@ -10592,13 +10611,15 @@ async function runSend(opts = {}) {
10592
10611
  );
10593
10612
  return { ok: true };
10594
10613
  }
10595
- var CHAT_BASE2, TERMINALHIRE_DIR12, READS_FILE;
10614
+ var CHAT_BASE2, GH_SESSION_COOKIE4, TERMINALHIRE_DIR12, READS_FILE;
10596
10615
  var init_jpi_chat_read = __esm({
10597
10616
  "bin/jpi-chat-read.js"() {
10598
10617
  "use strict";
10599
10618
  init_chat_client();
10619
+ init_web_session();
10600
10620
  init_jpi_chat();
10601
10621
  CHAT_BASE2 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
10622
+ GH_SESSION_COOKIE4 = "__jpi_gh_session";
10602
10623
  TERMINALHIRE_DIR12 = join16(homedir15(), ".terminalhire");
10603
10624
  READS_FILE = join16(TERMINALHIRE_DIR12, "chat-reads.json");
10604
10625
  }
@@ -10675,7 +10696,7 @@ async function fetchIntroList(deps = {}) {
10675
10696
  try {
10676
10697
  res = await fetchImpl(`${CHAT_BASE3}/api/intro/list`, {
10677
10698
  method: "GET",
10678
- headers: { Cookie: `${GH_SESSION_COOKIE4}=${cookie}` },
10699
+ headers: { Cookie: `${GH_SESSION_COOKIE5}=${cookie}` },
10679
10700
  signal: AbortSignal.timeout(1e4)
10680
10701
  });
10681
10702
  } catch (err) {
@@ -11217,7 +11238,7 @@ async function run9() {
11217
11238
  process.exit(1);
11218
11239
  }
11219
11240
  }
11220
- var CHAT_BASE3, GH_SESSION_COOKIE4, HIDE_CURSOR, SHOW_CURSOR, CLEAR, KEY_CTRL_C, KEY_CTRL_S, KEY_ENTER_A, KEY_ENTER_B, KEY_BACKSPACE_A, KEY_BACKSPACE_B, MAX_INPUT_LEN, ANSI_CSI, ANSI_OSC, ANSI_OTHER, C0_C1_DEL, CHAT_DISCLOSURE, CHAT_AT_REST, CHAT_CODE_OF_CONDUCT, CHAT_MIN_AGE, DEPOSIT_CTA;
11241
+ var CHAT_BASE3, GH_SESSION_COOKIE5, HIDE_CURSOR, SHOW_CURSOR, CLEAR, KEY_CTRL_C, KEY_CTRL_S, KEY_ENTER_A, KEY_ENTER_B, KEY_BACKSPACE_A, KEY_BACKSPACE_B, MAX_INPUT_LEN, ANSI_CSI, ANSI_OSC, ANSI_OTHER, C0_C1_DEL, CHAT_DISCLOSURE, CHAT_AT_REST, CHAT_CODE_OF_CONDUCT, CHAT_MIN_AGE, DEPOSIT_CTA;
11221
11242
  var init_jpi_chat = __esm({
11222
11243
  "bin/jpi-chat.js"() {
11223
11244
  "use strict";
@@ -11225,7 +11246,7 @@ var init_jpi_chat = __esm({
11225
11246
  init_config();
11226
11247
  init_web_session();
11227
11248
  CHAT_BASE3 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
11228
- GH_SESSION_COOKIE4 = "__jpi_gh_session";
11249
+ GH_SESSION_COOKIE5 = "__jpi_gh_session";
11229
11250
  HIDE_CURSOR = "\x1B[?25l";
11230
11251
  SHOW_CURSOR = "\x1B[?25h";
11231
11252
  CLEAR = "\x1B[2J\x1B[H";
@@ -11431,7 +11452,7 @@ async function runLinkLogout(overrides) {
11431
11452
  try {
11432
11453
  const res = await deps.fetchImpl(`${LINK_BASE3}/api/auth/session`, {
11433
11454
  method: "DELETE",
11434
- headers: { Cookie: `${GH_SESSION_COOKIE5}=${token}` },
11455
+ headers: { Cookie: `${GH_SESSION_COOKIE6}=${token}` },
11435
11456
  signal: AbortSignal.timeout(1e4)
11436
11457
  });
11437
11458
  revoked = res.ok;
@@ -11446,14 +11467,14 @@ async function runLinkLogout(overrides) {
11446
11467
  }
11447
11468
  deps.exit(0);
11448
11469
  }
11449
- var LINK_BASE3, GH_SESSION_COOKIE5, LINK_TIMEOUT_MS, LINKED_HTML, FAILED_HTML;
11470
+ var LINK_BASE3, GH_SESSION_COOKIE6, LINK_TIMEOUT_MS, LINKED_HTML, FAILED_HTML;
11450
11471
  var init_link = __esm({
11451
11472
  "src/link.ts"() {
11452
11473
  "use strict";
11453
11474
  init_web_session();
11454
11475
  init_config();
11455
11476
  LINK_BASE3 = "https://www.terminalhire.com";
11456
- GH_SESSION_COOKIE5 = "__jpi_gh_session";
11477
+ GH_SESSION_COOKIE6 = "__jpi_gh_session";
11457
11478
  LINK_TIMEOUT_MS = 12e4;
11458
11479
  LINKED_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>terminalhire</title></head>
11459
11480
  <body style="font-family:system-ui;padding:2rem;background:#0b0d10;color:#e6e6e6">
@@ -13071,7 +13092,7 @@ async function run18() {
13071
13092
  if (sessionCookie && !isInboundNudgeMuted()) try {
13072
13093
  const res = await fetch(`${API_URL6}/api/intro/list`, {
13073
13094
  method: "GET",
13074
- headers: { Cookie: `${GH_SESSION_COOKIE6}=${sessionCookie}` },
13095
+ headers: { Cookie: `${GH_SESSION_COOKIE7}=${sessionCookie}` },
13075
13096
  signal: AbortSignal.timeout(1e4)
13076
13097
  });
13077
13098
  if (res.ok) {
@@ -13082,6 +13103,24 @@ async function run18() {
13082
13103
  }
13083
13104
  } catch {
13084
13105
  }
13106
+ let unreadChat = { count: 0 };
13107
+ if (sessionCookie && !isInboundNudgeMuted()) try {
13108
+ const res = await fetch(`${API_URL6}/api/chat/inbox`, {
13109
+ method: "GET",
13110
+ headers: { Cookie: `${GH_SESSION_COOKIE7}=${sessionCookie}` },
13111
+ signal: AbortSignal.timeout(1e4)
13112
+ });
13113
+ if (res.ok) {
13114
+ const body = await res.json();
13115
+ const inbox = Array.isArray(body?.inbox) ? body.inbox : [];
13116
+ const total = inbox.reduce(
13117
+ (sum, it) => sum + (it && typeof it.unreadCount === "number" && it.unreadCount > 0 ? it.unreadCount : 0),
13118
+ 0
13119
+ );
13120
+ unreadChat = { count: total };
13121
+ }
13122
+ } catch {
13123
+ }
13085
13124
  mkdirSync17(TERMINALHIRE_DIR14, { recursive: true });
13086
13125
  const cacheEntry = {
13087
13126
  ts: Date.now(),
@@ -13089,7 +13128,8 @@ async function run18() {
13089
13128
  matchCount,
13090
13129
  topMatches,
13091
13130
  topPeers,
13092
- incomingPending
13131
+ incomingPending,
13132
+ unreadChat
13093
13133
  };
13094
13134
  writeFileSync17(INDEX_CACHE_FILE5, JSON.stringify(cacheEntry), "utf8");
13095
13135
  try {
@@ -13142,14 +13182,14 @@ async function run18() {
13142
13182
  process.exit(1);
13143
13183
  }
13144
13184
  }
13145
- var GH_SESSION_COOKIE6, __dirname4, TERMINALHIRE_DIR14, INDEX_CACHE_FILE5, API_URL6;
13185
+ var GH_SESSION_COOKIE7, __dirname4, TERMINALHIRE_DIR14, INDEX_CACHE_FILE5, API_URL6;
13146
13186
  var init_jpi_refresh = __esm({
13147
13187
  "bin/jpi-refresh.js"() {
13148
13188
  "use strict";
13149
13189
  init_directory2();
13150
13190
  init_config();
13151
13191
  init_web_session();
13152
- GH_SESSION_COOKIE6 = "__jpi_gh_session";
13192
+ GH_SESSION_COOKIE7 = "__jpi_gh_session";
13153
13193
  __dirname4 = fileURLToPath5(new URL(".", import.meta.url));
13154
13194
  TERMINALHIRE_DIR14 = join23(homedir21(), ".terminalhire");
13155
13195
  INDEX_CACHE_FILE5 = join23(TERMINALHIRE_DIR14, "index-cache.json");
@@ -7094,6 +7094,24 @@ async function run() {
7094
7094
  }
7095
7095
  } catch {
7096
7096
  }
7097
+ let unreadChat = { count: 0 };
7098
+ if (sessionCookie && !isInboundNudgeMuted()) try {
7099
+ const res = await fetch(`${API_URL2}/api/chat/inbox`, {
7100
+ method: "GET",
7101
+ headers: { Cookie: `${GH_SESSION_COOKIE}=${sessionCookie}` },
7102
+ signal: AbortSignal.timeout(1e4)
7103
+ });
7104
+ if (res.ok) {
7105
+ const body = await res.json();
7106
+ const inbox = Array.isArray(body?.inbox) ? body.inbox : [];
7107
+ const total = inbox.reduce(
7108
+ (sum, it) => sum + (it && typeof it.unreadCount === "number" && it.unreadCount > 0 ? it.unreadCount : 0),
7109
+ 0
7110
+ );
7111
+ unreadChat = { count: total };
7112
+ }
7113
+ } catch {
7114
+ }
7097
7115
  mkdirSync6(TERMINALHIRE_DIR4, { recursive: true });
7098
7116
  const cacheEntry = {
7099
7117
  ts: Date.now(),
@@ -7101,7 +7119,8 @@ async function run() {
7101
7119
  matchCount,
7102
7120
  topMatches,
7103
7121
  topPeers,
7104
- incomingPending
7122
+ incomingPending,
7123
+ unreadChat
7105
7124
  };
7106
7125
  writeFileSync6(INDEX_CACHE_FILE2, JSON.stringify(cacheEntry), "utf8");
7107
7126
  try {
package/dist/bin/jpi.js CHANGED
@@ -94,6 +94,17 @@ function getCachedIncomingCount() {
94
94
  return 0;
95
95
  }
96
96
  }
97
+ function getCachedUnreadChatCount() {
98
+ try {
99
+ const raw = readFileSync(INDEX_CACHE_FILE, "utf8");
100
+ const entry = JSON.parse(raw);
101
+ if (Date.now() - entry.ts > INDEX_CACHE_TTL_MS) return 0;
102
+ const n = entry.unreadChat && entry.unreadChat.count;
103
+ return typeof n === "number" && n > 0 ? n : 0;
104
+ } catch {
105
+ return 0;
106
+ }
107
+ }
97
108
  function getNudgeMode() {
98
109
  const envVal = process.env["TERMINALHIRE_NUDGE"];
99
110
  if (envVal) {
@@ -166,20 +177,26 @@ try {
166
177
  }
167
178
  const matchCount = getCachedMatchCount();
168
179
  const incomingCount = getCachedIncomingCount();
180
+ const unreadChatCount = getCachedUnreadChatCount();
169
181
  const haveRoles = matchCount !== null && matchCount > 0;
170
- if (!haveRoles && incomingCount === 0) process.exit(0);
182
+ if (!haveRoles && incomingCount === 0 && unreadChatCount === 0) process.exit(0);
183
+ const hasConnectionSignal = incomingCount > 0 || unreadChatCount > 0;
171
184
  const nudgeMode = getNudgeMode();
172
- if (!shouldNudge(nudgeMode, sessionId)) process.exit(0);
185
+ if (!hasConnectionSignal && !shouldNudge(nudgeMode, sessionId)) process.exit(0);
173
186
  let line;
174
187
  if (haveRoles) {
175
188
  const plural = matchCount === 1 ? "role" : "roles";
176
189
  line = `\u2726 ${matchCount} ${plural} match your current work \u2014 run: terminalhire jobs`;
177
190
  if (incomingCount > 0) line += ` \xB7 \u2709 ${incomingCount} waiting to connect`;
178
- } else {
191
+ if (unreadChatCount > 0) line += ` \xB7 \u{1F4AC} ${unreadChatCount} unread`;
192
+ } else if (incomingCount > 0) {
179
193
  line = `\u2709 ${incomingCount} waiting to connect \u2014 run: terminalhire intro --list`;
194
+ if (unreadChatCount > 0) line += ` \xB7 \u{1F4AC} ${unreadChatCount} unread`;
195
+ } else {
196
+ line = `\u{1F4AC} ${unreadChatCount} unread \u2014 run: terminalhire chat`;
180
197
  }
181
198
  process.stdout.write(line + "\n");
182
- if (nudgeMode === "session") {
199
+ if (haveRoles && nudgeMode === "session") {
183
200
  markNudged(sessionId);
184
201
  }
185
202
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.10.4",
3
+ "version": "0.10.6",
4
4
  "description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Matching runs on your machine; your profile never leaves it.",
5
5
  "repository": {
6
6
  "type": "git",