claude-threads 0.33.3 → 0.33.5

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/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.33.5] - 2026-01-04
11
+
12
+ ### Fixed
13
+ - **Task toggle emoji disappearing on uncollapse** - Fixed issue where the task toggle emoji (📋) would disappear when uncollapsing the task list. Added re-add of toggle emoji after expanding tasks.
14
+ - **Status bar cleanup** - Removed redundant session count from status bars, added keep-alive indicator to show connection health.
15
+
16
+ ## [0.33.4] - 2026-01-04
17
+
18
+ ### Fixed
19
+ - **Graceful shutdown now actually waits** - Fixed issue where Ctrl-C would exit immediately instead of waiting for Claude CLI processes to exit gracefully. The `kill()` method now returns a Promise that resolves when the process exits, and shutdown waits for all sessions to complete (up to 2 seconds per session).
20
+ - **Signal handlers now work correctly** - Fixed conflict with `when-exit` package (transitive dependency via `update-notifier`) that was intercepting SIGINT before our handlers could run. Now removes conflicting handlers before registering our own.
21
+ - **No more reconnection attempts during shutdown** - WebSocket client now tracks intentional disconnects and skips reconnection attempts when shutting down gracefully.
22
+
10
23
  ## [0.33.3] - 2026-01-04
11
24
 
12
25
  ### Fixed
package/dist/index.js CHANGED
@@ -13312,6 +13312,7 @@ class MattermostClient extends EventEmitter {
13312
13312
  HEARTBEAT_TIMEOUT_MS = 60000;
13313
13313
  lastProcessedPostId = null;
13314
13314
  isReconnecting = false;
13315
+ isIntentionalDisconnect = false;
13315
13316
  constructor(platformConfig) {
13316
13317
  super();
13317
13318
  this.platformId = platformConfig.id;
@@ -13575,7 +13576,9 @@ class MattermostClient extends EventEmitter {
13575
13576
  wsLogger.debug("WebSocket disconnected");
13576
13577
  this.stopHeartbeat();
13577
13578
  this.emit("disconnected");
13578
- this.scheduleReconnect();
13579
+ if (!this.isIntentionalDisconnect) {
13580
+ this.scheduleReconnect();
13581
+ }
13579
13582
  };
13580
13583
  this.ws.onerror = (event) => {
13581
13584
  wsLogger.debug(`WebSocket error: ${event}`);
@@ -13739,6 +13742,8 @@ class MattermostClient extends EventEmitter {
13739
13742
  }));
13740
13743
  }
