claude-threads 1.0.5 → 1.0.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ 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.0.6] - 2026-01-13
9
+
10
+ ### Fixed
11
+ - **Pre-split tall content for new posts** - Height-aware splitting now also applies when creating new posts, not just when updating existing ones (#211)
12
+
8
13
  ## [1.0.5] - 2026-01-13
9
14
 
10
15
  ### Added
package/dist/index.js CHANGED
@@ -50860,9 +50860,9 @@ class DefaultContentBreaker {
50860
50860
  const searchWindow = content.substring(startPos, startPos + maxLookAhead);
50861
50861
  const codeBlockState = this.getCodeBlockState(content, startPos);
50862
50862
  if (codeBlockState.isInside) {
50863
- const codeBlockEndMatch2 = searchWindow.match(/^```$/m);
50864
- if (codeBlockEndMatch2 && codeBlockEndMatch2.index !== undefined) {
50865
- const pos = startPos + codeBlockEndMatch2.index + codeBlockEndMatch2[0].length;
50863
+ const codeBlockEndMatch = searchWindow.match(/^```$/m);
50864
+ if (codeBlockEndMatch && codeBlockEndMatch.index !== undefined) {
50865
+ const pos = startPos + codeBlockEndMatch.index + codeBlockEndMatch[0].length;
50866
50866
  const nextChar = content[pos];
50867
50867
  const finalPos = nextChar === `
50868
50868
  ` ? pos + 1 : pos;
@@ -50884,13 +50884,20 @@ class DefaultContentBreaker {
50884
50884
  return { position: pos, type: "heading" };
50885
50885
  }
50886
50886
  }
50887
- const codeBlockEndMatch = searchWindow.match(/^```$/m);
50888
- if (codeBlockEndMatch && codeBlockEndMatch.index !== undefined) {
50889
- const pos = startPos + codeBlockEndMatch.index + codeBlockEndMatch[0].length;
50890
- const nextChar = content[pos];
50891
- const finalPos = nextChar === `
50887
+ const codeBlockMarkerRegex = /^```$/gm;
50888
+ const matches = [...searchWindow.matchAll(codeBlockMarkerRegex)];
50889
+ for (const match of matches) {
50890
+ if (match.index !== undefined) {
50891
+ const matchPos = startPos + match.index;
50892
+ const stateAtMatch = this.getCodeBlockState(content, matchPos);
50893
+ if (stateAtMatch.isInside) {
50894
+ const pos = matchPos + match[0].length;
50895
+ const nextChar = content[pos];
50896
+ const finalPos = nextChar === `
50892
50897
  ` ? pos + 1 : pos;
50893
- return { position: finalPos, type: "code_block_end" };
50898
+ return { position: finalPos, type: "code_block_end" };
50899
+ }
50900
+ }
50894
50901
  }
50895
50902
  const paragraphMatch = searchWindow.match(/\n\n/);
50896
50903
  if (paragraphMatch && paragraphMatch.index !== undefined) {
@@ -50935,6 +50942,32 @@ class DefaultContentBreaker {
50935
50942
  return "none";
50936
50943
  }
50937
50944
  }
50945
+ function splitContentForHeight(content, contentBreaker) {
50946
+ const chunks = [];
50947
+ let remaining = content;
50948
+ const goodBreakpointTypes = new Set(["paragraph", "code_block_end", "heading", "tool_marker"]);
50949
+ while (remaining && contentBreaker.shouldFlushEarly(remaining)) {
50950
+ const breakpoint = contentBreaker.findLogicalBreakpoint(remaining, 0, remaining.length);
50951
+ if (!breakpoint || breakpoint.position <= 0 || breakpoint.position >= remaining.length) {
50952
+ break;
50953
+ }
50954
+ if (!goodBreakpointTypes.has(breakpoint.type)) {
50955
+ break;
50956
+ }
50957
+ const firstPart = remaining.substring(0, breakpoint.position).trim();
50958
+ const secondPart = remaining.substring(breakpoint.position).trim();
50959
+ if (!contentBreaker.shouldFlushEarly(firstPart) && secondPart.length > 0) {
50960
+ chunks.push(firstPart);
50961
+ remaining = secondPart;
50962
+ } else {
50963
+ break;
50964
+ }
50965
+ }
50966
+ if (remaining) {
50967
+ chunks.push(remaining);
50968
+ }
50969
+ return chunks.length ? chunks : [content];
50970
+ }
50938
50971
  // src/operations/post-tracker.ts
50939
50972
  class PostTracker {
50940
50973
  posts = new Map;
@@ -52437,11 +52470,19 @@ class ContentExecutor extends BaseExecutor {
52437
52470
  this.state.currentPostContent = "";
52438
52471
  }
52439
52472
  } else {
52473
+ const chunks = splitContentForHeight(content, ctx.contentBreaker);
52440
52474
  ctx.threadLogger?.logExecutor("content", "create_start", "none", {
52441
52475
  contentLength: content.length,
52476
+ chunkCount: chunks.length,
52442
52477
  reason: "no_currentPostId"
52443
52478
  }, "flush");
52444
- await this.createNewPost(ctx, content, pendingAtFlushStart);
52479
+ for (let i2 = 0;i2 < chunks.length; i2++) {
52480
+ await this.createNewPost(ctx, chunks[i2], pendingAtFlushStart);
52481
+ if (i2 < chunks.length - 1) {
52482
+ this.state.currentPostId = null;
52483
+ this.state.currentPostContent = "";
52484
+ }
52485
+ }
52445
52486
  }
52446
52487
  }
52447
52488
  async handleSplit(ctx, content, pendingAtFlushStart, hardThreshold) {
@@ -35569,9 +35569,9 @@ class DefaultContentBreaker {
35569
35569
  const searchWindow = content.substring(startPos, startPos + maxLookAhead);
35570
35570
  const codeBlockState = this.getCodeBlockState(content, startPos);
35571
35571
  if (codeBlockState.isInside) {
35572
- const codeBlockEndMatch2 = searchWindow.match(/^```$/m);
35573
- if (codeBlockEndMatch2 && codeBlockEndMatch2.index !== undefined) {
35574
- const pos = startPos + codeBlockEndMatch2.index + codeBlockEndMatch2[0].length;
35572
+ const codeBlockEndMatch = searchWindow.match(/^```$/m);
35573
+ if (codeBlockEndMatch && codeBlockEndMatch.index !== undefined) {
35574
+ const pos = startPos + codeBlockEndMatch.index + codeBlockEndMatch[0].length;
35575
35575
  const nextChar = content[pos];
35576
35576
  const finalPos = nextChar === `
35577
35577
  ` ? pos + 1 : pos;
@@ -35593,13 +35593,20 @@ class DefaultContentBreaker {
35593
35593
  return { position: pos, type: "heading" };
35594
35594
  }
35595
35595
  }
35596
- const codeBlockEndMatch = searchWindow.match(/^```$/m);
35597
- if (codeBlockEndMatch && codeBlockEndMatch.index !== undefined) {
35598
- const pos = startPos + codeBlockEndMatch.index + codeBlockEndMatch[0].length;
35599
- const nextChar = content[pos];
35600
- const finalPos = nextChar === `
35596
+ const codeBlockMarkerRegex = /^```$/gm;
35597
+ const matches = [...searchWindow.matchAll(codeBlockMarkerRegex)];
35598
+ for (const match of matches) {
35599
+ if (match.index !== undefined) {
35600
+ const matchPos = startPos + match.index;
35601
+ const stateAtMatch = this.getCodeBlockState(content, matchPos);
35602
+ if (stateAtMatch.isInside) {
35603
+ const pos = matchPos + match[0].length;
35604
+ const nextChar = content[pos];
35605
+ const finalPos = nextChar === `
35601
35606
  ` ? pos + 1 : pos;
35602
- return { position: finalPos, type: "code_block_end" };
35607
+ return { position: finalPos, type: "code_block_end" };
35608
+ }
35609
+ }
35603
35610
  }
35604
35611
  const paragraphMatch = searchWindow.match(/\n\n/);
35605
35612
  if (paragraphMatch && paragraphMatch.index !== undefined) {
@@ -35644,6 +35651,32 @@ class DefaultContentBreaker {
35644
35651
  return "none";
35645
35652
  }
35646
35653
  }
35654
+ function splitContentForHeight(content, contentBreaker) {
35655
+ const chunks = [];
35656
+ let remaining = content;
35657
+ const goodBreakpointTypes = new Set(["paragraph", "code_block_end", "heading", "tool_marker"]);
35658
+ while (remaining && contentBreaker.shouldFlushEarly(remaining)) {
35659
+ const breakpoint = contentBreaker.findLogicalBreakpoint(remaining, 0, remaining.length);
35660
+ if (!breakpoint || breakpoint.position <= 0 || breakpoint.position >= remaining.length) {
35661
+ break;
35662
+ }
35663
+ if (!goodBreakpointTypes.has(breakpoint.type)) {
35664
+ break;
35665
+ }
35666
+ const firstPart = remaining.substring(0, breakpoint.position).trim();
35667
+ const secondPart = remaining.substring(breakpoint.position).trim();
35668
+ if (!contentBreaker.shouldFlushEarly(firstPart) && secondPart.length > 0) {
35669
+ chunks.push(firstPart);
35670
+ remaining = secondPart;
35671
+ } else {
35672
+ break;
35673
+ }
35674
+ }
35675
+ if (remaining) {
35676
+ chunks.push(remaining);
35677
+ }
35678
+ return chunks.length ? chunks : [content];
35679
+ }
35647
35680
  // src/operations/post-tracker.ts
35648
35681
  class PostTracker {
35649
35682
  posts = new Map;
@@ -37237,11 +37270,19 @@ class ContentExecutor extends BaseExecutor {
37237
37270
  this.state.currentPostContent = "";
37238
37271
  }
37239
37272
  } else {
37273
+ const chunks = splitContentForHeight(content, ctx.contentBreaker);
37240
37274
  ctx.threadLogger?.logExecutor("content", "create_start", "none", {
37241
37275
  contentLength: content.length,
37276
+ chunkCount: chunks.length,
37242
37277
  reason: "no_currentPostId"
37243
37278
  }, "flush");
37244
- await this.createNewPost(ctx, content, pendingAtFlushStart);
37279
+ for (let i = 0;i < chunks.length; i++) {
37280
+ await this.createNewPost(ctx, chunks[i], pendingAtFlushStart);
37281
+ if (i < chunks.length - 1) {
37282
+ this.state.currentPostId = null;
37283
+ this.state.currentPostContent = "";
37284
+ }
37285
+ }
37245
37286
  }
37246
37287
  }
37247
37288
  async handleSplit(ctx, content, pendingAtFlushStart, hardThreshold) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
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",