claude-threads 1.0.0 → 1.0.2

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,21 @@ 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.2] - 2026-01-13
9
+
10
+ ### Fixed
11
+ - **Session header posts deleted by sticky cleanup** - Fixed bug where session header and task list posts were incorrectly deleted by the sticky message cleanup function (#204)
12
+ - **Table rendering regression** - Fixed pipe escaping in `formatKeyValueList` and missing blank line before tables (#203)
13
+
14
+ ## [1.0.1] - 2026-01-13
15
+
16
+ ### Changed
17
+ - **Cleaner session header** - Simplified session start message, moved detailed info to help menu (#202)
18
+
19
+ ### Fixed
20
+ - **Worktree prompt skipped when branch specified** - When starting a session with a branch name in the initial message (e.g., `@bot on branch fix/bug do X`), worktree prompt is now correctly skipped (#201)
21
+ - **Pipe characters in markdown tables** - Fixed escaping of `|` characters in help menu table rows (#200)
22
+
8
23
  ## [1.0.0] - 2026-01-13
9
24
 
10
25
  ### Changed
package/dist/index.js CHANGED
@@ -11826,11 +11826,11 @@ var require_util3 = __commonJS((exports) => {
11826
11826
  if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
11827
11827
  return name2;
11828
11828
  }
11829
- const dirname7 = path6.dirname(name2);
11830
- if (dirname7 === name2) {
11829
+ const dirname8 = path6.dirname(name2);
11830
+ if (dirname8 === name2) {
11831
11831
  return original;
11832
11832
  }
11833
- return find(dirname7, original);
11833
+ return find(dirname8, original);
11834
11834
  } catch (error) {
11835
11835
  if (name2 === original) {
11836
11836
  if (error.code === "ENOENT") {
@@ -46708,14 +46708,16 @@ ${code}
46708
46708
  return text.replace(/([*_`[\]()#+\-.!])/g, "\\$1");
46709
46709
  }
46710
46710
  formatTable(headers, rows) {
46711
- const headerRow = `| ${headers.join(" | ")} |`;
46711
+ const escapeCell = (cell) => cell.replace(/\|/g, "\\|");
46712
+ const headerRow = `| ${headers.map(escapeCell).join(" | ")} |`;
46712
46713
  const separatorRow = `| ${headers.map(() => "---").join(" | ")} |`;
46713
- const dataRows = rows.map((row) => `| ${row.join(" | ")} |`);
46714
+ const dataRows = rows.map((row) => `| ${row.map(escapeCell).join(" | ")} |`);
46714
46715
  return [headerRow, separatorRow, ...dataRows].join(`
46715
46716
  `);
46716
46717
  }
46717
46718
  formatKeyValueList(items) {
46718
- const rows = items.map(([icon, label, value]) => `| ${icon} ${this.formatBold(label)} | ${value} |`);
46719
+ const escapeCell = (cell) => cell.replace(/\|/g, "\\|");
46720
+ const rows = items.map(([icon, label, value]) => `| ${icon} ${this.formatBold(escapeCell(label))} | ${escapeCell(value)} |`);
46719
46721
  return ["| | |", "|---|---|", ...rows].join(`
46720
46722
  `);
46721
46723
  }
@@ -54499,6 +54501,121 @@ function formatUptime(startedAt) {
54499
54501
  return `${minutes}m`;
54500
54502
  }
54501
54503
 
54504
+ // src/changelog.ts
54505
+ import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
54506
+ import { dirname as dirname7, resolve as resolve4 } from "path";
54507
+ import { fileURLToPath as fileURLToPath4 } from "url";
54508
+ var __dirname4 = dirname7(fileURLToPath4(import.meta.url));
54509
+ function getReleaseNotes(version) {
54510
+ const possiblePaths = [
54511
+ resolve4(__dirname4, "..", "CHANGELOG.md"),
54512
+ resolve4(__dirname4, "..", "..", "CHANGELOG.md")
54513
+ ];
54514
+ let changelogPath = null;
54515
+ for (const p of possiblePaths) {
54516
+ if (existsSync8(p)) {
54517
+ changelogPath = p;
54518
+ break;
54519
+ }
54520
+ }
54521
+ if (!changelogPath) {
54522
+ return null;
54523
+ }
54524
+ try {
54525
+ const content = readFileSync7(changelogPath, "utf-8");
54526
+ return parseChangelog(content, version);
54527
+ } catch {
54528
+ return null;
54529
+ }
54530
+ }
54531
+ function parseChangelog(content, targetVersion) {
54532
+ const lines = content.split(`
54533
+ `);
54534
+ let currentVersion = null;
54535
+ let currentDate = null;
54536
+ let currentSection = null;
54537
+ let sections = {};
54538
+ let foundTarget = false;
54539
+ for (const line of lines) {
54540
+ const versionMatch = line.match(/^## \[(\d+\.\d+\.\d+)\](?: - (\d{4}-\d{2}-\d{2}))?/);
54541
+ if (versionMatch) {
54542
+ if (foundTarget) {
54543
+ break;
54544
+ }
54545
+ currentVersion = versionMatch[1];
54546
+ currentDate = versionMatch[2] || "";
54547
+ sections = {};
54548
+ currentSection = null;
54549
+ if (!targetVersion || currentVersion === targetVersion) {
54550
+ foundTarget = true;
54551
+ }
54552
+ continue;
54553
+ }
54554
+ if (!foundTarget)
54555
+ continue;
54556
+ const sectionMatch = line.match(/^### (\w+)/);
54557
+ if (sectionMatch) {
54558
+ currentSection = sectionMatch[1];
54559
+ sections[currentSection] = [];
54560
+ continue;
54561
+ }
54562
+ const itemMatch = line.match(/^- (.+)/);
54563
+ if (itemMatch && currentSection) {
54564
+ sections[currentSection].push(itemMatch[1]);
54565
+ }
54566
+ }
54567
+ if (!foundTarget || !currentVersion) {
54568
+ return null;
54569
+ }
54570
+ return {
54571
+ version: currentVersion,
54572
+ date: currentDate || "",
54573
+ sections
54574
+ };
54575
+ }
54576
+ function formatReleaseNotes(notes, formatter) {
54577
+ let msg = `${formatter.formatHeading(`\uD83D\uDCCB Release Notes - v${notes.version}`, 3)}`;
54578
+ if (notes.date) {
54579
+ msg += ` (${notes.date})`;
54580
+ }
54581
+ msg += `
54582
+
54583
+ `;
54584
+ for (const [section, items] of Object.entries(notes.sections)) {
54585
+ if (items.length === 0)
54586
+ continue;
54587
+ const emoji = section === "Added" ? "\u2728" : section === "Fixed" ? "\uD83D\uDC1B" : section === "Changed" ? "\uD83D\uDD04" : section === "Removed" ? "\uD83D\uDDD1\uFE0F" : "\u2022";
54588
+ msg += `${formatter.formatBold(`${emoji} ${section}`)}
54589
+ `;
54590
+ for (const item of items) {
54591
+ msg += `- ${item}
54592
+ `;
54593
+ }
54594
+ msg += `
54595
+ `;
54596
+ }
54597
+ return msg.trim();
54598
+ }
54599
+ function getWhatsNewSummary(notes) {
54600
+ const items = [];
54601
+ for (const section of ["Added", "Fixed", "Changed"]) {
54602
+ const sectionItems = notes.sections[section] || [];
54603
+ for (const item of sectionItems) {
54604
+ const short = item.split(" - ")[0].replace(/\*\*/g, "");
54605
+ if (short.length <= 50) {
54606
+ items.push(short);
54607
+ } else {
54608
+ items.push(short.substring(0, 47) + "...");
54609
+ }
54610
+ if (items.length >= 2)
54611
+ break;
54612
+ }
54613
+ if (items.length >= 2)
54614
+ break;
54615
+ }
54616
+ return items.join(", ");
54617
+ }
54618
+
54502
54619
  // src/utils/pr-detector.ts
54503
54620
  var PR_PATTERNS = [
54504
54621
  {
@@ -54835,6 +54952,12 @@ async function buildStickyMessage(sessions, platformId, config, formatter, getTh
54835
54952
  lines2.push(...formatHistoryEntry(historySession, formatter, getThreadLink));
54836
54953
  }
54837
54954
  }
54955
+ const releaseNotes2 = getReleaseNotes(VERSION);
54956
+ const whatsNew2 = releaseNotes2 ? getWhatsNewSummary(releaseNotes2) : "";
54957
+ if (whatsNew2) {
54958
+ lines2.push("");
54959
+ lines2.push(`\u2728 ${formatter.formatBold("What's new:")} ${whatsNew2}`);
54960
+ }
54838
54961
  lines2.push("");
54839
54962
  lines2.push(`${formatter.formatItalic("Mention me to start a session")} \xB7 ${formatter.formatCode("bun install -g claude-threads")} \xB7 ${formatter.formatLink("claude-threads.run", "https://claude-threads.run/")}`);
54840
54963
  return lines2.join(`
@@ -54891,6 +55014,12 @@ async function buildStickyMessage(sessions, platformId, config, formatter, getTh
54891
55014
  lines.push(...formatHistoryEntry(historySession, formatter, getThreadLink));
54892
55015
  }
54893
55016
  }
55017
+ const releaseNotes = getReleaseNotes(VERSION);
55018
+ const whatsNew = releaseNotes ? getWhatsNewSummary(releaseNotes) : "";
55019
+ if (whatsNew) {
55020
+ lines.push("");
55021
+ lines.push(`\u2728 ${formatter.formatBold("What's new:")} ${whatsNew}`);
55022
+ }
54894
55023
  lines.push("");
54895
55024
  lines.push(`${formatter.formatItalic("Mention me to start a session")} \xB7 ${formatter.formatCode("bun install -g claude-threads")} \xB7 ${formatter.formatLink("claude-threads.run", "https://claude-threads.run/")}`);
54896
55025
  return lines.join(`
@@ -54903,8 +55032,8 @@ async function updateStickyMessage(platform, sessions, config) {
54903
55032
  await pendingUpdate;
54904
55033
  }
54905
55034
  let releaseLock;
54906
- const lock = new Promise((resolve4) => {
54907
- releaseLock = resolve4;
55035
+ const lock = new Promise((resolve5) => {
55036
+ releaseLock = resolve5;
54908
55037
  });
54909
55038
  updateLocks.set(platformId, lock);
54910
55039
  try {
@@ -54973,8 +55102,21 @@ async function updateStickyMessageImpl(platform, sessions, config) {
54973
55102
  sessionStore.saveStickyPostId(platform.platformId, post2.id);
54974
55103
  }
54975
55104
  log17.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
55105
+ const excludePostIds = new Set;
55106
+ if (sessionStore) {
55107
+ for (const session of sessionStore.load().values()) {
55108
+ if (session.platformId === platform.platformId) {
55109
+ if (session.sessionStartPostId) {
55110
+ excludePostIds.add(session.sessionStartPostId);
55111
+ }
55112
+ if (session.tasksPostId) {
55113
+ excludePostIds.add(session.tasksPostId);
55114
+ }
55115
+ }
55116
+ }
55117
+ }
54976
55118
  const botUser = await platform.getBotUser();
54977
- cleanupOldStickyMessages(platform, botUser.id).catch((err) => {
55119
+ cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
54978
55120
  log17.debug(`Background cleanup failed: ${err}`);
54979
55121
  });
54980
55122
  } catch (err) {
@@ -54997,7 +55139,7 @@ function isRecentPost(postId) {
54997
55139
  }
54998
55140
  return true;
54999
55141
  }
55000
- async function cleanupOldStickyMessages(platform, botUserId, forceRun = false) {
55142
+ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, excludePostIds) {
55001
55143
  const platformId = platform.platformId;
55002
55144
  const now = Date.now();
55003
55145
  if (!forceRun) {
@@ -55011,7 +55153,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false) {
55011
55153
  const currentStickyId = stickyPostIds.get(platformId);
55012
55154
  try {
55013
55155
  const pinnedPostIds = await platform.getPinnedPosts();
55014
- const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && isRecentPost(id));
55156
+ const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
55015
55157
  if (recentPinnedIds.length === 0) {
55016
55158
  log17.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
55017
55159
  return;
@@ -55688,7 +55830,7 @@ import { existsSync as existsSync9, statSync as statSync3 } from "fs";
55688
55830
  // node_modules/update-notifier/update-notifier.js
55689
55831
  import process10 from "process";
55690
55832
  import { spawn as spawn5 } from "child_process";
55691
- import { fileURLToPath as fileURLToPath5 } from "url";
55833
+ import { fileURLToPath as fileURLToPath6 } from "url";
55692
55834
  import path9 from "path";
55693
55835
  import { format } from "util";
55694
55836
 
@@ -55763,7 +55905,7 @@ var retryifyAsync = (fn, options) => {
55763
55905
  throw error;
55764
55906
  const delay = Math.round(interval * Math.random());
55765
55907
  if (delay > 0) {
55766
- const delayPromise = new Promise((resolve4) => setTimeout(resolve4, delay));
55908
+ const delayPromise = new Promise((resolve5) => setTimeout(resolve5, delay));
55767
55909
  return delayPromise.then(() => attempt.apply(undefined, args));
55768
55910
  } else {
55769
55911
  return attempt.apply(undefined, args);
@@ -57316,14 +57458,14 @@ class TimeoutError extends Error {
57316
57458
 
57317
57459
  // node_modules/ky/distribution/utils/timeout.js
57318
57460
  async function timeout(request, init, abortController, options) {
57319
- return new Promise((resolve4, reject) => {
57461
+ return new Promise((resolve5, reject) => {
57320
57462
  const timeoutId = setTimeout(() => {
57321
57463
  if (abortController) {
57322
57464
  abortController.abort();
57323
57465
  }
57324
57466
  reject(new TimeoutError(request));
57325
57467
  }, options.timeout);
57326
- options.fetch(request, init).then(resolve4).catch(reject).then(() => {
57468
+ options.fetch(request, init).then(resolve5).catch(reject).then(() => {
57327
57469
  clearTimeout(timeoutId);
57328
57470
  });
57329
57471
  });
@@ -57331,7 +57473,7 @@ async function timeout(request, init, abortController, options) {
57331
57473
 
57332
57474
  // node_modules/ky/distribution/utils/delay.js
57333
57475
  async function delay(ms, { signal }) {
57334
- return new Promise((resolve4, reject) => {
57476
+ return new Promise((resolve5, reject) => {
57335
57477
  if (signal) {
57336
57478
  signal.throwIfAborted();
57337
57479
  signal.addEventListener("abort", abortHandler, { once: true });
@@ -57342,7 +57484,7 @@ async function delay(ms, { signal }) {
57342
57484
  }
57343
57485
  const timeoutId = setTimeout(() => {
57344
57486
  signal?.removeEventListener("abort", abortHandler);
57345
- resolve4();
57487
+ resolve5();
57346
57488
  }, ms);
57347
57489
  });
57348
57490
  }
@@ -57827,7 +57969,7 @@ var isNpmOrYarn = isNpm || isYarn;
57827
57969
  // node_modules/is-installed-globally/index.js
57828
57970
  import fs5 from "fs";
57829
57971
  import path8 from "path";
57830
- import { fileURLToPath as fileURLToPath4 } from "url";
57972
+ import { fileURLToPath as fileURLToPath5 } from "url";
57831
57973
 
57832
57974
  // node_modules/global-directory/index.js
57833
57975
  var import_ini = __toESM(require_ini3(), 1);
@@ -57928,10 +58070,10 @@ function isPathInside(childPath, parentPath) {
57928
58070
  }
57929
58071
 
57930
58072
  // node_modules/is-installed-globally/index.js
57931
- var __dirname4 = path8.dirname(fileURLToPath4(import.meta.url));
58073
+ var __dirname5 = path8.dirname(fileURLToPath5(import.meta.url));
57932
58074
  var isInstalledGlobally = (() => {
57933
58075
  try {
57934
- return isPathInside(__dirname4, global_directory_default.yarn.packages) || isPathInside(__dirname4, fs5.realpathSync(global_directory_default.npm.packages));
58076
+ return isPathInside(__dirname5, global_directory_default.yarn.packages) || isPathInside(__dirname5, fs5.realpathSync(global_directory_default.npm.packages));
57935
58077
  } catch {
57936
58078
  return false;
57937
58079
  }
@@ -58960,7 +59102,7 @@ function pupa(template, data, { ignoreMissing = false, transform = ({ value }) =
58960
59102
  }
58961
59103
 
58962
59104
  // node_modules/update-notifier/update-notifier.js
58963
- var __dirname5 = path9.dirname(fileURLToPath5(import.meta.url));
59105
+ var __dirname6 = path9.dirname(fileURLToPath6(import.meta.url));
58964
59106
  var ONE_DAY = 1000 * 60 * 60 * 24;
58965
59107
 
58966
59108
  class UpdateNotifier {
@@ -59017,7 +59159,7 @@ class UpdateNotifier {
59017
59159
  if (Date.now() - this.config.get("lastUpdateCheck") < this.#updateCheckInterval) {
59018
59160
  return;
59019
59161
  }
59020
- spawn5(process10.execPath, [path9.join(__dirname5, "check.js"), JSON.stringify(this.#options)], {
59162
+ spawn5(process10.execPath, [path9.join(__dirname6, "check.js"), JSON.stringify(this.#options)], {
59021
59163
  detached: true,
59022
59164
  stdio: "ignore"
59023
59165
  }).unref();
@@ -59105,121 +59247,6 @@ function getUpdateInfo() {
59105
59247
  return cachedUpdateInfo;
59106
59248
  }
59107
59249
 
59108
- // src/changelog.ts
59109
- import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
59110
- import { dirname as dirname7, resolve as resolve4 } from "path";
59111
- import { fileURLToPath as fileURLToPath6 } from "url";
59112
- var __dirname6 = dirname7(fileURLToPath6(import.meta.url));
59113
- function getReleaseNotes(version) {
59114
- const possiblePaths = [
59115
- resolve4(__dirname6, "..", "CHANGELOG.md"),
59116
- resolve4(__dirname6, "..", "..", "CHANGELOG.md")
59117
- ];
59118
- let changelogPath = null;
59119
- for (const p of possiblePaths) {
59120
- if (existsSync8(p)) {
59121
- changelogPath = p;
59122
- break;
59123
- }
59124
- }
59125
- if (!changelogPath) {
59126
- return null;
59127
- }
59128
- try {
59129
- const content = readFileSync7(changelogPath, "utf-8");
59130
- return parseChangelog(content, version);
59131
- } catch {
59132
- return null;
59133
- }
59134
- }
59135
- function parseChangelog(content, targetVersion) {
59136
- const lines = content.split(`
59137
- `);
59138
- let currentVersion = null;
59139
- let currentDate = null;
59140
- let currentSection = null;
59141
- let sections = {};
59142
- let foundTarget = false;
59143
- for (const line of lines) {
59144
- const versionMatch = line.match(/^## \[(\d+\.\d+\.\d+)\](?: - (\d{4}-\d{2}-\d{2}))?/);
59145
- if (versionMatch) {
59146
- if (foundTarget) {
59147
- break;
59148
- }
59149
- currentVersion = versionMatch[1];
59150
- currentDate = versionMatch[2] || "";
59151
- sections = {};
59152
- currentSection = null;
59153
- if (!targetVersion || currentVersion === targetVersion) {
59154
- foundTarget = true;
59155
- }
59156
- continue;
59157
- }
59158
- if (!foundTarget)
59159
- continue;
59160
- const sectionMatch = line.match(/^### (\w+)/);
59161
- if (sectionMatch) {
59162
- currentSection = sectionMatch[1];
59163
- sections[currentSection] = [];
59164
- continue;
59165
- }
59166
- const itemMatch = line.match(/^- (.+)/);
59167
- if (itemMatch && currentSection) {
59168
- sections[currentSection].push(itemMatch[1]);
59169
- }
59170
- }
59171
- if (!foundTarget || !currentVersion) {
59172
- return null;
59173
- }
59174
- return {
59175
- version: currentVersion,
59176
- date: currentDate || "",
59177
- sections
59178
- };
59179
- }
59180
- function formatReleaseNotes(notes, formatter) {
59181
- let msg = `${formatter.formatHeading(`\uD83D\uDCCB Release Notes - v${notes.version}`, 3)}`;
59182
- if (notes.date) {
59183
- msg += ` (${notes.date})`;
59184
- }
59185
- msg += `
59186
-
59187
- `;
59188
- for (const [section, items] of Object.entries(notes.sections)) {
59189
- if (items.length === 0)
59190
- continue;
59191
- const emoji = section === "Added" ? "\u2728" : section === "Fixed" ? "\uD83D\uDC1B" : section === "Changed" ? "\uD83D\uDD04" : section === "Removed" ? "\uD83D\uDDD1\uFE0F" : "\u2022";
59192
- msg += `${formatter.formatBold(`${emoji} ${section}`)}
59193
- `;
59194
- for (const item of items) {
59195
- msg += `- ${item}
59196
- `;
59197
- }
59198
- msg += `
59199
- `;
59200
- }
59201
- return msg.trim();
59202
- }
59203
- function getWhatsNewSummary(notes) {
59204
- const items = [];
59205
- for (const section of ["Added", "Fixed", "Changed"]) {
59206
- const sectionItems = notes.sections[section] || [];
59207
- for (const item of sectionItems) {
59208
- const short = item.split(" - ")[0].replace(/\*\*/g, "");
59209
- if (short.length <= 50) {
59210
- items.push(short);
59211
- } else {
59212
- items.push(short.substring(0, 47) + "...");
59213
- }
59214
- if (items.length >= 2)
59215
- break;
59216
- }
59217
- if (items.length >= 2)
59218
- break;
59219
- }
59220
- return items.join(", ");
59221
- }
59222
-
59223
59250
  // src/operations/commands/handler.ts
59224
59251
  init_emoji();
59225
59252
  var log18 = createLogger("commands");
@@ -59473,6 +59500,9 @@ async function updateSessionHeader(session, ctx) {
59473
59500
  const permMode = isInteractive ? "\uD83D\uDD10 Interactive" : "\u26A1 Auto";
59474
59501
  const otherParticipants = [...session.sessionAllowedUsers].filter((u) => u !== session.startedBy).map((u) => formatter.formatUserMention(u)).join(", ");
59475
59502
  const statusItems = [];
59503
+ const claudeVersion = getClaudeCliVersion();
59504
+ const versionStr = claudeVersion ? `v${VERSION} \xB7 CLI ${claudeVersion}` : `v${VERSION}`;
59505
+ statusItems.push(formatter.formatCode(versionStr));
59476
59506
  if (session.usageStats) {
59477
59507
  const stats = session.usageStats;
59478
59508
  statusItems.push(formatter.formatCode(`\uD83E\uDD16 ${stats.modelDisplayName}`));
@@ -59536,34 +59566,24 @@ async function updateSessionHeader(session, ctx) {
59536
59566
  if (otherParticipants) {
59537
59567
  items.push(["\uD83D\uDC65", "Participants", otherParticipants]);
59538
59568
  }
59539
- const claudeVersion = getClaudeCliVersion();
59540
- if (claudeVersion) {
59541
- items.push(["\uD83E\uDD16", "Claude CLI", formatter.formatCode(claudeVersion)]);
59542
- }
59543
59569
  items.push(["\uD83C\uDD94", "Session ID", formatter.formatCode(session.claudeSessionId.substring(0, 8))]);
59544
59570
  const logPath = getLogFilePath(session.platform.platformId, session.claudeSessionId);
59545
59571
  const shortLogPath = logPath.replace(process.env.HOME || "", "~");
59546
59572
  items.push(["\uD83D\uDCCB", "Log File", formatter.formatCode(shortLogPath)]);
59547
59573
  const updateInfo = getUpdateInfo();
59548
- const updateNotice = updateInfo ? `
59549
- > \u26A0\uFE0F ${formatter.formatBold("Update available:")} v${updateInfo.current} \u2192 v${updateInfo.latest} - Run ${formatter.formatCode("bun install -g claude-threads")}
59550
- ` : "";
59551
- const releaseNotes = getReleaseNotes(VERSION);
59552
- const whatsNew = releaseNotes ? getWhatsNewSummary(releaseNotes) : "";
59553
- const whatsNewLine = whatsNew ? `
59554
- > \u2728 ${formatter.formatBold("What's new:")} ${whatsNew}
59555
- ` : "";
59574
+ const updateNotice = updateInfo ? `> \u26A0\uFE0F ${formatter.formatBold("Update available:")} v${updateInfo.current} \u2192 v${updateInfo.latest} - Run ${formatter.formatCode("bun install -g claude-threads")}
59575
+
59576
+ ` : undefined;
59556
59577
  const msg = [
59557
- getLogo(VERSION),
59558
59578
  updateNotice,
59559
- whatsNewLine,
59560
59579
  statusBar,
59561
59580
  "",
59562
59581
  formatter.formatKeyValueList(items)
59563
- ].join(`
59582
+ ].filter((item) => item !== null && item !== undefined).join(`
59564
59583
  `);
59565
59584
  const postId = session.sessionStartPostId;
59566
59585
  await updatePost2(session, postId, msg);
59586
+ session.platform.pinPost(postId).catch(() => {});
59567
59587
  }
59568
59588
  async function showUpdateStatus(session, updateManager, ctx) {
59569
59589
  const formatter = session.platform.getFormatter();
@@ -61133,6 +61153,9 @@ ${CHAT_PLATFORM_PROMPT}`;
61133
61153
  fireMetadataSuggestions(session, options.prompt, ctx);
61134
61154
  keepAlive.sessionStarted();
61135
61155
  await ctx.ops.updateSessionHeader(session);
61156
+ if (session.sessionStartPostId) {
61157
+ await platform.pinPost(session.sessionStartPostId).catch(() => {});
61158
+ }
61136
61159
  await ctx.ops.updateStickyMessage();
61137
61160
  ctx.ops.startTyping(session);
61138
61161
  claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
@@ -61147,7 +61170,7 @@ ${CHAT_PLATFORM_PROMPT}`;
61147
61170
  await ctx.ops.updateStickyMessage();
61148
61171
  return;
61149
61172
  }
61150
- const shouldPrompt = await ctx.ops.shouldPromptForWorktree(session);
61173
+ const shouldPrompt = options.skipWorktreePrompt ? null : await ctx.ops.shouldPromptForWorktree(session);
61151
61174
  if (shouldPrompt) {
61152
61175
  session.queuedPrompt = options.prompt;
61153
61176
  session.queuedFiles = options.files;
@@ -61332,6 +61355,9 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
61332
61355
  await post(session, "resume", restartMsg);
61333
61356
  }
61334
61357
  await ctx.ops.updateSessionHeader(session);
61358
+ if (session.sessionStartPostId) {
61359
+ await session.platform.pinPost(session.sessionStartPostId).catch(() => {});
61360
+ }
61335
61361
  await ctx.ops.updateStickyMessage();
61336
61362
  ctx.ops.persistSession(session);
61337
61363
  } catch (err) {
@@ -62224,15 +62250,6 @@ class SessionManager extends EventEmitter5 {
62224
62250
  initialize(this.sessionStore);
62225
62251
  this.sessionMonitor?.start();
62226
62252
  this.backgroundCleanup?.start();
62227
- for (const platform of this.platforms.values()) {
62228
- platform.getBotUser().then((botUser) => {
62229
- cleanupOldStickyMessages(platform, botUser.id, true).catch((err) => {
62230
- log26.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
62231
- });
62232
- }).catch((err) => {
62233
- log26.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
62234
- });
62235
- }
62236
62253
  const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
62237
62254
  const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
62238
62255
  if (staleIds.length > 0) {
@@ -62244,6 +62261,31 @@ class SessionManager extends EventEmitter5 {
62244
62261
  }
62245
62262
  const persisted = this.sessionStore.load();
62246
62263
  log26.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
62264
+ const excludePostIdsByPlatform = new Map;
62265
+ for (const session of persisted.values()) {
62266
+ const platformId = session.platformId;
62267
+ let excludeSet = excludePostIdsByPlatform.get(platformId);
62268
+ if (!excludeSet) {
62269
+ excludeSet = new Set;
62270
+ excludePostIdsByPlatform.set(platformId, excludeSet);
62271
+ }
62272
+ if (session.sessionStartPostId) {
62273
+ excludeSet.add(session.sessionStartPostId);
62274
+ }
62275
+ if (session.tasksPostId) {
62276
+ excludeSet.add(session.tasksPostId);
62277
+ }
62278
+ }
62279
+ for (const platform of this.platforms.values()) {
62280
+ const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
62281
+ platform.getBotUser().then((botUser) => {
62282
+ cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
62283
+ log26.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
62284
+ });
62285
+ }).catch((err) => {
62286
+ log26.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
62287
+ });
62288
+ }
62247
62289
  if (persisted.size > 0) {
62248
62290
  const activeToResume = [];
62249
62291
  const pausedToSkip = [];
@@ -62501,7 +62543,7 @@ class SessionManager extends EventEmitter5 {
62501
62543
  return session.sessionAllowedUsers.has(username) || session.platform.isUserAllowed(username);
62502
62544
  }
62503
62545
  async startSessionWithWorktree(options, branch, username, replyToPostId, platformId = "default", displayName) {
62504
- await this.startSession(options, username, replyToPostId, platformId, displayName);
62546
+ await this.startSession({ ...options, skipWorktreePrompt: true }, username, replyToPostId, platformId, displayName);
62505
62547
  const threadId = replyToPostId || "";
62506
62548
  const session = this.registry.find(platformId, threadId);
62507
62549
  if (session) {
@@ -39290,6 +39290,11 @@ function loadPackageJson() {
39290
39290
  var pkgInfo = loadPackageJson();
39291
39291
  var VERSION = pkgInfo.version;
39292
39292
 
39293
+ // src/changelog.ts
39294
+ import { dirname as dirname2, resolve as resolve2 } from "path";
39295
+ import { fileURLToPath as fileURLToPath2 } from "url";
39296
+ var __dirname3 = dirname2(fileURLToPath2(import.meta.url));
39297
+
39293
39298
  // src/claude/version-check.ts
39294
39299
  var import_semver = __toESM(require_semver2(), 1);
39295
39300
 
@@ -39721,7 +39726,7 @@ class Redactor {
39721
39726
 
39722
39727
  // src/persistence/thread-logger.ts
39723
39728
  import { homedir } from "os";
39724
- import { join, dirname as dirname2 } from "path";
39729
+ import { join, dirname as dirname3 } from "path";
39725
39730
  var log5 = createLogger("thread-log");
39726
39731
  var LOGS_BASE_DIR = join(homedir(), ".claude-threads", "logs");
39727
39732
 
@@ -39743,8 +39748,8 @@ class UsernameAnonymizer {
39743
39748
  // src/claude/cli.ts
39744
39749
  import { spawn as spawn2 } from "child_process";
39745
39750
  import { EventEmitter as EventEmitter2 } from "events";
39746
- import { resolve as resolve2, dirname as dirname3 } from "path";
39747
- import { fileURLToPath as fileURLToPath2 } from "url";
39751
+ import { resolve as resolve3, dirname as dirname4 } from "path";
39752
+ import { fileURLToPath as fileURLToPath3 } from "url";
39748
39753
  import { existsSync as existsSync2, readFileSync as readFileSync2, watchFile, unwatchFile, unlinkSync, statSync, readdirSync } from "fs";
39749
39754
  import { tmpdir } from "os";
39750
39755
  import { join as join2 } from "path";
@@ -40007,7 +40012,7 @@ class ClaudeCli extends EventEmitter2 {
40007
40012
  const pid = proc.pid;
40008
40013
  this.process = null;
40009
40014
  this.log.debug(`Killing Claude process (pid=${pid})`);
40010
- return new Promise((resolve3) => {
40015
+ return new Promise((resolve4) => {
40011
40016
  this.log.debug("Sending first SIGINT");
40012
40017
  proc.kill("SIGINT");
40013
40018
  const secondSigint = setTimeout(() => {
@@ -40026,7 +40031,7 @@ class ClaudeCli extends EventEmitter2 {
40026
40031
  this.log.debug(`Claude process exited (code=${code})`);
40027
40032
  clearTimeout(secondSigint);
40028
40033
  clearTimeout(forceKillTimeout);
40029
- resolve3();
40034
+ resolve4();
40030
40035
  });
40031
40036
  });
40032
40037
  }
@@ -40040,25 +40045,20 @@ class ClaudeCli extends EventEmitter2 {
40040
40045
  return true;
40041
40046
  }
40042
40047
  getMcpServerPath() {
40043
- const __filename2 = fileURLToPath2(import.meta.url);
40044
- const __dirname3 = dirname3(__filename2);
40045
- return resolve2(__dirname3, "..", "mcp", "permission-server.js");
40048
+ const __filename2 = fileURLToPath3(import.meta.url);
40049
+ const __dirname4 = dirname4(__filename2);
40050
+ return resolve3(__dirname4, "..", "mcp", "permission-server.js");
40046
40051
  }
40047
40052
  getStatusLineWriterPath() {
40048
- const __filename2 = fileURLToPath2(import.meta.url);
40049
- const __dirname3 = dirname3(__filename2);
40050
- return resolve2(__dirname3, "..", "statusline", "writer.js");
40053
+ const __filename2 = fileURLToPath3(import.meta.url);
40054
+ const __dirname4 = dirname4(__filename2);
40055
+ return resolve3(__dirname4, "..", "statusline", "writer.js");
40051
40056
  }
40052
40057
  }
40053
40058
 
40054
40059
  // src/update-notifier.ts
40055
40060
  var import_semver2 = __toESM(require_semver2(), 1);
40056
40061
 
40057
- // src/changelog.ts
40058
- import { dirname as dirname4, resolve as resolve3 } from "path";
40059
- import { fileURLToPath as fileURLToPath3 } from "url";
40060
- var __dirname3 = dirname4(fileURLToPath3(import.meta.url));
40061
-
40062
40062
  // src/operations/commands/handler.ts
40063
40063
  init_emoji();
40064
40064
 
@@ -40394,14 +40394,16 @@ ${code}
40394
40394
  return text.replace(/([*_`[\]()#+\-.!])/g, "\\$1");
40395
40395
  }
40396
40396
  formatTable(headers, rows) {
40397
- const headerRow = `| ${headers.join(" | ")} |`;
40397
+ const escapeCell = (cell) => cell.replace(/\|/g, "\\|");
40398
+ const headerRow = `| ${headers.map(escapeCell).join(" | ")} |`;
40398
40399
  const separatorRow = `| ${headers.map(() => "---").join(" | ")} |`;
40399
- const dataRows = rows.map((row) => `| ${row.join(" | ")} |`);
40400
+ const dataRows = rows.map((row) => `| ${row.map(escapeCell).join(" | ")} |`);
40400
40401
  return [headerRow, separatorRow, ...dataRows].join(`
40401
40402
  `);
40402
40403
  }
40403
40404
  formatKeyValueList(items) {
40404
- const rows = items.map(([icon, label, value]) => `| ${icon} ${this.formatBold(label)} | ${value} |`);
40405
+ const escapeCell = (cell) => cell.replace(/\|/g, "\\|");
40406
+ const rows = items.map(([icon, label, value]) => `| ${icon} ${this.formatBold(escapeCell(label))} | ${escapeCell(value)} |`);
40405
40407
  return ["| | |", "|---|---|", ...rows].join(`
40406
40408
  `);
40407
40409
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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",