13741
13744
  disconnect() {
13745
+ this.isIntentionalDisconnect = true;
13746
+ this.stopHeartbeat();
13742
13747
  if (this.ws) {
13743
13748
  this.ws.close();
13744
13749
  this.ws = null;
@@ -15824,6 +15829,9 @@ async function handleTaskToggleReaction(session, action, ctx) {
15824
15829
  const displayMessage = session.tasksMinimized ? minimizedMessage : session.lastTasksContent;
15825
15830
  const tasksPostId = session.tasksPostId;
15826
15831
  await withErrorHandling(() => session.platform.updatePost(tasksPostId, displayMessage), { action: "Toggle tasks display", session });
15832
+ try {
15833
+ await session.platform.addReaction(tasksPostId, TASK_TOGGLE_EMOJIS[0]);
15834
+ } catch {}
15827
15835
  return true;
15828
15836
  }
15829
15837
  async function handleExistingWorktreeReaction(session, postId, emojiName, username, ctx, switchToWorktree) {
@@ -16106,23 +16114,26 @@ class ClaudeCli extends EventEmitter2 {
16106
16114
  kill() {
16107
16115
  this.stopStatusWatch();
16108
16116
  if (!this.process)
16109
- return;
16117
+ return Promise.resolve();
16110
16118
  const proc = this.process;
16111
16119
  this.process = null;
16112
- proc.kill("SIGINT");
16113
- const secondSigint = setTimeout(() => {
16114
- try {
16115
- proc.kill("SIGINT");
16116
- } catch {}
16117
- }, 100);
16118
- const forceKillTimeout = setTimeout(() => {
16119
- try {
16120
- proc.kill("SIGTERM");
16121
- } catch {}
16122
- }, 2000);
16123
- proc.once("exit", () => {
16124
- clearTimeout(secondSigint);
16125
- clearTimeout(forceKillTimeout);
16120
+ return new Promise((resolve3) => {
16121
+ proc.kill("SIGINT");
16122
+ const secondSigint = setTimeout(() => {
16123
+ try {
16124
+ proc.kill("SIGINT");
16125
+ } catch {}
16126
+ }, 100);
16127
+ const forceKillTimeout = setTimeout(() => {
16128
+ try {
16129
+ proc.kill("SIGTERM");
16130
+ } catch {}
16131
+ }, 2000);
16132
+ proc.once("exit", () => {
16133
+ clearTimeout(secondSigint);
16134
+ clearTimeout(forceKillTimeout);
16135
+ resolve3();
16136
+ });
16126
16137
  });
16127
16138
  }
16128
16139
  interrupt() {
@@ -20362,7 +20373,6 @@ async function updateSessionHeader(session, ctx) {
20362
20373
  statusItems.push(`\`${contextBar} ${contextPercent}%\``);
20363
20374
  statusItems.push(`\`\uD83D\uDCB0 $${stats.totalCostUSD.toFixed(2)}\``);
20364
20375
  }
20365
- statusItems.push(`\`${session.sessionNumber}/${ctx.config.maxSessions}\``);
20366
20376
  statusItems.push(`\`${permMode}\``);
20367
20377
  if (ctx.config.chromeEnabled) {
20368
20378
  statusItems.push("`\uD83C\uDF10 Chrome`");
@@ -20924,14 +20934,16 @@ async function killSession(session, unpersist, ctx) {
20924
20934
  log10.info(`\u2716 Session killed (${shortId}\u2026) \u2014 ${ctx.state.sessions.size} active`);
20925
20935
  await ctx.ops.updateStickyMessage();
20926
20936
  }
20927
- function killAllSessions(ctx) {
20937
+ async function killAllSessions(ctx) {
20938
+ const killPromises = [];
20928
20939
  for (const session of ctx.state.sessions.values()) {
20929
20940
  ctx.ops.stopTyping(session);
20930
20941
  if (ctx.state.isShuttingDown) {
20931
20942
  ctx.ops.persistSession(session);
20932
20943
  }
20933
- session.claude.kill();
20944
+ killPromises.push(session.claude.kill());
20934
20945
  }
20946
+ await Promise.all(killPromises);
20935
20947
  mutableSessions(ctx).clear();
20936
20948
  mutablePostIndex(ctx).clear();
20937
20949
  keepAlive.forceStop();
@@ -21560,6 +21572,9 @@ async function buildStatusBar(sessionCount, config) {
21560
21572
  if (config.debug) {
21561
21573
  items.push("`\uD83D\uDC1B Debug`");
21562
21574
  }
21575
+ if (keepAlive.isActive()) {
21576
+ items.push("`\uD83D\uDC93 Keep-alive`");
21577
+ }
21563
21578
  const battery = await formatBatteryStatus();
21564
21579
  if (battery) {
21565
21580
  items.push(`\`${battery}\``);
@@ -22180,8 +22195,8 @@ class SessionManager {
22180
22195
  return;
22181
22196
  await killSession(session, unpersist, this.getContext());
22182
22197
  }
22183
- killAllSessions() {
22184
- killAllSessions(this.getContext());
22198
+ async killAllSessions() {
22199
+ await killAllSessions(this.getContext());
22185
22200
  }
22186
22201
  async cancelSession(threadId, username) {
22187
22202
  const session = this.findSessionByThreadId(threadId);
@@ -22665,13 +22680,19 @@ Release notes not available. See [GitHub releases](https://github.com/anneschuth
22665
22680
  } catch {}
22666
22681
  }
22667
22682
  }
22668
- session.killAllSessions();
22683
+ await session.killAllSessions();
22669
22684
  mattermost.disconnect();
22670
22685
  };
22686
+ process.removeAllListeners("SIGINT");
22687
+ process.removeAllListeners("SIGTERM");
22671
22688
  process.on("SIGINT", () => {
22689
+ console.log(`
22690
+ \uD83D\uDC4B Shutting down (SIGINT)...`);
22672
22691
  shutdown().finally(() => process.exit(0));
22673
22692
  });
22674
22693
  process.on("SIGTERM", () => {
22694
+ console.log(`
22695
+ \uD83D\uDC4B Shutting down (SIGTERM)...`);
22675
22696
  shutdown().finally(() => process.exit(0));
22676
22697
  });
22677
22698
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "0.33.3",
3
+ "version": "0.33.5",
4
4
  "description": "Share Claude Code sessions live in a Mattermost channel with interactive features",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",