mattermost-claude-code 0.5.9 → 0.6.1

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/README.md CHANGED
@@ -252,6 +252,7 @@ ALLOWED_USERS=alice,bob,carol
252
252
  | `SKIP_PERMISSIONS` | `true` to auto-approve actions |
253
253
  | `MAX_SESSIONS` | Max concurrent sessions (default: `5`) |
254
254
  | `SESSION_TIMEOUT_MS` | Idle timeout in ms (default: `1800000` = 30 min) |
255
+ | `NO_UPDATE_NOTIFIER` | Set to `1` to disable update checks |
255
256
 
256
257
  Config file locations (in priority order):
257
258
  1. `./.env` (current directory)
@@ -266,6 +267,21 @@ Config file locations (in priority order):
266
267
  - **Read**: Shows file path being read
267
268
  - **MCP tools**: Shows tool name and server
268
269
 
270
+ ## Auto-Updates
271
+
272
+ mm-claude checks for updates every 30 minutes and notifies you when a new version is available:
273
+
274
+ - **CLI**: Shows a notification box on startup
275
+ - **Mattermost**: Shows a warning in session headers
276
+
277
+ To update:
278
+
279
+ ```bash
280
+ npm install -g mattermost-claude-code
281
+ ```
282
+
283
+ To disable update checks, set `NO_UPDATE_NOTIFIER=1`.
284
+
269
285
  ## For Mattermost Admins
270
286
 
271
287
  To set up a bot account:
@@ -1,4 +1,5 @@
1
1
  import { ClaudeCli } from './cli.js';
2
+ import { getUpdateInfo } from '../update-notifier.js';
2
3
  import { readFileSync } from 'fs';
3
4
  import { dirname, resolve } from 'path';
4
5
  import { fileURLToPath } from 'url';
@@ -214,13 +215,14 @@ export class SessionManager {
214
215
  session.activeSubagents.delete(toolUseId);
215
216
  }
216
217
  catch (err) {
217
- console.error('[Session] Failed to update subagent completion:', err);
218
+ console.error(' ⚠️ Failed to update subagent completion:', err);
218
219
  }
219
220
  }
220
221
  async handleExitPlanMode(session) {
221
222
  // If already approved in this session, auto-continue
222
223
  if (session.planApproved) {
223
- console.log('[Session] Plan already approved, auto-continuing...');
224
+ if (this.debug)
225
+ console.log(' ↪ Plan already approved, auto-continuing');
224
226
  if (session.claude.isRunning()) {
225
227
  session.claude.sendMessage('Continue with the implementation.');
226
228
  this.startTyping(session);
@@ -229,7 +231,8 @@ export class SessionManager {
229
231
  }
230
232
  // If we already have a pending approval, don't post another one
231
233
  if (session.pendingApproval && session.pendingApproval.type === 'plan') {
232
- console.log('[Session] Plan approval already pending, waiting...');
234
+ if (this.debug)
235
+ console.log(' ↪ Plan approval already pending, waiting');
233
236
  return;
234
237
  }
235
238
  // Flush any pending content first
@@ -250,7 +253,7 @@ export class SessionManager {
250
253
  await this.mattermost.addReaction(post.id, '-1');
251
254
  }
252
255
  catch (err) {
253
- console.error('[Session] Failed to add approval reactions:', err);
256
+ console.error(' ⚠️ Failed to add approval reactions:', err);
254
257
  }
255
258
  // Track this for reaction handling
256
259
  session.pendingApproval = { postId: post.id, type: 'plan' };
@@ -266,7 +269,7 @@ export class SessionManager {
266
269
  await this.mattermost.updatePost(session.tasksPostId, '📋 ~~Tasks~~ *(completed)*');
267
270
  }
268
271
  catch (err) {
269
- console.error('[Session] Failed to update tasks:', err);
272
+ console.error(' ⚠️ Failed to update tasks:', err);
270
273
  }
271
274
  }
272
275
  return;
@@ -302,7 +305,7 @@ export class SessionManager {
302
305
  }
303
306
  }
304
307
  catch (err) {
305
- console.error('[Session] Failed to update tasks:', err);
308
+ console.error(' ⚠️ Failed to update tasks:', err);
306
309
  }
307
310
  }
