mattermost-claude-code 0.5.1 → 0.5.3

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
@@ -3,7 +3,17 @@
3
3
  [![npm version](https://img.shields.io/npm/v/mattermost-claude-code.svg)](https://www.npmjs.com/package/mattermost-claude-code)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Share your Claude Code sessions live in a public Mattermost channel. Your colleagues can watch you work with Claude Code in real-time, and authorized users can even trigger sessions from Mattermost.
6
+ Share Claude Code sessions live in a Mattermost channel. Your colleagues can watch you work with Claude in real-time, collaborate on sessions, and even trigger their own sessions from Mattermost.
7
+
8
+ ## Features
9
+
10
+ - **Real-time streaming** - Claude's responses stream live to Mattermost
11
+ - **Multiple concurrent sessions** - Each thread gets its own Claude session
12
+ - **Session collaboration** - Invite others to participate in your session
13
+ - **Interactive permissions** - Approve Claude's actions via emoji reactions
14
+ - **Plan approval** - Review and approve Claude's plans before execution
15
+ - **Task tracking** - Live todo list updates as Claude works
16
+ - **Code diffs** - See exactly what Claude is changing
7
17
 
8
18
  ## How it works
9
19
 
@@ -11,159 +21,252 @@ Share your Claude Code sessions live in a public Mattermost channel. Your collea
11
21
  ┌─────────────────────────────────────────────────────────────────┐
12
22
  │ Your Local Machine │
13
23
  │ ┌─────────────────┐ ┌─────────────────────────────┐ │
14
- │ │ Claude Code CLI │◄───────►│ This service │ │
15
- │ │ (subprocess) │ stdio │ (Node.js) │ │
24
+ │ │ Claude Code CLI │◀───────▶│ mm-claude │ │
25
+ │ │ (subprocess) │ stdio │ (this service) │ │
16
26
  │ └─────────────────┘ └──────────┬──────────────────┘ │
17
27
  └─────────────────────────────────────────┼───────────────────────┘
18
28
  │ WebSocket + REST API
19
- ▼ (outbound only!)
29
+ ▼ (outbound only)
20
30
  ┌─────────────────────────────────────────────────────────────────┐
21
31
  │ Mattermost Server │
22
32
  │ ┌─────────────────┐ ┌─────────────────────────────┐ │
23
- │ │ Bot Account │ │ Public Channel │ │
24
- │ │ @claude-code │◄───────►│ #claude-code-sessions │ │
33
+ │ │ Bot Account │◀───────▶│ Channel │ │
34
+ │ │ @claude-code │ │ #claude-sessions │ │
25
35
  │ └─────────────────┘ └─────────────────────────────┘ │
26
36
  └─────────────────────────────────────────────────────────────────┘
27
37
  ```
28
38
 
29
- This runs entirely on your local machine - it only makes **outbound** connections to Mattermost. No port forwarding or public IP needed!
39
+ Runs entirely on your machine - only **outbound** connections to Mattermost. No port forwarding needed!
30
40
 
31
41
  ## Prerequisites
32
42
 
33
43
  1. **Claude Code CLI** installed and authenticated (`claude --version`)
34
44
  2. **Node.js 18+**
35
- 3. **Mattermost bot account** with personal access token (ask your admin)
45
+ 3. **Mattermost bot account** with a personal access token
36
46
 
37
- ## Installation
47
+ ## Quick Start
48
+
49
+ ### 1. Install
38
50
 
39
- ### Option 1: npm (recommended)
40
51
  ```bash
41
52
  npm install -g mattermost-claude-code
42
53
  ```
43
54
 
44
- ### Option 2: From source
55
+ ### 2. Run
56
+
45
57
  ```bash
46
- git clone https://github.com/anneschuth/mattermost-claude-code.git
47
- cd mattermost-claude-code
48
- npm install
49
- npm run build
50
- npm link
58
+ cd /your/project
59
+ mm-claude
51
60
  ```
52
61
 
53
- ## Configuration
62
+ On first run, an interactive setup wizard guides you through configuration:
54
63
 
55
- Create a config file at `~/.config/mm-claude/.env`:
64
+ ```
65
+ Welcome to mm-claude!
56
66
 
57
- ```bash
58
- mkdir -p ~/.config/mm-claude
59
- cp .env.example ~/.config/mm-claude/.env
67
+ No configuration found. Let's set things up.
68
+
69
+ You'll need:
70
+ • A Mattermost bot account with a token
71
+ • A channel ID where the bot will listen
72
+
73
+ ? Mattermost URL: https://your-mattermost.com
74
+ ? Bot token: ********
75
+ ? Channel ID: abc123def456
76
+ ? Bot mention name: claude-code
77
+ ? Allowed usernames: alice,bob
78
+ ? Skip permission prompts? No
79
+
80
+ ✓ Configuration saved!
81
+ ~/.config/mm-claude/.env
82
+
83
+ Starting mm-claude...
60
84
  ```
61
85
 
62
- Edit the config with your Mattermost details:
63
- ```env
64
- MATTERMOST_URL=https://your-mattermost.com
65
- MATTERMOST_TOKEN=your-bot-token
66
- MATTERMOST_CHANNEL_ID=your-channel-id
67
- MATTERMOST_BOT_NAME=claude-code
86
+ ### 3. Use
68
87
 
69
- ALLOWED_USERS=anne.schuth,colleague1
88
+ In Mattermost, mention the bot:
70
89
 
71
- DEFAULT_WORKING_DIR=/path/to/your/project
72
- ```
90
+ ```
91
+ @claude-code help me fix the bug in src/auth.ts
92
+ ```
73
93
 
74
- ## Running
94
+ ## CLI Options
75
95
 
76
- Navigate to your project directory and run:
77
96
  ```bash
78
- cd /your/project
79
- mm-claude
97
+ mm-claude [options]
98
+
99
+ Options:
100
+ --url <url> Mattermost server URL
101
+ --token <token> Bot token
102
+ --channel <id> Channel ID
103
+ --bot-name <name> Bot mention name (default: claude-code)
104
+ --allowed-users <list> Comma-separated allowed usernames
105
+ --skip-permissions Skip permission prompts (auto-approve)
106
+ --no-skip-permissions Enable permission prompts (override env)
107
+ --debug Enable debug logging
108
+ --version Show version
109
+ --help Show help
80
110
  ```
81
111
 
82
- With debug output:
83
- ```bash
84
- mm-claude --debug
112
+ CLI options override environment variables.
113
+
114
+ ## Session Collaboration
115
+
116
+ ### Invite Users
117
+
118
+ Session owners can temporarily allow others to participate:
119
+
120
+ ```
121
+ /invite @colleague
85
122
  ```
86
123
 
87
- ## Usage
124
+ The colleague can now send messages in this session thread.
88
125
 
89
- In your Mattermost channel, mention the bot to start a session:
126
+ ### Kick Users
127
+
128
+ Remove an invited user from the session:
90
129
 
91
130
  ```
92
- @claude-code help me fix the bug in src/auth.ts
131
+ /kick @colleague
93
132
  ```
94
133
 
95
- The bot will:
96
- 1. Post a session start message
97
- 2. Stream Claude Code's responses in real-time
98
- 3. Show tool activity (file reads, edits, bash commands)
99
- 4. Post a session end message when complete
134
+ ### Message Approval
135
+
136
+ When an unauthorized user sends a message in a session thread, the owner sees an approval prompt:
137
+
138
+ ```
139
+ 🔒 @unauthorized-user wants to send a message:
140
+ > Can you also add error handling?
141
+
142
+ React 👍 to allow this message, ✅ to invite them to the session, 👎 to deny
143
+ ```
144
+
145
+ ### Side Conversations
146
+
147
+ Messages starting with `@someone-else` are ignored by the bot, allowing side conversations in the thread without triggering Claude.
148
+
149
+ ### Downgrade Permissions
150
+
151
+ If the bot is running with `--skip-permissions` (auto mode), you can enable interactive permissions for a specific session:
152
+
153
+ ```
154
+ /permissions interactive
155
+ ```
156
+
157
+ This allows collaboration by requiring approval for Claude's actions. Note: you can only downgrade (auto → interactive), not upgrade - this ensures security.
100
158
 
101
159
  ## Interactive Features
102
160
 
103
- ### Typing Indicator
104
- While Claude is thinking or working, you'll see the "is typing..." indicator in Mattermost.
105
-
106
- ### Plan Mode Approval
107
- When Claude enters plan mode and is ready to implement:
108
- - Bot posts an approval message with 👍/👎 reactions
109
- - React with 👍 to approve and start building
110
- - React with 👎 to request changes
111
- - Once approved, subsequent plan exits auto-continue
112
-
113
- ### Questions with Emoji Reactions
114
- When Claude needs to ask questions:
115
- - Questions are posted one at a time (sequential flow)
116
- - Each question shows numbered options: 1️⃣ 2️⃣ 3️⃣ 4️⃣
117
- - React with the corresponding emoji to answer
118
- - After all questions are answered, Claude continues
119
-
120
- ### Task List Display
121
- When Claude creates a todo list (TodoWrite):
122
- - Tasks are shown with status icons: ⬜ pending, 🔄 in progress, ✅ completed
123
- - The task list updates in place as Claude works
124
- - In-progress tasks show the active description
125
-
126
- ### Subagent Status
127
- When Claude spawns subagents (Task tool):
128
- - Shows subagent type and description
129
- - Updates to ✅ completed when done
130
-
131
- ### Permission Approval via Reactions
132
- By default, Claude Code requests permission before executing tools. This service forwards these requests to Mattermost:
133
- - Permission requests are posted with 👍/✅/👎 reactions
134
- - 👍 **Allow this** - approve this specific tool use
135
- - ✅ **Allow all** - approve all future tool uses in this session
136
- - 👎 **Deny** - reject this tool use
137
-
138
- To skip permission prompts (use with caution):
139
- ```bash
140
- mm-claude --dangerously-skip-permissions
141
- # or set in .env:
142
- SKIP_PERMISSIONS=true
161
+ ### Permission Approval
162
+
163
+ When Claude wants to execute a tool (edit file, run command, etc.):
164
+
165
+ - **👍 Allow** - Approve this specific action
166
+ - **✅ Allow all** - Approve all future actions this session
167
+ - **👎 Deny** - Reject this action
168
+
169
+ To skip prompts: `mm-claude --skip-permissions` or set `SKIP_PERMISSIONS=true`
170
+
171
+ ### Plan Mode
172
+
173
+ When Claude creates a plan and is ready to implement:
174
+
175
+ - **👍** Approve and start building
176
+ - **👎** Request changes
177
+
178
+ Once approved, subsequent plans auto-continue.
179
+
180
+ ### Questions
181
+
182
+ When Claude asks questions with multiple choice options:
183
+
184
+ - React with 1️⃣ 2️⃣ 3️⃣ or 4️⃣ to answer
185
+ - Questions are asked one at a time
186
+
187
+ ### Task List
188
+
189
+ Claude's todo list shows live in Mattermost:
190
+
191
+ - Pending
192
+ - 🔄 In progress
193
+ - ✅ Completed
194
+
195
+ ### Session Header
196
+
197
+ The session start message shows current status and updates when participants change:
198
+
199
+ ```
200
+ 🤖 mm-claude v0.5.1
201
+
202
+ | | |
203
+ |:--|:--|
204
+ | 📂 Directory | ~/project |
205
+ | 👤 Started by | @alice |
206
+ | 👥 Participants | @bob, @carol |
207
+ | 🔢 Session | #1 of 5 max |
208
+ | 🔐 Permissions | Interactive |
143
209
  ```
144
210
 
145
- ### Code Diffs and Previews
146
- - **Edit**: Shows actual diff with `-` old lines and `+` new lines
147
- - **Write**: Shows first 6 lines of content with line count
148
- - **Bash**: Shows the command being executed
149
- - **Read**: Shows the file path being read
150
- - **MCP tools**: Shows tool name and server (e.g., `🔌 get-library-docs *(context7)*`)
211
+ ### Cancel Session
151
212
 
152
- ## Access Control
213
+ Stop a running session:
153
214
 
154
- - **ALLOWED_USERS**: Comma-separated list of Mattermost usernames that can trigger Claude Code
155
- - If empty, anyone in the channel can use the bot (be careful!)
156
- - Non-authorized users get a polite rejection message
215
+ - Type `/stop` or `/cancel` in the thread
216
+ - React with or 🛑 to any message in the thread
157
217
 
158
- ## Message to your Mattermost admin
218
+ ## Access Control
159
219
 
160
- > "Kun je een bot account voor me aanmaken om Claude Code sessies te delen in een publiek kanaal?
161
- > Ik heb nodig: een bot account met posting rechten, een personal access token, en de bot toegevoegd aan [kanaal naam]."
220
+ Set `ALLOWED_USERS` to restrict who can use the bot:
162
221
 
163
- Or in English:
222
+ ```env
223
+ ALLOWED_USERS=alice,bob,carol
224
+ ```
164
225
 
165
- > "Could you create a bot account for me to share Claude Code sessions in a public channel?
166
- > I need: bot account with posting permissions, a personal access token, and the bot added to [channel name]."
226
+ - Only listed users can start sessions
227
+ - Only listed users can approve permissions
228
+ - Session owners can `/invite` others temporarily
229
+ - Empty = anyone can use (be careful!)
230
+
231
+ ## Environment Variables
232
+
233
+ | Variable | Description |
234
+ |----------|-------------|
235
+ | `MATTERMOST_URL` | Server URL |
236
+ | `MATTERMOST_TOKEN` | Bot token |
237
+ | `MATTERMOST_CHANNEL_ID` | Channel to listen in |
238
+ | `MATTERMOST_BOT_NAME` | Mention name (default: `claude-code`) |
239
+ | `ALLOWED_USERS` | Comma-separated usernames |
240
+ | `SKIP_PERMISSIONS` | `true` to auto-approve actions |
241
+ | `MAX_SESSIONS` | Max concurrent sessions (default: `5`) |
242
+ | `SESSION_TIMEOUT_MS` | Idle timeout in ms (default: `1800000` = 30 min) |
243
+
244
+ Config file locations (in priority order):
245
+ 1. `./.env` (current directory)
246
+ 2. `~/.config/mm-claude/.env`
247
+ 3. `~/.mm-claude.env`
248
+
249
+ ## Code Display
250
+
251
+ - **Edit**: Shows diff with `-` removed and `+` added lines
252
+ - **Write**: Shows preview of new file content
253
+ - **Bash**: Shows command being executed
254
+ - **Read**: Shows file path being read
255
+ - **MCP tools**: Shows tool name and server
256
+
257
+ ## For Mattermost Admins
258
+
259
+ To set up a bot account:
260
+
261
+ 1. Go to **Integrations > Bot Accounts > Add Bot Account**
262
+ 2. Give it a username (e.g., `claude-code`) and display name
263
+ 3. Create a **Personal Access Token** for the bot
264
+ 4. Add the bot to the channel where it should listen
265
+
266
+ The bot needs permissions to:
267
+ - Post messages
268
+ - Add reactions
269
+ - Read channel messages
167
270
 
168
271
  ## License
169
272
 
@@ -45,6 +45,7 @@ interface Session {
45
45
  pendingMessageApproval: PendingMessageApproval | null;
46
46
  planApproved: boolean;
47
47
  sessionAllowedUsers: Set<string>;
48
+ forceInteractivePermissions: boolean;
48
49
  sessionStartPostId: string | null;
49
50
  tasksPostId: string | null;
50
51
  activeSubagents: Map<string, string>;
@@ -111,6 +112,13 @@ export declare class SessionManager {
111
112
  inviteUser(threadId: string, invitedUser: string, invitedBy: string): Promise<void>;
112
113
  /** Kick a user from a specific session */
113
114
  kickUser(threadId: string, kickedUser: string, kickedBy: string): Promise<void>;
115
+ /**
116
+ * Enable interactive permissions for a session.
117
+ * Can only downgrade (skip → interactive), not upgrade.
118
+ */
119
+ enableInteractivePermissions(threadId: string, username: string): Promise<void>;
120
+ /** Check if a session should use interactive permissions */
121
+ isSessionInteractive(threadId: string): boolean;
114
122
  /** Update the session header post with current participants */
115
123
  private updateSessionHeader;
116
124
  /** Request approval for a message from an unauthorized user */
@@ -121,6 +121,7 @@ export class SessionManager {
121
121
  pendingMessageApproval: null,
122
122
  planApproved: false,
123
123
  sessionAllowedUsers: new Set([username]), // Owner is always allowed
124
+ forceInteractivePermissions: false, // Can be enabled via /permissions interactive
124
125
  sessionStartPostId: post.id, // Track for updating participants
125
126
  tasksPostId: null,
126
127
  activeSubagents: new Map(),
@@ -813,12 +814,53 @@ export class SessionManager {
813
814
  await this.mattermost.createPost(`⚠️ @${kickedUser} was not in this session`, threadId);
814
815
  }
815
816
  }
817
+ /**
818
+ * Enable interactive permissions for a session.
819
+ * Can only downgrade (skip → interactive), not upgrade.
820
+ */
821
+ async enableInteractivePermissions(threadId, username) {
822
+ const session = this.sessions.get(threadId);
823
+ if (!session)
824
+ return;
825
+ // Only session owner or globally allowed users can change permissions
826
+ if (session.startedBy !== username && !this.mattermost.isUserAllowed(username)) {
827
+ await this.mattermost.createPost(`⚠️ Only @${session.startedBy} or allowed users can change permissions`, threadId);
828
+ return;
829
+ }
830
+ // Can only downgrade, not upgrade
831
+ if (!this.skipPermissions) {
832
+ await this.mattermost.createPost(`ℹ️ Permissions are already interactive for this session`, threadId);
833
+ return;
834
+ }
835
+ // Already enabled for this session
836
+ if (session.forceInteractivePermissions) {
837
+ await this.mattermost.createPost(`ℹ️ Interactive permissions already enabled for this session`, threadId);
838
+ return;
839
+ }
840
+ session.forceInteractivePermissions = true;
841
+ await this.mattermost.createPost(`🔐 Interactive permissions enabled for this session by @${username}`, threadId);
842
+ console.log(` 🔐 Interactive permissions enabled for session by @${username}`);
843
+ await this.updateSessionHeader(session);
844
+ }
845
+ /** Check if a session should use interactive permissions */
846
+ isSessionInteractive(threadId) {
847
+ const session = this.sessions.get(threadId);
848
+ if (!session)
849
+ return !this.skipPermissions;
850
+ // If global is interactive, always interactive
851
+ if (!this.skipPermissions)
852
+ return true;
853
+ // If session has forced interactive, use that
854
+ return session.forceInteractivePermissions;
855
+ }
816
856
  /** Update the session header post with current participants */
817
857
  async updateSessionHeader(session) {
818
858
  if (!session.sessionStartPostId)
819
859
  return;
820
860
  const shortDir = this.workingDir.replace(process.env.HOME || '', '~');
821
- const permMode = this.skipPermissions ? '⚡ Auto' : '🔐 Interactive';
861
+ // Check session-level permission override
862
+ const isInteractive = !this.skipPermissions || session.forceInteractivePermissions;
863
+ const permMode = isInteractive ? '🔐 Interactive' : '⚡ Auto';
822
864
  // Build participants list (excluding owner who is shown in "Started by")
823
865
  const otherParticipants = [...session.sessionAllowedUsers]
824
866
  .filter(u => u !== session.startedBy)
package/dist/index.js CHANGED
@@ -102,6 +102,19 @@ async function main() {
102
102
  await session.kickUser(threadRoot, kickMatch[1], username);
103
103
  return;
104
104
  }
105
+ // Check for /permissions command
106
+ const permMatch = content.match(/^\/permissions?\s+(interactive|auto)/i);
107
+ if (permMatch) {
108
+ const mode = permMatch[1].toLowerCase();
109
+ if (mode === 'interactive') {
110
+ await session.enableInteractivePermissions(threadRoot, username);
111
+ }
112
+ else {
113
+ // Can't upgrade to auto - that would be less secure
114
+ await mattermost.createPost(`⚠️ Cannot upgrade to auto permissions - can only downgrade to interactive`, threadRoot);
115
+ }
116
+ return;
117
+ }
105
118
  // Check if user is allowed in this session
106
119
  if (!session.isUserAllowedInSession(threadRoot, username)) {
107
120
  // Request approval for their message
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mattermost-claude-code",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
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",