claude-threads 0.45.0 → 0.46.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/CHANGELOG.md +13 -0
- package/dist/index.js +75 -19
- package/package.json +2 -2
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.46.0] - 2026-01-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Emoji reactions for `!update` command** - React with 👍 to update immediately or 👎 to defer for 1 hour, easier than typing commands on mobile
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **Auto-update uses bun instead of npm** - Fixed updates to use `bun install -g` matching the actual install location
|
|
17
|
+
- **ESLint warnings resolved** - Fixed 8 non-null assertion warnings with proper null checks
|
|
18
|
+
- **Dead code removed** - Removed unused Discord formatter/types and other dead code via Knip
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Knip added to CI and pre-commit** - Dead code detection now runs automatically
|
|
22
|
+
|
|
10
23
|
## [0.45.0] - 2026-01-07
|
|
11
24
|
|
|
12
25
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -45243,6 +45243,7 @@ async function flush(session, registerPost) {
|
|
|
45243
45243
|
const { maxLength: MAX_POST_LENGTH, hardThreshold: HARD_CONTINUATION_THRESHOLD } = session.platform.getMessageLimits();
|
|
45244
45244
|
const shouldBreakEarly = session.currentPostId && content.length > MIN_BREAK_THRESHOLD && shouldFlushEarly(content);
|
|
45245
45245
|
if (session.currentPostId && (content.length > HARD_CONTINUATION_THRESHOLD || shouldBreakEarly)) {
|
|
45246
|
+
const currentPostId = session.currentPostId;
|
|
45246
45247
|
let breakPoint;
|
|
45247
45248
|
let codeBlockLanguage;
|
|
45248
45249
|
if (content.length > HARD_CONTINUATION_THRESHOLD) {
|
|
@@ -45275,7 +45276,7 @@ async function flush(session, registerPost) {
|
|
|
45275
45276
|
breakPoint = breakInfo.position;
|
|
45276
45277
|
} else {
|
|
45277
45278
|
try {
|
|
45278
|
-
await session.platform.updatePost(
|
|
45279
|
+
await session.platform.updatePost(currentPostId, content);
|
|
45279
45280
|
} catch {
|
|
45280
45281
|
sessionLog(session).debug("Update failed (no breakpoint), will create new post on next flush");
|
|
45281
45282
|
session.currentPostId = null;
|
|
@@ -45295,7 +45296,7 @@ async function flush(session, registerPost) {
|
|
|
45295
45296
|
|
|
45296
45297
|
` + formatter2.formatItalic("... (continued below)") : firstPart;
|
|
45297
45298
|
try {
|
|
45298
|
-
await session.platform.updatePost(
|
|
45299
|
+
await session.platform.updatePost(currentPostId, firstPartWithMarker);
|
|
45299
45300
|
} catch {
|
|
45300
45301
|
sessionLog(session).debug("Update failed during split, continuing with new post");
|
|
45301
45302
|
}
|
|
@@ -45329,8 +45330,9 @@ async function flush(session, registerPost) {
|
|
|
45329
45330
|
` + formatter2.formatItalic("... (truncated)");
|
|
45330
45331
|
}
|
|
45331
45332
|
if (session.currentPostId) {
|
|
45333
|
+
const postId = session.currentPostId;
|
|
45332
45334
|
try {
|
|
45333
|
-
await session.platform.updatePost(
|
|
45335
|
+
await session.platform.updatePost(postId, content);
|
|
45334
45336
|
} catch {
|
|
45335
45337
|
sessionLog(session).debug("Update failed, will create new post on next flush");
|
|
45336
45338
|
session.currentPostId = null;
|
|
@@ -46581,7 +46583,9 @@ async function handleTodoWrite(session, input, ctx) {
|
|
|
46581
46583
|
await session.platform.pinPost(post.id).catch(() => {});
|
|
46582
46584
|
}
|
|
46583
46585
|
} finally {
|
|
46584
|
-
resolveCreation
|
|
46586
|
+
if (resolveCreation) {
|
|
46587
|
+
resolveCreation();
|
|
46588
|
+
}
|
|
46585
46589
|
session.taskListCreationPromise = undefined;
|
|
46586
46590
|
}
|
|
46587
46591
|
}
|
|
@@ -46633,7 +46637,8 @@ async function handleCompactionComplete(session, compactMetadata, _ctx) {
|
|
|
46633
46637
|
const formatter = session.platform.getFormatter();
|
|
46634
46638
|
const completionMessage = `\u2705 ${formatter.formatBold("Context compacted")} ${formatter.formatItalic(`(${info})`)}`;
|
|
46635
46639
|
if (session.compactionPostId) {
|
|
46636
|
-
|
|
46640
|
+
const postId = session.compactionPostId;
|
|
46641
|
+
await withErrorHandling(() => session.platform.updatePost(postId, completionMessage), { action: "Update compaction complete", session });
|
|
46637
46642
|
session.compactionPostId = undefined;
|
|
46638
46643
|
} else {
|
|
46639
46644
|
const post = await withErrorHandling(() => session.platform.createPost(completionMessage, session.threadId), { action: "Post compaction complete", session });
|
|
@@ -46949,6 +46954,37 @@ async function handleExistingWorktreeReaction(session, postId, emojiName, userna
|
|
|
46949
46954
|
}
|
|
46950
46955
|
return true;
|
|
46951
46956
|
}
|
|
46957
|
+
async function handleUpdateReaction(session, postId, emojiName, username, ctx, updateHandler) {
|
|
46958
|
+
const pending = session.pendingUpdatePrompt;
|
|
46959
|
+
if (!pending || pending.postId !== postId) {
|
|
46960
|
+
return false;
|
|
46961
|
+
}
|
|
46962
|
+
if (session.startedBy !== username && !session.platform.isUserAllowed(username)) {
|
|
46963
|
+
return false;
|
|
46964
|
+
}
|
|
46965
|
+
const isUpdateNow = isApprovalEmoji(emojiName);
|
|
46966
|
+
const isDefer = isDenialEmoji(emojiName);
|
|
46967
|
+
if (!isUpdateNow && !isDefer) {
|
|
46968
|
+
return false;
|
|
46969
|
+
}
|
|
46970
|
+
const formatter = session.platform.getFormatter();
|
|
46971
|
+
if (isUpdateNow) {
|
|
46972
|
+
await withErrorHandling(() => session.platform.updatePost(pending.postId, `\uD83D\uDD04 ${formatter.formatBold("Forcing update")} - restarting shortly...
|
|
46973
|
+
` + formatter.formatItalic("Sessions will resume automatically")), { action: "Update post for force update", session });
|
|
46974
|
+
session.pendingUpdatePrompt = undefined;
|
|
46975
|
+
ctx.ops.persistSession(session);
|
|
46976
|
+
sessionLog3(session).info(`\uD83D\uDD04 @${username} triggered immediate update`);
|
|
46977
|
+
await updateHandler.forceUpdate();
|
|
46978
|
+
} else {
|
|
46979
|
+
updateHandler.deferUpdate(60);
|
|
46980
|
+
await withErrorHandling(() => session.platform.updatePost(pending.postId, `\u23F8\uFE0F ${formatter.formatBold("Update deferred")} for 1 hour
|
|
46981
|
+
` + formatter.formatItalic("Use !update now to apply earlier")), { action: "Update post for defer update", session });
|
|
46982
|
+
session.pendingUpdatePrompt = undefined;
|
|
46983
|
+
ctx.ops.persistSession(session);
|
|
46984
|
+
sessionLog3(session).info(`\u23F8\uFE0F @${username} deferred update for 1 hour`);
|
|
46985
|
+
}
|
|
46986
|
+
return true;
|
|
46987
|
+
}
|
|
46952
46988
|
|
|
46953
46989
|
// src/claude/cli.ts
|
|
46954
46990
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -50700,7 +50736,7 @@ function checkForUpdates() {
|
|
|
50700
50736
|
cachedUpdateInfo = notifier.update;
|
|
50701
50737
|
notifier.notify({
|
|
50702
50738
|
message: `Update available: {currentVersion} \u2192 {latestVersion}
|
|
50703
|
-
Run:
|
|
50739
|
+
Run: bun install -g claude-threads`
|
|
50704
50740
|
});
|
|
50705
50741
|
} catch {}
|
|
50706
50742
|
}
|
|
@@ -51144,7 +51180,7 @@ function validateClaudeCli() {
|
|
|
51144
51180
|
installed: false,
|
|
51145
51181
|
version: null,
|
|
51146
51182
|
compatible: false,
|
|
51147
|
-
message: "Claude CLI not found. Install it with:
|
|
51183
|
+
message: "Claude CLI not found. Install it with: bun install -g @anthropic-ai/claude-code"
|
|
51148
51184
|
};
|
|
51149
51185
|
}
|
|
51150
51186
|
const compatible = isVersionCompatible(version);
|
|
@@ -51154,7 +51190,7 @@ function validateClaudeCli() {
|
|
|
51154
51190
|
version,
|
|
51155
51191
|
compatible: false,
|
|
51156
51192
|
message: `Claude CLI version ${version} is not compatible. Required: ${CLAUDE_CLI_VERSION_RANGE}
|
|
51157
|
-
` + `Install a compatible version:
|
|
51193
|
+
` + `Install a compatible version: bun install -g @anthropic-ai/claude-code@2.0.76`
|
|
51158
51194
|
};
|
|
51159
51195
|
}
|
|
51160
51196
|
return {
|
|
@@ -51477,7 +51513,7 @@ async function updateSessionHeader(session, ctx) {
|
|
|
51477
51513
|
items.push(["\uD83C\uDD94", "Session ID", formatter.formatCode(session.claudeSessionId.substring(0, 8))]);
|
|
51478
51514
|
const updateInfo = getUpdateInfo();
|
|
51479
51515
|
const updateNotice = updateInfo ? `
|
|
51480
|
-
> \u26A0\uFE0F ${formatter.formatBold("Update available:")} v${updateInfo.current} \u2192 v${updateInfo.latest} - Run ${formatter.formatCode("
|
|
51516
|
+
> \u26A0\uFE0F ${formatter.formatBold("Update available:")} v${updateInfo.current} \u2192 v${updateInfo.latest} - Run ${formatter.formatCode("bun install -g claude-threads")}
|
|
51481
51517
|
` : "";
|
|
51482
51518
|
const releaseNotes = getReleaseNotes(VERSION);
|
|
51483
51519
|
const whatsNew = releaseNotes ? getWhatsNewSummary(releaseNotes) : "";
|
|
@@ -51520,15 +51556,15 @@ async function showUpdateStatus(session, updateManager) {
|
|
|
51520
51556
|
} else {
|
|
51521
51557
|
statusLine = `Mode: ${config.autoRestartMode}`;
|
|
51522
51558
|
}
|
|
51523
|
-
|
|
51559
|
+
const message = `\uD83D\uDD04 ${formatter.formatBold("Update available")}
|
|
51524
51560
|
|
|
51525
51561
|
` + `Current: v${updateInfo.currentVersion}
|
|
51526
51562
|
` + `Latest: v${updateInfo.latestVersion}
|
|
51527
51563
|
` + `${statusLine}
|
|
51528
51564
|
|
|
51529
|
-
` + `
|
|
51530
|
-
|
|
51531
|
-
|
|
51565
|
+
` + `React: \uD83D\uDC4D Update now | \uD83D\uDC4E Defer for 1 hour`;
|
|
51566
|
+
const post = await session.platform.createInteractivePost(message, [APPROVAL_EMOJIS[0], DENIAL_EMOJIS[0]], session.threadId);
|
|
51567
|
+
session.pendingUpdatePrompt = { postId: post.id };
|
|
51532
51568
|
}
|
|
51533
51569
|
async function forceUpdateNow(session, username, updateManager) {
|
|
51534
51570
|
if (!await requireSessionOwner(session, username, "force updates")) {
|
|
@@ -51929,9 +51965,10 @@ Please start a new session.`, state.threadId), { action: "Post resume failure no
|
|
|
51929
51965
|
sessionLog5(session).info(`\uD83D\uDD04 Session resumed (@${state.startedBy})`);
|
|
51930
51966
|
const sessionFormatter = session.platform.getFormatter();
|
|
51931
51967
|
if (session.lifecyclePostId) {
|
|
51968
|
+
const postId = session.lifecyclePostId;
|
|
51932
51969
|
const resumeMsg = `\uD83D\uDD04 ${sessionFormatter.formatBold("Session resumed")} by ${sessionFormatter.formatUserMention(session.startedBy)}
|
|
51933
51970
|
${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue where you left off.")}`;
|
|
51934
|
-
await withErrorHandling(() => session.platform.updatePost(
|
|
51971
|
+
await withErrorHandling(() => session.platform.updatePost(postId, resumeMsg), { action: "Update timeout/shutdown post for resume", session });
|
|
51935
51972
|
session.lifecyclePostId = undefined;
|
|
51936
51973
|
} else {
|
|
51937
51974
|
const restartMsg = `${sessionFormatter.formatBold("Session resumed")} after bot restart (v${VERSION})
|
|
@@ -52163,7 +52200,8 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
|
|
|
52163
52200
|
|
|
52164
52201
|
\uD83D\uDCA1 React with \uD83D\uDD04 to resume, or send a new message to continue.`;
|
|
52165
52202
|
if (session.lifecyclePostId) {
|
|
52166
|
-
|
|
52203
|
+
const postId = session.lifecyclePostId;
|
|
52204
|
+
await withErrorHandling(() => session.platform.updatePost(postId, `\u23F1\uFE0F ${timeoutMessage}`), { action: "Update timeout post", session });
|
|
52167
52205
|
} else {
|
|
52168
52206
|
const timeoutPost = await withErrorHandling(() => postTimeout(session, timeoutMessage), { action: "Post session timeout", session });
|
|
52169
52207
|
if (timeoutPost) {
|
|
@@ -53310,7 +53348,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
53310
53348
|
if (!this.worktreeUsers.has(worktreePath)) {
|
|
53311
53349
|
this.worktreeUsers.set(worktreePath, new Set);
|
|
53312
53350
|
}
|
|
53313
|
-
this.worktreeUsers.get(worktreePath)
|
|
53351
|
+
const users = this.worktreeUsers.get(worktreePath);
|
|
53352
|
+
if (users) {
|
|
53353
|
+
users.add(sessionId);
|
|
53354
|
+
}
|
|
53314
53355
|
log18.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
|
|
53315
53356
|
}
|
|
53316
53357
|
unregisterWorktreeUser(worktreePath, sessionId) {
|
|
@@ -53466,6 +53507,17 @@ class SessionManager extends EventEmitter4 {
|
|
|
53466
53507
|
if (handled)
|
|
53467
53508
|
return;
|
|
53468
53509
|
}
|
|
53510
|
+
if (action === "added" && session.pendingUpdatePrompt?.postId === postId) {
|
|
53511
|
+
if (this.autoUpdateManager) {
|
|
53512
|
+
const updateHandler = {
|
|
53513
|
+
forceUpdate: () => this.autoUpdateManager.forceUpdate(),
|
|
53514
|
+
deferUpdate: (minutes) => this.autoUpdateManager.deferUpdate(minutes)
|
|
53515
|
+
};
|
|
53516
|
+
const handled = await handleUpdateReaction(session, postId, emojiName, username, this.getContext(), updateHandler);
|
|
53517
|
+
if (handled)
|
|
53518
|
+
return;
|
|
53519
|
+
}
|
|
53520
|
+
}
|
|
53469
53521
|
if (action === "added" && session.pendingContextPrompt?.postId === postId) {
|
|
53470
53522
|
await this.handleContextPromptReaction(session, emojiName, username);
|
|
53471
53523
|
return;
|
|
@@ -64480,8 +64532,11 @@ async function installVersion(version) {
|
|
|
64480
64532
|
justUpdated: false
|
|
64481
64533
|
});
|
|
64482
64534
|
return new Promise((resolve7) => {
|
|
64483
|
-
const
|
|
64484
|
-
const
|
|
64535
|
+
const useBun = process.platform !== "win32";
|
|
64536
|
+
const cmd = useBun ? "bun" : "npm.cmd";
|
|
64537
|
+
const args = useBun ? ["install", "-g", `${PACKAGE_NAME3}@${version}`] : ["install", "-g", `${PACKAGE_NAME3}@${version}`];
|
|
64538
|
+
log21.debug(`Using ${useBun ? "bun" : "npm"} for installation`);
|
|
64539
|
+
const child = spawn5(cmd, args, {
|
|
64485
64540
|
stdio: ["ignore", "pipe", "pipe"],
|
|
64486
64541
|
env: {
|
|
64487
64542
|
...process.env,
|
|
@@ -64529,8 +64584,9 @@ async function installVersion(version) {
|
|
|
64529
64584
|
});
|
|
64530
64585
|
}
|
|
64531
64586
|
function getRollbackInstructions(previousVersion) {
|
|
64587
|
+
const cmd = process.platform === "win32" ? "npm" : "bun";
|
|
64532
64588
|
return `To rollback to the previous version, run:
|
|
64533
|
-
|
|
64589
|
+
${cmd} install -g ${PACKAGE_NAME3}@${previousVersion}`;
|
|
64534
64590
|
}
|
|
64535
64591
|
|
|
64536
64592
|
class UpdateInstaller {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-threads",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
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",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@inkjs/ui": "^2.0.0",
|
|
65
65
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
66
|
+
"cli-spinners": "^3.3.0",
|
|
66
67
|
"commander": "^14.0.2",
|
|
67
68
|
"diff": "^8.0.2",
|
|
68
69
|
"ink": "^6.6.0",
|
|
@@ -76,7 +77,6 @@
|
|
|
76
77
|
"devDependencies": {
|
|
77
78
|
"@eslint/js": "^9.39.2",
|
|
78
79
|
"@types/bun": "latest",
|
|
79
|
-
"@types/diff": "^8.0.0",
|
|
80
80
|
"@types/node": "^25.0.3",
|
|
81
81
|
"@types/prompts": "^2.4.9",
|
|
82
82
|
"@types/react": "^19.2.7",
|