mattermost-claude-code 0.7.0 → 0.7.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.
@@ -53,6 +53,7 @@ interface Session {
53
53
  updateTimer: ReturnType<typeof setTimeout> | null;
54
54
  typingTimer: ReturnType<typeof setInterval> | null;
55
55
  timeoutWarningPosted: boolean;
56
+ isRestarting: boolean;
56
57
  }
57
58
  export declare class SessionManager {
58
59
  private mattermost;
@@ -131,6 +131,7 @@ export class SessionManager {
131
131
  updateTimer: null,
132
132
  typingTimer: null,
133
133
  timeoutWarningPosted: false,
134
+ isRestarting: false,
134
135
  };
135
136
  // Register session
136
137
  this.sessions.set(actualThreadId, session);
@@ -220,14 +221,11 @@ export class SessionManager {
220
221
  }
221
222
  }
222
223
  async handleExitPlanMode(session) {
223
- // If already approved in this session, auto-continue
224
+ // If already approved in this session, silently ignore subsequent ExitPlanMode calls
225
+ // (Don't send another message - that causes Claude to loop)
224
226
  if (session.planApproved) {
225
227
  if (this.debug)
226
- console.log(' ↪ Plan already approved, auto-continuing');
227
- if (session.claude.isRunning()) {
228
- session.claude.sendMessage('Continue with the implementation.');
229
- this.startTyping(session);
230
- }
228
+ console.log(' ↪ Plan already approved, ignoring ExitPlanMode');
231
229
  return;
232
230
  }
233
231
  // If we already have a pending approval, don't post another one
@@ -713,6 +711,10 @@ export class SessionManager {
713
711
  const session = this.sessions.get(threadId);
714
712
  if (!session)
715
713
  return;
714
+ // If we're intentionally restarting (e.g., !cd), don't clean up or post exit message
715
+ if (session.isRestarting) {
716
+ return;
717
+ }
716
718
  this.stopTyping(session);
717
719
  if (session.updateTimer) {
718
720
  clearTimeout(session.updateTimer);
@@ -813,6 +815,7 @@ export class SessionManager {
813
815
  console.log(` 📂 Session (${shortId}…) changing directory to ${shortDir}`);
814
816
  // Stop the current Claude CLI
815
817
  this.stopTyping(session);
818
+ session.isRestarting = true; // Suppress exit message during restart
816
819
  session.claude.kill();
817
820
  // Flush any pending content
818
821
  await this.flush(session);
@@ -833,8 +836,10 @@ export class SessionManager {
833
836
  // Start the new Claude CLI
834
837
  try {
835
838
  session.claude.start();
839
+ session.isRestarting = false; // Successfully restarted
836
840
  }
837
841
  catch (err) {
842
+ session.isRestarting = false; // Reset flag even on failure
838
843
  console.error(' ❌ Failed to restart Claude:', err);
839
844
  await this.mattermost.createPost(`❌ Failed to restart Claude: ${err}`, threadId);
840
845
  return;
@@ -29,6 +29,7 @@ export declare class MattermostClient extends EventEmitter {
29
29
  createPost(message: string, threadId?: string): Promise<MattermostPost>;
30
30
  updatePost(postId: string, message: string): Promise<MattermostPost>;
31
31
  addReaction(postId: string, emojiName: string): Promise<void>;
32
+ downloadFile(fileId: string): Promise<Buffer>;
32
33
  connect(): Promise<void>;
33
34
  private handleEvent;
34
35
  private scheduleReconnect;
@@ -88,6 +88,20 @@ export class MattermostClient extends EventEmitter {
88
88
  emoji_name: emojiName,
89
89
  });
90
90
  }
91
+ // Download a file attachment
92
+ async downloadFile(fileId) {
93
+ const url = `${this.config.mattermost.url}/api/v4/files/${fileId}`;
94
+ const response = await fetch(url, {
95
+ headers: {
96
+ Authorization: `Bearer ${this.config.mattermost.token}`,
97
+ },
98
+ });
99
+ if (!response.ok) {
100
+ throw new Error(`Failed to download file ${fileId}: ${response.status}`);
101
+ }
102
+ const arrayBuffer = await response.arrayBuffer();
103
+ return Buffer.from(arrayBuffer);
104
+ }
91
105
  // Connect to WebSocket
92
106
  async connect() {
93
107
  // Get bot user first
@@ -8,6 +8,15 @@ export interface MattermostWebSocketEvent {
8
8
  };
9
9
  seq: number;
10
10
  }
11
+ export interface MattermostFile {
12
+ id: string;
13
+ name: string;
14
+ size: number;
15
+ mime_type: string;
16
+ extension: string;
17
+ width?: number;
18
+ height?: number;
19
+ }
11
20
  export interface MattermostPost {
12
21
  id: string;
13
22
  create_at: number;
@@ -21,7 +30,7 @@ export interface MattermostPost {
21
30
  props: Record<string, unknown>;
22
31
  metadata?: {
23
32
  embeds?: unknown[];
24
- files?: unknown[];
33
+ files?: MattermostFile[];
25
34
  };
26
35
  }
27
36
  export interface MattermostUser {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mattermost-claude-code",
3
- "version": "0.7.0",
3
+ "version": "0.7.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",