308
311
  async handleTaskStart(session, toolUseId, input) {
@@ -317,13 +320,14 @@ export class SessionManager {
317
320
  session.activeSubagents.set(toolUseId, post.id);
318
321
  }
319
322
  catch (err) {
320
- console.error('[Session] Failed to post subagent status:', err);
323
+ console.error(' ⚠️ Failed to post subagent status:', err);
321
324
  }
322
325
  }
323
326
  async handleAskUserQuestion(session, toolUseId, input) {
324
327
  // If we already have pending questions, don't start another set
325
328
  if (session.pendingQuestionSet) {
326
- console.log('[Session] Questions already pending, waiting...');
329
+ if (this.debug)
330
+ console.log(' ↪ Questions already pending, waiting');
327
331
  return;
328
332
  }
329
333
  // Flush any pending content first
@@ -380,7 +384,7 @@ export class SessionManager {
380
384
  await this.mattermost.addReaction(post.id, REACTION_EMOJIS[i]);
381
385
  }
382
386
  catch (err) {
383
- console.error(`[Session] Failed to add reaction ${REACTION_EMOJIS[i]}:`, err);
387
+ console.error(` ⚠️ Failed to add reaction ${REACTION_EMOJIS[i]}:`, err);
384
388
  }
385
389
  }
386
390
  }
