claude-threads 1.23.0 → 1.24.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 CHANGED
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.24.0] - 2026-08-08
9
+
10
+ ### Added
11
+ - **`!model` and `!effort` — switch model or reasoning effort mid-session.** Verified against the real CLI (2.1.226): `/model` and `/effort` work over stream-json (`/model` with no args lists the options; `/model sonnet` switches "for this session only"; `/effort low|medium|high|xhigh|max|auto`), and both are listed in the CLI's `init.slash_commands` — so the bot's dynamic slash-command passthrough forwards `!model sonnet` / `!effort high` as-is and the CLI's confirmation posts to the thread. Both are now first-class registered commands with unconditional forwarding handlers (like `!context`/`!cost`/`!compact` — they work even before the CLI's init event arrives, and forward their argument: `!model sonnet` → `/model sonnet`), and `!help` gained a line pointing out that Claude Code slash commands work with `!`.
12
+ - **The session header now shows the model the session actually runs on.** Previously the header picked the "primary model" by highest cumulative cost — after a `!model` switch the old model keeps the larger spend, so the header kept naming the old model indefinitely. The per-turn `init.model` (re-emitted every turn, per the reference captures) is now authoritative. Model display names also cover the Claude 5 family via generic id parsing (`claude-sonnet-5` → "Sonnet 5", `claude-fable-5` → "Fable 5", dated ids still render as before).
13
+
14
+ ### Changed
15
+ - **Latest verified Claude CLI: 2.1.226** (from 2.1.223). The full verification battery ran against it this cycle: the decision-bridge e2e (4/4), all 17 reference captures re-recorded on 2.1.225/2.1.226, and both integration matrices. Install hints updated. Also verified while probing: `/cd` and `/add-dir` report "isn't available in this environment" over stream-json on 2.1.226 — a native in-session directory switch isn't possible, so `!cd` keeps its restart-based implementation.
16
+
8
17
  ## [1.23.0] - 2026-08-08
9
18
 
10
19
  ### Fixed