@@ -428,13 +432,14 @@ export class SessionManager {
428
432
  return;
429
433
  const selectedOption = question.options[optionIndex];
430
434
  question.answer = selectedOption.label;
431
- console.log(`[Session] User ${username} answered "${question.header}": ${selectedOption.label}`);
435
+ if (this.debug)
436
+ console.log(` 💬 @${username} answered "${question.header}": ${selectedOption.label}`);
432
437
  // Update the post to show answer
433
438
  try {
434
439
  await this.mattermost.updatePost(postId, `✅ **${question.header}**: ${selectedOption.label}`);
435
440
  }
436
441
  catch (err) {
437
- console.error('[Session] Failed to update answered question:', err);
442
+ console.error(' ⚠️ Failed to update answered question:', err);
438
443
  }
439
444
  // Move to next question or finish
440
445
  session.pendingQuestionSet.currentIndex++;
@@ -448,7 +453,8 @@ export class SessionManager {
448
453
  for (const q of questions) {
449
454
  answersText += `- **${q.header}**: ${q.answer}\n`;
450
455
  }
451
- console.log(`[Session] All questions answered, sending as message:`, answersText);
456
+ if (this.debug)
457
+ console.log(' ✅ All questions answered');
452
458
  // Clear and send as regular message
453
459
  session.pendingQuestionSet = null;
454
460
  if (session.claude.isRunning()) {
@@ -465,7 +471,8 @@ export class SessionManager {
465
471
  if (!isApprove && !isReject)
466
472
  return;
467
473
  const postId = session.pendingApproval.postId;
468
- console.log(`[Session] User ${username} ${isApprove ? 'approved' : 'rejected'} the plan`);
474
+ const shortId = session.threadId.substring(0, 8);
475
+ console.log(` ${isApprove ? '✅' : '❌'} Plan ${isApprove ? 'approved' : 'rejected'} (${shortId}…) by @${username}`);
469
476
  // Update the post to show the decision
470
477
  try {
471
478
  const statusMessage = isApprove
@@ -474,7 +481,7 @@ export class SessionManager {
474
481
  await this.mattermost.updatePost(postId, statusMessage);
475
482
  }
476
483
  catch (err) {
477
- console.error('[Session] Failed to update approval post:', err);
484
+ console.error(' ⚠️ Failed to update approval post:', err);
478
485
  }
479
486
  // Clear pending approval and mark as approved
480
487
  session.pendingApproval = null;
@@ -878,9 +885,14 @@ export class SessionManager {
878
885
  }
879
886
  rows.push(`| 🔢 **Session** | #${session.sessionNumber} of ${MAX_SESSIONS} max |`);
880
887
  rows.push(`| ${permMode.split(' ')[0]} **Permissions** | ${permMode.split(' ')[1]} |`);
888
+ // Check for available updates
889
+ const updateInfo = getUpdateInfo();
890
+ const updateNotice = updateInfo
891
+ ? `\n> ⚠️ **Update available:** v${updateInfo.current} → v${updateInfo.latest} - Run \`npm install -g mattermost-claude-code\`\n`
892
+ : '';
881
893
  const msg = [
882
894
  `### 🤖 mm-claude \`v${pkg.version}\``,
883
- ``,
895
+ updateNotice,
884
896
  `| | |`,
885
897
  `|:--|:--|`,
886
898
  ...rows,
@@ -889,7 +901,7 @@ export class SessionManager {
889
901
  await this.mattermost.updatePost(session.sessionStartPostId, msg);
890
902
  }
891
903
  catch (err) {
892
- console.error('[Session] Failed to update session header:', err);
904
+ console.error(' ⚠️ Failed to update session header:', err);
893
905
  }
894
906
  }
895
907
  /** Request approval for a message from an unauthorized user */
@@ -917,7 +929,7 @@ export class SessionManager {
917
929
  await this.mattermost.addReaction(post.id, '-1');
918
930
  }
919
931
  catch (err) {
920
- console.error('[Session] Failed to add message approval reactions:', err);
932
+ console.error(' ⚠️ Failed to add message approval reactions:', err);
921
933
  }
922
934
  }
923
935
  /** Kill all active sessions (for graceful shutdown) */
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { SessionManager } from './claude/session.js';
7
7
  import { readFileSync } from 'fs';
8
8
  import { dirname, resolve } from 'path';
9
9
  import { fileURLToPath } from 'url';
10
+ import { checkForUpdates } from './update-notifier.js';
10
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
11
12
  const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'));
12
13
  const dim = (s) => `\x1b[2m${s}\x1b[0m`;
@@ -32,6 +33,8 @@ function hasRequiredCliArgs(args) {
32
33
  return !!(args.url && args.token && args.channel);
33
34
  }
34
35
  async function main() {
36
+ // Check for updates (non-blocking, shows notification if available)
37
+ checkForUpdates();
35
38
  // Set debug mode from CLI flag
36
39
  if (opts.debug) {
37
40
  process.env.DEBUG = '1';
@@ -0,0 +1,3 @@
1
+ import { type UpdateInfo } from 'update-notifier';
2
+ export declare function checkForUpdates(): void;
3
+ export declare function getUpdateInfo(): UpdateInfo | undefined;
@@ -0,0 +1,31 @@
1
+ import updateNotifier from 'update-notifier';
2
+ import { readFileSync } from 'fs';
3
+ import { resolve } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
6
+ let cachedUpdateInfo;
7
+ export function checkForUpdates() {
8
+ if (process.env.NO_UPDATE_NOTIFIER)
9
+ return;
10
+ try {
11
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'));
12
+ const notifier = updateNotifier({
13
+ pkg,
14
+ updateCheckInterval: 1000 * 60 * 30, // Check every 30 minutes
15
+ });
16
+ // Cache for Mattermost notifications
17
+ cachedUpdateInfo = notifier.update;
18
+ // Show CLI notification
19
+ notifier.notify({
20
+ message: `Update available: {currentVersion} → {latestVersion}
21
+ Run: npm install -g mattermost-claude-code`,
22
+ });
23
+ }
24
+ catch {
25
+ // Silently fail - update checking is not critical
26
+ }
27
+ }
28
+ // Returns update info if available, for posting to Mattermost
29
+ export function getUpdateInfo() {
30
+ return cachedUpdateInfo;
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mattermost-claude-code",
3
- "version": "0.5.9",
3
+ "version": "0.6.1",
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",
@@ -44,12 +44,14 @@
44
44
  "commander": "^14.0.2",
45
45
  "dotenv": "^16.4.7",
46
46
  "prompts": "^2.4.2",
47
+ "update-notifier": "^7.3.1",
47
48
  "ws": "^8.18.0",
48
49
  "zod": "^4.2.1"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@types/node": "^22.10.2",
52
53
  "@types/prompts": "^2.4.9",
54
+ "@types/update-notifier": "^6.0.8",
53
55
  "@types/ws": "^8.5.13",
54
56
  "tsx": "^4.19.2",
55
57
  "typescript": "^5.7.2"