package/dist/index.js CHANGED
@@ -51319,7 +51319,7 @@ var COMMON_CLAUDE_PATHS = process.platform === "win32" ? [
51319
51319
  var CLAUDE_CLI_MIN_VERSION = "2.0.74";
51320
51320
  var CLAUDE_CLI_VERIFIED_RANGE = ">=2.0.74 <2.2.0";
51321
51321
  var CLAUDE_CLI_SUPPORTED_MAJOR = 2;
51322
- var CLAUDE_CLI_LATEST_VERIFIED = "2.1.223";
51322
+ var CLAUDE_CLI_LATEST_VERIFIED = "2.1.226";
51323
51323
  function tryClaudeVersion(claudePath) {
51324
51324
  try {
51325
51325
  const output = execSync(`"${claudePath}" --version`, {
@@ -57560,6 +57560,18 @@ var COMMAND_REGISTRY = [
57560
57560
  description: "Compact conversation",
57561
57561
  category: "passthrough",
57562
57562
  audience: "both"
57563
+ },
57564
+ {
57565
+ command: "model",
57566
+ description: "Show or switch the model for this session (e.g. !model sonnet)",
57567
+ category: "passthrough",
57568
+ audience: "both"
57569
+ },
57570
+ {
57571
+ command: "effort",
57572
+ description: "Set reasoning effort: low|medium|high|xhigh|max|auto (e.g. !effort high)",
57573
+ category: "passthrough",
57574
+ audience: "both"
57563
57575
  }
57564
57576
  ];
57565
57577
  var REACTION_REGISTRY = [
@@ -57617,6 +57629,8 @@ var COMMAND_PATTERNS = [
57617
57629
  ["context", /^!context\s*$/i],
57618
57630
  ["cost", /^!cost\s*$/i],
57619
57631
  ["compact", /^!compact\s*$/i],
57632
+ ["model", /^!model(?:\s+(\S+))?\s*$/i],
57633
+ ["effort", /^!effort(?:\s+(\S+))?\s*$/i],
57620
57634
  ["plugin", /^!plugin(?:\s+(.+))?$/i],
57621
57635
  ["kill", /^!kill\s*$/i],
57622
57636
  ["bug", /^!bug(?:\s+(.+))?$/i],
@@ -57741,6 +57755,8 @@ ${formatter.formatBold("Reactions:")}
57741
57755
  ` + `${formatter.formatListItem(approvalReactions)}
57742
57756
  ` + `${formatter.formatListItem(sessionReactions)}
57743
57757
 
57758
+ ` + `Claude Code slash commands work with ${code("!")} too — e.g. ${code("!model sonnet")}, ${code("!effort high")}, ${code("!compact")}, ${code("!context")}.
57759
+
57744
57760
  ` + formatSponsorFooter(formatter);
57745
57761
  }
57746
57762
 
@@ -58122,12 +58138,13 @@ var handlePlugin = async (ctx, args) => {
58122
58138
  return { handled: true };
58123
58139
  };
58124
58140
  function createPassthroughHandler(slashCommand) {
58125
- return async (ctx) => {
58141
+ return async (ctx, args) => {
58126
58142
  if (ctx.commandContext === "first-message") {
58127
58143
  return { handled: false };
58128
58144
  }
58129
58145
  if (ctx.isAllowed) {
58130
- await ctx.sessionManager.sendFollowUp(ctx.threadId, `/${slashCommand}`, undefined, undefined, undefined, { system: true });
58146
+ const full = args ? `/${slashCommand} ${args}` : `/${slashCommand}`;
58147
+ await ctx.sessionManager.sendFollowUp(ctx.threadId, full, undefined, undefined, undefined, { system: true });
58131
58148
  }
58132
58149
  return { handled: true };
58133
58150
  };
@@ -58150,6 +58167,8 @@ handlers.set("plugin", handlePlugin);
58150
58167
  handlers.set("context", createPassthroughHandler("context"));
58151
58168
  handlers.set("cost", createPassthroughHandler("cost"));
58152
58169
  handlers.set("compact", createPassthroughHandler("compact"));
58170
+ handlers.set("model", createPassthroughHandler("model"));
58171
+ handlers.set("effort", createPassthroughHandler("effort"));
58153
58172
  async function executeCommand(command, args, ctx) {
58154
58173
  const cmdDef = getCommandDef(command);
58155
58174
  if (!cmdDef) {
@@ -68841,6 +68860,9 @@ function handleEventPreProcessing(session, event, ctx) {
68841
68860
  }
68842
68861
  if (event.type === "system") {
68843
68862
  const e = event;
68863
+ if (e.subtype === "init" && typeof e.model === "string") {
68864
+ session.currentModel = e.model;
68865
+ }
68844
68866
  if (e.subtype === "init" && e.slash_commands && Array.isArray(e.slash_commands)) {
68845
68867
  session.availableSlashCommands = new Set(e.slash_commands.map((cmd) => cmd.startsWith("/") ? cmd.slice(1) : cmd));
68846
68868
  sessionLog3(session).info(`Captured ${session.availableSlashCommands.size} slash commands from init: ${[...session.availableSlashCommands].join(", ")}`);
@@ -68975,20 +68997,15 @@ async function handleCompactionComplete(session, compactMetadata, _ctx) {
68975
68997
  }
68976
68998
  }
68977
68999
  function getModelDisplayName(modelId) {
68978
- if (modelId.includes("opus-4-5") || modelId.includes("opus-4.5"))
68979
- return "Opus 4.5";
68980
- if (modelId.includes("opus-4"))
68981
- return "Opus 4";
68982
- if (modelId.includes("opus"))
68983
- return "Opus";
68984
- if (modelId.includes("sonnet-4"))
68985
- return "Sonnet 4";
68986
- if (modelId.includes("sonnet-3-5") || modelId.includes("sonnet-3.5"))
68987
- return "Sonnet 3.5";
69000
+ const modern = modelId.match(/^claude-([a-z]+)-(\d+)(?:-(\d{1,2}))?(?:-\d{8})?$/);
69001
+ if (modern) {
69002
+ const family = modern[1].charAt(0).toUpperCase() + modern[1].slice(1);
69003
+ return modern[3] ? `${family} ${modern[2]}.${modern[3]}` : `${family} ${modern[2]}`;
69004
+ }
68988
69005
  if (modelId.includes("sonnet"))
68989
69006
  return "Sonnet";
68990
- if (modelId.includes("haiku-4-5") || modelId.includes("haiku-4.5"))
68991
- return "Haiku 4.5";
69007
+ if (modelId.includes("opus"))
69008
+ return "Opus";
68992
69009
  if (modelId.includes("haiku"))
68993
69010
  return "Haiku";
68994
69011
  const match = modelId.match(/claude-(\w+)/);
@@ -69019,6 +69036,10 @@ function updateUsageStats(session, event, ctx) {
69019
69036
  contextWindowSize = usage.contextWindow;
69020
69037
  }
69021
69038
  }
69039
+ if (session.currentModel && result.modelUsage[session.currentModel]) {
69040
+ primaryModel = session.currentModel;
69041
+ contextWindowSize = result.modelUsage[session.currentModel].contextWindow;
69042
+ }
69022
69043
  let contextTokens = 0;
69023
69044
  if (result.usage) {
69024
69045
  contextTokens = result.usage.input_tokens + result.usage.cache_creation_input_tokens + result.usage.cache_read_input_tokens;
@@ -56734,6 +56734,18 @@ var COMMAND_REGISTRY = [
56734
56734
  description: "Compact conversation",
56735
56735
  category: "passthrough",
56736
56736
  audience: "both"
56737
+ },
56738
+ {
56739
+ command: "model",
56740
+ description: "Show or switch the model for this session (e.g. !model sonnet)",
56741
+ category: "passthrough",
56742
+ audience: "both"
56743
+ },
56744
+ {
56745
+ command: "effort",
56746
+ description: "Set reasoning effort: low|medium|high|xhigh|max|auto (e.g. !effort high)",
56747
+ category: "passthrough",
56748
+ audience: "both"
56737
56749
  }
56738
56750
  ];
56739
56751
  var REACTION_REGISTRY = [
@@ -56806,6 +56818,8 @@ ${formatter.formatBold("Reactions:")}
56806
56818
  ` + `${formatter.formatListItem(approvalReactions)}
56807
56819
  ` + `${formatter.formatListItem(sessionReactions)}
56808
56820
 
56821
+ ` + `Claude Code slash commands work with ${code("!")} too — e.g. ${code("!model sonnet")}, ${code("!effort high")}, ${code("!compact")}, ${code("!context")}.
56822
+
56809
56823
  ` + formatSponsorFooter(formatter);
56810
56824
  }
56811
56825
 
@@ -57072,12 +57086,13 @@ var handlePlugin = async (ctx, args) => {
57072
57086
  return { handled: true };
57073
57087
  };
57074
57088
  function createPassthroughHandler(slashCommand) {
57075
- return async (ctx) => {
57089
+ return async (ctx, args) => {
57076
57090
  if (ctx.commandContext === "first-message") {
57077
57091
  return { handled: false };
57078
57092
  }
57079
57093
  if (ctx.isAllowed) {
57080
- await ctx.sessionManager.sendFollowUp(ctx.threadId, `/${slashCommand}`, undefined, undefined, undefined, { system: true });
57094
+ const full = args ? `/${slashCommand} ${args}` : `/${slashCommand}`;
57095
+ await ctx.sessionManager.sendFollowUp(ctx.threadId, full, undefined, undefined, undefined, { system: true });
57081
57096
  }
57082
57097
  return { handled: true };
57083
57098
  };
@@ -57100,6 +57115,8 @@ handlers.set("plugin", handlePlugin);
57100
57115
  handlers.set("context", createPassthroughHandler("context"));
57101
57116
  handlers.set("cost", createPassthroughHandler("cost"));
57102
57117
  handlers.set("compact", createPassthroughHandler("compact"));
57118
+ handlers.set("model", createPassthroughHandler("model"));
57119
+ handlers.set("effort", createPassthroughHandler("effort"));
57103
57120
  // src/commands/system-prompt-generator.ts
57104
57121
  var log9 = createLogger("system-prompt");
57105
57122
  function formatUserCommand(cmd) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "1.23.0",
3
+ "version": "1.24.0",
4
4
  "description": "Run Claude Code from Slack or Mattermost. Sessions stream live into threads where your whole team can watch and steer.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",