@shakudo/opencode-mattermost-control 0.3.45
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/.opencode/command/mattermost-connect.md +5 -0
- package/.opencode/command/mattermost-disconnect.md +5 -0
- package/.opencode/command/mattermost-monitor.md +12 -0
- package/.opencode/command/mattermost-status.md +5 -0
- package/.opencode/command/speckit.analyze.md +184 -0
- package/.opencode/command/speckit.checklist.md +294 -0
- package/.opencode/command/speckit.clarify.md +181 -0
- package/.opencode/command/speckit.constitution.md +82 -0
- package/.opencode/command/speckit.implement.md +135 -0
- package/.opencode/command/speckit.plan.md +89 -0
- package/.opencode/command/speckit.specify.md +258 -0
- package/.opencode/command/speckit.tasks.md +137 -0
- package/.opencode/command/speckit.taskstoissues.md +30 -0
- package/.opencode/plugin/mattermost-control/event-handlers/compaction.ts +61 -0
- package/.opencode/plugin/mattermost-control/event-handlers/file.ts +36 -0
- package/.opencode/plugin/mattermost-control/event-handlers/index.ts +14 -0
- package/.opencode/plugin/mattermost-control/event-handlers/message.ts +124 -0
- package/.opencode/plugin/mattermost-control/event-handlers/permission.ts +34 -0
- package/.opencode/plugin/mattermost-control/event-handlers/question.ts +92 -0
- package/.opencode/plugin/mattermost-control/event-handlers/session.ts +100 -0
- package/.opencode/plugin/mattermost-control/event-handlers/todo.ts +33 -0
- package/.opencode/plugin/mattermost-control/event-handlers/tool.ts +76 -0
- package/.opencode/plugin/mattermost-control/formatters.ts +202 -0
- package/.opencode/plugin/mattermost-control/index.ts +964 -0
- package/.opencode/plugin/mattermost-control/package.json +12 -0
- package/.opencode/plugin/mattermost-control/state.ts +180 -0
- package/.opencode/plugin/mattermost-control/timers.ts +96 -0
- package/.opencode/plugin/mattermost-control/tools/connect.ts +563 -0
- package/.opencode/plugin/mattermost-control/tools/file.ts +41 -0
- package/.opencode/plugin/mattermost-control/tools/index.ts +12 -0
- package/.opencode/plugin/mattermost-control/tools/monitor.ts +183 -0
- package/.opencode/plugin/mattermost-control/tools/schedule.ts +253 -0
- package/.opencode/plugin/mattermost-control/tools/session.ts +120 -0
- package/.opencode/plugin/mattermost-control/types.ts +107 -0
- package/LICENSE +21 -0
- package/README.md +1280 -0
- package/opencode-shared +359 -0
- package/opencode-shared-restart +495 -0
- package/opencode-shared-stop +90 -0
- package/package.json +65 -0
- package/src/clients/mattermost-client.ts +221 -0
- package/src/clients/websocket-client.ts +199 -0
- package/src/command-handler.ts +1035 -0
- package/src/config.ts +170 -0
- package/src/context-builder.ts +309 -0
- package/src/file-completion-handler.ts +521 -0
- package/src/file-handler.ts +242 -0
- package/src/guest-approval-handler.ts +223 -0
- package/src/logger.ts +73 -0
- package/src/merge-handler.ts +335 -0
- package/src/message-router.ts +151 -0
- package/src/models/index.ts +197 -0
- package/src/models/routing.ts +50 -0
- package/src/models/thread-mapping.ts +40 -0
- package/src/monitor-service.ts +222 -0
- package/src/notification-service.ts +118 -0
- package/src/opencode-session-registry.ts +370 -0
- package/src/persistence/team-store.ts +396 -0
- package/src/persistence/thread-mapping-store.ts +258 -0
- package/src/question-handler.ts +401 -0
- package/src/reaction-handler.ts +111 -0
- package/src/response-streamer.ts +364 -0
- package/src/scheduler/schedule-store.ts +261 -0
- package/src/scheduler/scheduler-service.ts +349 -0
- package/src/session-manager.ts +142 -0
- package/src/session-ownership-handler.ts +253 -0
- package/src/status-indicator.ts +279 -0
- package/src/thread-manager.ts +231 -0
- package/src/todo-manager.ts +162 -0
package/README.md
ADDED
|
@@ -0,0 +1,1280 @@
|
|
|
1
|
+
# OpenCode Mattermost Control Plugin
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.npmjs.com/package/opencode-mattermost-control)
|
|
5
|
+
|
|
6
|
+
Control [OpenCode](https://opencode.ai) remotely via Mattermost direct messages. Send prompts to your OpenCode session by messaging a bot user, and receive real-time streaming responses.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
### Core Features
|
|
11
|
+
- **Thread-Per-Session**: Each OpenCode session automatically gets its own dedicated Mattermost thread for clean conversation isolation
|
|
12
|
+
- **Remote Control**: Send prompts to OpenCode via Mattermost DMs
|
|
13
|
+
- **Multi-Session Management**: Control multiple OpenCode sessions in parallel via separate threads
|
|
14
|
+
- **Session Monitoring**: Get DM alerts when sessions need attention (permission requests, idle, questions)
|
|
15
|
+
- **Real-time Streaming**: Responses stream back in chunks with intelligent buffering
|
|
16
|
+
- **File Attachments**: Send and receive files through Mattermost
|
|
17
|
+
- **Automatic Reconnection**: WebSocket auto-reconnects with exponential backoff
|
|
18
|
+
|
|
19
|
+
### Real-time Status Display
|
|
20
|
+
- **Enhanced Status Indicator**: Shows processing state with elapsed time (e.g., `💻 Processing... (15s)`)
|
|
21
|
+
- **Tool Execution Display**: See which tools are being executed in real-time with timing
|
|
22
|
+
- **Live Shell Output**: Bash command output streams directly to Mattermost as it executes
|
|
23
|
+
- **Todo List Tracking**: View task progress during complex multi-step operations
|
|
24
|
+
- **Cost & Token Tracking**: Monitor LLM costs and token usage per session (e.g., `💰 $0.45 (+$0.03) | 125K tok`)
|
|
25
|
+
|
|
26
|
+
### Model Selection
|
|
27
|
+
- **Per-Session Model Switching**: Use `!models` to list available models, select by number
|
|
28
|
+
- **Model Persistence**: Selected model persists for the session thread
|
|
29
|
+
- **Multi-Provider Support**: Switch between providers (Anthropic, OpenAI, etc.) on the fly
|
|
30
|
+
|
|
31
|
+
### Multi-User Support
|
|
32
|
+
- **Owner Filtering**: Set `MATTERMOST_OWNER_USER_ID` to ensure your OpenCode instance only responds to your DMs
|
|
33
|
+
- **Shared Bot Account**: Multiple users can run separate OpenCode instances with the same bot
|
|
34
|
+
- **Per-User Sessions**: Each user's sessions are isolated
|
|
35
|
+
|
|
36
|
+
### Channel Support (Group DMs, Public & Private Channels)
|
|
37
|
+
- **Any Channel Type**: Bot works in 1:1 DMs, Group DMs, Public channels (`O`), and Private channels (`P`)
|
|
38
|
+
- **Selective Response**: In channels (non-1:1 DM), the bot only responds when explicitly @mentioned (e.g., `@opencode-bot help me with this`)
|
|
39
|
+
- **Thread Context Injection**: When responding in channels, the bot automatically includes context from the last 5 messages in the thread
|
|
40
|
+
- **Smart Context Summarization**: If thread context exceeds 8K characters, it's automatically summarized using Claude Haiku to stay within token limits
|
|
41
|
+
- **1:1 DM Behavior Unchanged**: Direct messages respond to all messages without requiring @mention
|
|
42
|
+
- **Configurable Channel Types**: Use `OPENCODE_MM_ALLOWED_CHANNEL_TYPES` to restrict which channel types the bot responds to
|
|
43
|
+
|
|
44
|
+
### Emoji Commands
|
|
45
|
+
React to any bot message with these emojis:
|
|
46
|
+
- ✅ Approve pending permission
|
|
47
|
+
- ❌ Deny pending permission
|
|
48
|
+
- 🛑 Cancel current operation
|
|
49
|
+
- 🔁 Retry last prompt
|
|
50
|
+
- 🗑️ Clear session files
|
|
51
|
+
|
|
52
|
+
### Notifications
|
|
53
|
+
- Get notified on completion, errors, and status changes
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Quick Start for Humans
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
|
|
61
|
+
- [OpenCode](https://opencode.ai) installed and configured
|
|
62
|
+
- A Mattermost instance with API access
|
|
63
|
+
- A Mattermost bot account with appropriate permissions
|
|
64
|
+
- [Bun](https://bun.sh) runtime (recommended) or Node.js 18+
|
|
65
|
+
|
|
66
|
+
### Step 1: Install the Plugin
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Using bun (recommended)
|
|
70
|
+
bun add -g opencode-mattermost-control
|
|
71
|
+
|
|
72
|
+
# Or using npm
|
|
73
|
+
npm install -g opencode-mattermost-control
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Step 2: Create a Mattermost Bot
|
|
77
|
+
|
|
78
|
+
1. Go to your Mattermost **System Console** → **Integrations** → **Bot Accounts**
|
|
79
|
+
2. Click **Add Bot Account**
|
|
80
|
+
3. Give it a username (e.g., `opencode-bot`) and description
|
|
81
|
+
4. Copy the generated **Access Token** - you'll need this!
|
|
82
|
+
|
|
83
|
+
### Step 3: Set Environment Variables
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
export MATTERMOST_TOKEN="your-bot-access-token"
|
|
87
|
+
export MATTERMOST_URL="https://your-mattermost-instance.com/api/v4"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Step 4: Connect from OpenCode
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Start OpenCode in your project
|
|
94
|
+
opencode
|
|
95
|
+
|
|
96
|
+
# Then type or use the tool:
|
|
97
|
+
mattermost_connect
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
You should see:
|
|
101
|
+
```
|
|
102
|
+
✓ Connected to Mattermost as @opencode-bot
|
|
103
|
+
✓ Listening for DMs
|
|
104
|
+
✓ Project: your-project-name
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Step 5: Send Prompts via DM
|
|
108
|
+
|
|
109
|
+
Open Mattermost, find your bot user, and send it a direct message:
|
|
110
|
+
```
|
|
111
|
+
Create a hello world function in Python
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The response will stream back in real-time!
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Quick Start for AI Agents
|
|
119
|
+
|
|
120
|
+
If you're an AI agent setting up this plugin, follow these steps:
|
|
121
|
+
|
|
122
|
+
### 1. Install
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
bun add -g opencode-mattermost-control
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 2. Configure Environment
|
|
129
|
+
|
|
130
|
+
Set these environment variables before starting OpenCode:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
export MATTERMOST_TOKEN="<bot-access-token>"
|
|
134
|
+
export MATTERMOST_URL="https://<mattermost-host>/api/v4"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 3. Connect
|
|
138
|
+
|
|
139
|
+
Use the `mattermost_connect` tool to establish connection:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
mattermost_connect
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 4. Verify Connection
|
|
146
|
+
|
|
147
|
+
Use `mattermost_status` to confirm the connection is active.
|
|
148
|
+
|
|
149
|
+
### 5. Available Tools
|
|
150
|
+
|
|
151
|
+
| Tool | Purpose |
|
|
152
|
+
|------|---------|
|
|
153
|
+
| `mattermost_connect` | Establish connection to Mattermost |
|
|
154
|
+
| `mattermost_disconnect` | Terminate connection |
|
|
155
|
+
| `mattermost_status` | Check connection state |
|
|
156
|
+
| `mattermost_list_sessions` | List available OpenCode sessions |
|
|
157
|
+
| `mattermost_select_session` | Select which session receives prompts |
|
|
158
|
+
| `mattermost_current_session` | Show currently targeted session |
|
|
159
|
+
| `mattermost_monitor` | Monitor session for events (permission, idle, question) |
|
|
160
|
+
| `mattermost_unmonitor` | Stop monitoring a session |
|
|
161
|
+
| `mattermost_send_file` | Upload a file to the current Mattermost thread |
|
|
162
|
+
| `mattermost_schedule_add` | Create a scheduled task with cron expression |
|
|
163
|
+
| `mattermost_schedule_list` | List all scheduled tasks |
|
|
164
|
+
| `mattermost_schedule_remove` | Delete a scheduled task |
|
|
165
|
+
| `mattermost_schedule_enable` | Enable a disabled scheduled task |
|
|
166
|
+
| `mattermost_schedule_disable` | Disable a scheduled task |
|
|
167
|
+
| `mattermost_schedule_run` | Run a scheduled task immediately |
|
|
168
|
+
|
|
169
|
+
### 6. Handling DMs
|
|
170
|
+
|
|
171
|
+
Once connected, DMs to the bot are processed as follows:
|
|
172
|
+
|
|
173
|
+
**Prompt Format:**
|
|
174
|
+
```
|
|
175
|
+
[Mattermost DM from @username]
|
|
176
|
+
[Reply-To: thread=abc123 post=xyz789 channel=dm_channel_id]
|
|
177
|
+
<user's message>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The `Reply-To` line provides context for agents with other Mattermost integrations (MCP servers, direct API) to reply to the correct thread.
|
|
181
|
+
|
|
182
|
+
**Auto-Session Creation:**
|
|
183
|
+
When a user sends a prompt in the main DM channel (not in a thread), a new OpenCode session is automatically created with its own dedicated thread. This makes the main DM the "new session launcher" - use threads to continue existing sessions. This can be disabled with `OPENCODE_MM_AUTO_CREATE_SESSION=false`.
|
|
184
|
+
|
|
185
|
+
### 7. Session Commands
|
|
186
|
+
|
|
187
|
+
Users can send these commands via DM to manage sessions:
|
|
188
|
+
- `!sessions` - List all available OpenCode sessions
|
|
189
|
+
- `!use <id>` - Switch to a specific session
|
|
190
|
+
- `!current` - Show currently selected session
|
|
191
|
+
- `!models` - List available models and select one for the current session
|
|
192
|
+
- `!model` - Show the currently selected model for this session
|
|
193
|
+
- `!help` - Show available commands
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Installation Options
|
|
198
|
+
|
|
199
|
+
### Option A: Global Install (Recommended)
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Install globally with bun
|
|
203
|
+
bun add -g opencode-mattermost-control
|
|
204
|
+
|
|
205
|
+
# Or with npm
|
|
206
|
+
npm install -g opencode-mattermost-control
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Option B: From Source
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
git clone https://github.com/Shakudo-io/opencode-mattermost-plugin.git
|
|
213
|
+
cd opencode-mattermost-plugin
|
|
214
|
+
bun install
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Option C: Per-Project
|
|
218
|
+
|
|
219
|
+
Add to your project's `opencode.json`:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"$schema": "https://opencode.ai/config.json",
|
|
224
|
+
"plugins": ["opencode-mattermost-control"]
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Configuration Reference
|
|
231
|
+
|
|
232
|
+
### Environment Variables
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Required
|
|
236
|
+
export MATTERMOST_TOKEN="your-bot-access-token"
|
|
237
|
+
export MATTERMOST_URL="https://your-mattermost-instance.com/api/v4"
|
|
238
|
+
|
|
239
|
+
# Optional (with defaults)
|
|
240
|
+
export MATTERMOST_WS_URL="wss://your-mattermost-instance.com/api/v4/websocket"
|
|
241
|
+
export MATTERMOST_TEAM="your-team-name"
|
|
242
|
+
export MATTERMOST_DEBUG="false"
|
|
243
|
+
export MATTERMOST_AUTO_CONNECT="true" # auto-connect on plugin load
|
|
244
|
+
|
|
245
|
+
# Advanced options
|
|
246
|
+
export MATTERMOST_RECONNECT_INTERVAL="5000" # ms between reconnect attempts
|
|
247
|
+
export MATTERMOST_MAX_RECONNECT_ATTEMPTS="10" # max reconnection tries
|
|
248
|
+
|
|
249
|
+
# Streaming configuration
|
|
250
|
+
export OPENCODE_MM_BUFFER_SIZE="50" # characters before flushing
|
|
251
|
+
export OPENCODE_MM_MAX_DELAY="500" # max ms before forced flush
|
|
252
|
+
export OPENCODE_MM_EDIT_RATE_LIMIT="10" # max edits per second
|
|
253
|
+
export OPENCODE_MM_MAX_POST_LENGTH="15000" # max chars before splitting into multiple posts
|
|
254
|
+
|
|
255
|
+
# Session configuration
|
|
256
|
+
export OPENCODE_MM_SESSION_TIMEOUT="3600000" # 1 hour in ms
|
|
257
|
+
export OPENCODE_MM_MAX_SESSIONS="50" # max concurrent sessions
|
|
258
|
+
export OPENCODE_MM_ALLOWED_USERS="" # comma-separated user IDs (empty = all)
|
|
259
|
+
export OPENCODE_MM_AUTO_CREATE_SESSION="true" # auto-create session from main DM
|
|
260
|
+
export OPENCODE_MM_ALLOWED_CHANNEL_TYPES="D,G,O,P" # allowed channel types (D=DM, G=Group, O=Public, P=Private)
|
|
261
|
+
|
|
262
|
+
# Multi-user / Owner filtering
|
|
263
|
+
export MATTERMOST_OWNER_USER_ID="" # Only respond to DMs from this user ID
|
|
264
|
+
# Allows multiple OpenCode instances to share one bot
|
|
265
|
+
|
|
266
|
+
# File handling
|
|
267
|
+
export OPENCODE_MM_TEMP_DIR="/tmp/opencode-mm-plugin"
|
|
268
|
+
export OPENCODE_MM_MAX_FILE_SIZE="10485760" # 10MB
|
|
269
|
+
export OPENCODE_MM_ALLOWED_EXTENSIONS="*" # comma-separated or * for all
|
|
270
|
+
|
|
271
|
+
# Notifications
|
|
272
|
+
export OPENCODE_MM_NOTIFY_COMPLETION="true"
|
|
273
|
+
export OPENCODE_MM_NOTIFY_PERMISSION="true"
|
|
274
|
+
export OPENCODE_MM_NOTIFY_ERROR="true"
|
|
275
|
+
export OPENCODE_MM_NOTIFY_STATUS="true"
|
|
276
|
+
|
|
277
|
+
# Logging
|
|
278
|
+
export MM_PLUGIN_LOG_FILE="/tmp/opencode-mattermost-plugin.log"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### OpenCode Configuration
|
|
282
|
+
|
|
283
|
+
Add to global config (`~/.config/opencode/opencode.json`):
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"plugins": ["opencode-mattermost-control"]
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Or per-project (`opencode.json` in project root):
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"$schema": "https://opencode.ai/config.json",
|
|
296
|
+
"plugins": ["opencode-mattermost-control"]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Usage Examples
|
|
303
|
+
|
|
304
|
+
### Basic Workflow
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# 1. Start OpenCode
|
|
308
|
+
opencode
|
|
309
|
+
|
|
310
|
+
# 2. Connect to Mattermost
|
|
311
|
+
> mattermost_connect
|
|
312
|
+
✓ Connected to Mattermost as @your-bot
|
|
313
|
+
✓ Listening for DMs
|
|
314
|
+
|
|
315
|
+
# 3. Check status anytime
|
|
316
|
+
> mattermost_status
|
|
317
|
+
|
|
318
|
+
# 4. Disconnect when done
|
|
319
|
+
> mattermost_disconnect
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Thread-Per-Session Workflow
|
|
323
|
+
|
|
324
|
+
When the plugin connects, it automatically creates a dedicated Mattermost thread for each active OpenCode session. This provides clean conversation isolation and parallel session control.
|
|
325
|
+
|
|
326
|
+
**How it works:**
|
|
327
|
+
1. When a new OpenCode session starts, a thread is automatically created in your DM with the bot
|
|
328
|
+
2. The thread root post shows session info (project, directory, session ID)
|
|
329
|
+
3. Post in the thread to send prompts to that specific session
|
|
330
|
+
4. Responses stream back to the same thread
|
|
331
|
+
5. When the session ends, the thread is marked as ended
|
|
332
|
+
|
|
333
|
+
**Example:**
|
|
334
|
+
```
|
|
335
|
+
Bot: 🚀 OpenCode Session Started
|
|
336
|
+
|
|
337
|
+
Project: my-awesome-app
|
|
338
|
+
Directory: /home/user/projects/my-awesome-app
|
|
339
|
+
Session: ses_abc1
|
|
340
|
+
Started: 2024-01-15T10:30:00.000Z
|
|
341
|
+
|
|
342
|
+
Reply in this thread to send prompts to this session.
|
|
343
|
+
|
|
344
|
+
You (in thread): List all TypeScript files
|
|
345
|
+
Bot (in thread): [Streaming response...]
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Main DM commands:**
|
|
349
|
+
- `!sessions` - List all sessions with links to their threads
|
|
350
|
+
- `!help` - Show available commands
|
|
351
|
+
|
|
352
|
+
**Thread behavior:**
|
|
353
|
+
- Prompts in main DM (outside threads) are rejected with guidance to use session threads
|
|
354
|
+
- Each thread maps to exactly one OpenCode session
|
|
355
|
+
- Thread posts are routed to the correct session automatically
|
|
356
|
+
- Ended sessions show a completion message and reject new prompts
|
|
357
|
+
|
|
358
|
+
### Sending Files to Mattermost
|
|
359
|
+
|
|
360
|
+
OpenCode can send files directly to your Mattermost conversation thread using the `mattermost_send_file` tool:
|
|
361
|
+
|
|
362
|
+
**Example conversation:**
|
|
363
|
+
```
|
|
364
|
+
You (in thread): Create a Python script that generates a report and send it to me
|
|
365
|
+
Bot: [Creates the file using Write tool]
|
|
366
|
+
Bot: [Uses mattermost_send_file to upload report.py]
|
|
367
|
+
File sent to Mattermost: report.py
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
The tool automatically posts the file to the correct thread. Users can simply ask:
|
|
371
|
+
- "Send me the file you just created"
|
|
372
|
+
- "Upload that script to Mattermost"
|
|
373
|
+
- "Give me the report.pdf"
|
|
374
|
+
|
|
375
|
+
**Supported file types:** All common types including code files, documents (PDF, MD, TXT), images (PNG, JPG, GIF), and archives (ZIP).
|
|
376
|
+
|
|
377
|
+
**Size limit:** 10MB by default (configurable via `OPENCODE_MM_MAX_FILE_SIZE`).
|
|
378
|
+
|
|
379
|
+
### Session Management Commands
|
|
380
|
+
|
|
381
|
+
When connected, you can manage multiple OpenCode sessions via DM commands:
|
|
382
|
+
|
|
383
|
+
| Command | Description |
|
|
384
|
+
|---------|-------------|
|
|
385
|
+
| `!sessions` | List all available OpenCode sessions with thread links |
|
|
386
|
+
| `!models` | List available models grouped by provider, select by number |
|
|
387
|
+
| `!model` | Show the currently selected model for this session |
|
|
388
|
+
| `!costs` | Show LLM token usage and costs for the current session |
|
|
389
|
+
| `!stop` | Cancel the current processing operation |
|
|
390
|
+
| `!merge <url>` | Merge another thread's conversation into the current session |
|
|
391
|
+
| `!reject` | Skip/cancel a pending AI question |
|
|
392
|
+
| `!help` | Display available commands and thread workflow |
|
|
393
|
+
|
|
394
|
+
**Example:**
|
|
395
|
+
```
|
|
396
|
+
You: !sessions
|
|
397
|
+
Bot: 📋 Available Sessions (2)
|
|
398
|
+
|
|
399
|
+
1. 🟢 my-awesome-app (ses_abc1) - 5m ago
|
|
400
|
+
📁 /home/user/projects/my-awesome-app
|
|
401
|
+
🔗 Thread: [Click to open]
|
|
402
|
+
|
|
403
|
+
2. 🟢 another-project (ses_def2) - 2h ago
|
|
404
|
+
📁 /home/user/projects/another-project
|
|
405
|
+
🔗 Thread: [Click to open]
|
|
406
|
+
|
|
407
|
+
Reply in a session thread to send prompts.
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Model Selection
|
|
411
|
+
|
|
412
|
+
Switch between different LLM models on a per-session basis:
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
You: !models
|
|
416
|
+
Bot: 🤖 Available Models
|
|
417
|
+
|
|
418
|
+
Anthropic
|
|
419
|
+
1. claude-sonnet-4-20250514
|
|
420
|
+
2. claude-3-5-haiku-20241022
|
|
421
|
+
|
|
422
|
+
OpenAI
|
|
423
|
+
3. gpt-4o
|
|
424
|
+
4. o3
|
|
425
|
+
|
|
426
|
+
Reply with a number to select a model.
|
|
427
|
+
|
|
428
|
+
You: 1
|
|
429
|
+
Bot: ✅ Model set to claude-sonnet-4-20250514 (Anthropic) for this session.
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The selected model persists for the session thread. Use `!model` to check the current selection.
|
|
433
|
+
|
|
434
|
+
### Cost Tracking
|
|
435
|
+
|
|
436
|
+
Monitor LLM token usage and costs for your session using the `!costs` command:
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
You: !costs
|
|
440
|
+
Bot: 💰 Session Costs (ses_abc1)
|
|
441
|
+
|
|
442
|
+
Total Cost: $0.47
|
|
443
|
+
Input Tokens: 125,432
|
|
444
|
+
Output Tokens: 8,291
|
|
445
|
+
|
|
446
|
+
Model: claude-sonnet-4-20250514
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
The status indicator also shows running costs during responses: `💰 $0.45 (+$0.03) | 125K tok`
|
|
450
|
+
|
|
451
|
+
### Thread Merging
|
|
452
|
+
|
|
453
|
+
Merge the context from one thread into another using the `!merge` command. This is useful when you want to continue a conversation from a different session thread while preserving its context.
|
|
454
|
+
|
|
455
|
+
**Usage:**
|
|
456
|
+
```
|
|
457
|
+
!merge https://mattermost.example.com/team/pl/postid123
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**How it works:**
|
|
461
|
+
1. Copy the URL of the thread you want to merge (source thread)
|
|
462
|
+
2. Go to the thread you want to merge INTO (destination thread)
|
|
463
|
+
3. Type `!merge <url>` with the source thread URL
|
|
464
|
+
4. The source thread's conversation is summarized using AI and injected into the destination thread
|
|
465
|
+
5. The source thread is marked as "merged" and locked
|
|
466
|
+
|
|
467
|
+
**Example:**
|
|
468
|
+
```
|
|
469
|
+
[In destination thread]
|
|
470
|
+
You: !merge https://mattermost.dev.hyperplane.dev/shakudo/pl/abc123xyz
|
|
471
|
+
|
|
472
|
+
Bot: 🔀 **Merged Thread Context**
|
|
473
|
+
|
|
474
|
+
Merged conversation from [my-project (ses_abc1)](link):
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
**Summary:**
|
|
479
|
+
- User requested implementation of a REST API for user management
|
|
480
|
+
- Discussed authentication approach (JWT tokens)
|
|
481
|
+
- Created User model with email, password, and role fields
|
|
482
|
+
- TODO: Implement password hashing and token refresh
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
_Merged by @alice at 2026-01-26 15:30_
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Source thread after merge:**
|
|
489
|
+
```
|
|
490
|
+
Bot: 🔒 **Thread Merged**
|
|
491
|
+
|
|
492
|
+
This conversation has been merged into another thread.
|
|
493
|
+
Continue the conversation [here](link).
|
|
494
|
+
|
|
495
|
+
_Merged at 2026-01-26 15:30_
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
**Restrictions:**
|
|
499
|
+
- Can only merge threads from the same OpenCode instance
|
|
500
|
+
- Cannot merge a thread into itself
|
|
501
|
+
- Cannot merge an already-merged thread
|
|
502
|
+
- Must have an active session in the destination thread
|
|
503
|
+
|
|
504
|
+
**What gets preserved:**
|
|
505
|
+
- AI-generated summary of key decisions and context
|
|
506
|
+
- Requirements and constraints discussed
|
|
507
|
+
- Actions taken and their outcomes
|
|
508
|
+
- Unfinished work and next steps
|
|
509
|
+
|
|
510
|
+
### File Path Completion (`!!`)
|
|
511
|
+
|
|
512
|
+
Reference files directly in your prompts using the `!!` prefix. The bot will automatically find and include matching files from your project.
|
|
513
|
+
|
|
514
|
+
**Usage:**
|
|
515
|
+
```
|
|
516
|
+
You: Look at !!src/config and tell me what settings are available
|
|
517
|
+
Bot: [Finds src/config.ts or similar, includes content, then responds]
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**How it works:**
|
|
521
|
+
1. Type `!!` followed by a partial file path (e.g., `!!src/resp`)
|
|
522
|
+
2. The bot searches for matching files in your project
|
|
523
|
+
3. If exact match: file content is automatically included
|
|
524
|
+
4. If multiple matches: bot prompts you to select from options
|
|
525
|
+
|
|
526
|
+
**Example with fuzzy matching:**
|
|
527
|
+
```
|
|
528
|
+
You: Check !!handler for bugs
|
|
529
|
+
|
|
530
|
+
Bot: 🔍 Multiple files match "handler". Select one or more:
|
|
531
|
+
|
|
532
|
+
1. src/command-handler.ts
|
|
533
|
+
2. src/file-handler.ts
|
|
534
|
+
3. src/question-handler.ts
|
|
535
|
+
4. src/reaction-handler.ts
|
|
536
|
+
|
|
537
|
+
Reply with number(s) separated by commas (e.g., "1,3")
|
|
538
|
+
|
|
539
|
+
You: 1,2
|
|
540
|
+
Bot: [Includes both files and processes the original request]
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Notes:**
|
|
544
|
+
- `!!` references inside code blocks are ignored
|
|
545
|
+
- You can include multiple `!!` references in one message
|
|
546
|
+
- Works with partial paths and fuzzy matching
|
|
547
|
+
|
|
548
|
+
### Channel Usage (Group DMs, Public & Private Channels)
|
|
549
|
+
|
|
550
|
+
The bot can participate in any channel it's a member of: Group DMs, Public channels, and Private channels. In these channels, the bot uses **selective response** - it only responds when explicitly @mentioned.
|
|
551
|
+
|
|
552
|
+
**Supported channel types:**
|
|
553
|
+
- `D` - 1:1 Direct Messages (no @mention required)
|
|
554
|
+
- `G` - Group Direct Messages (@mention required)
|
|
555
|
+
- `O` - Public Channels (@mention required)
|
|
556
|
+
- `P` - Private Channels (@mention required)
|
|
557
|
+
|
|
558
|
+
**How it works:**
|
|
559
|
+
1. Add the bot to a channel (Group DM, public, or private channel)
|
|
560
|
+
2. @mention the bot when you want it to respond: `@opencode-bot explain this error`
|
|
561
|
+
3. The bot automatically includes context from recent messages in the thread
|
|
562
|
+
4. Messages without @mention are silently ignored
|
|
563
|
+
|
|
564
|
+
**Example:**
|
|
565
|
+
```
|
|
566
|
+
Alice: Hey team, I'm seeing a weird error in the logs
|
|
567
|
+
Bob: Can you paste the stack trace?
|
|
568
|
+
Alice: [pastes error]
|
|
569
|
+
You: @opencode-bot can you explain what's causing this NullPointerException?
|
|
570
|
+
Bot: [Responds with explanation, having read the context from Alice and Bob's messages]
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
**Context handling:**
|
|
574
|
+
- The bot reads the last 5 messages from the thread to understand the conversation
|
|
575
|
+
- If the context is too long (>8K characters), it's automatically summarized using Claude Haiku
|
|
576
|
+
- This ensures the bot has relevant context without consuming excessive tokens
|
|
577
|
+
|
|
578
|
+
**Restricting channel types:**
|
|
579
|
+
You can limit which channel types the bot responds to using the `OPENCODE_MM_ALLOWED_CHANNEL_TYPES` environment variable:
|
|
580
|
+
```bash
|
|
581
|
+
# Only allow DMs and Group DMs (no public/private channels)
|
|
582
|
+
export OPENCODE_MM_ALLOWED_CHANNEL_TYPES="D,G"
|
|
583
|
+
|
|
584
|
+
# Only allow DMs (default 1:1 behavior)
|
|
585
|
+
export OPENCODE_MM_ALLOWED_CHANNEL_TYPES="D"
|
|
586
|
+
|
|
587
|
+
# All channel types (default)
|
|
588
|
+
export OPENCODE_MM_ALLOWED_CHANNEL_TYPES="D,G,O,P"
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Note:** In regular 1:1 DMs, the bot responds to all messages without requiring @mention.
|
|
592
|
+
|
|
593
|
+
### Guest Approval in Channels
|
|
594
|
+
|
|
595
|
+
When multiple users are in a channel with the bot, each OpenCode instance only processes messages from its owner (the user who owns the session). If another user @mentions the bot, the thread owner must approve the request.
|
|
596
|
+
|
|
597
|
+
**How it works:**
|
|
598
|
+
1. User A (thread owner) creates a session by @mentioning the bot
|
|
599
|
+
2. User B @mentions the bot in the same thread: `@opencode-bot help me with this`
|
|
600
|
+
3. The bot posts an approval request visible to User A:
|
|
601
|
+
```
|
|
602
|
+
🔔 Guest Access Request
|
|
603
|
+
|
|
604
|
+
@userB wants to send a prompt to your session.
|
|
605
|
+
|
|
606
|
+
**Options:**
|
|
607
|
+
- Reply `1` to approve this message only
|
|
608
|
+
- Reply `2` to approve @userB permanently for this thread
|
|
609
|
+
- Reply `3` to approve ALL users in this thread
|
|
610
|
+
- Reply `deny` to reject
|
|
611
|
+
|
|
612
|
+
⏰ Request expires in 30 minutes
|
|
613
|
+
```
|
|
614
|
+
4. User A replies with `1`, `2`, or `3`
|
|
615
|
+
5. If approved, the original message from User B is processed
|
|
616
|
+
|
|
617
|
+
**Approval options:**
|
|
618
|
+
- **1 (Once)**: Approve this single message, future messages from this user still require approval
|
|
619
|
+
- **2 (User)**: Permanently approve this user - their future @mentions are processed automatically
|
|
620
|
+
- **3 (All)**: Approve all users - any user in the channel can send prompts without approval
|
|
621
|
+
|
|
622
|
+
**Persistence:** User approvals (option 2) and "approve all" settings (option 3) are stored in the thread mapping and persist across sessions.
|
|
623
|
+
|
|
624
|
+
**Example flow:**
|
|
625
|
+
```
|
|
626
|
+
[Channel: #engineering with Alice, Bob, opencode-bot]
|
|
627
|
+
|
|
628
|
+
Alice: @opencode-bot analyze this code
|
|
629
|
+
Bot: [Creates session, responds to Alice]
|
|
630
|
+
|
|
631
|
+
Bob: @opencode-bot can you also check for security issues?
|
|
632
|
+
Bot: 🔔 Guest Access Request
|
|
633
|
+
@bob wants to send a prompt to your session...
|
|
634
|
+
Reply 1/2/3 or deny
|
|
635
|
+
|
|
636
|
+
Alice: 2
|
|
637
|
+
Bot: ✅ @bob is now approved for this thread.
|
|
638
|
+
Bot: [Processes Bob's request about security issues]
|
|
639
|
+
|
|
640
|
+
Bob: @opencode-bot what about performance?
|
|
641
|
+
Bot: [Processes automatically - Bob is pre-approved]
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Multi-User Setup (Shared Bot)
|
|
645
|
+
|
|
646
|
+
When multiple users want to run separate OpenCode instances with the same Mattermost bot account, use owner filtering to prevent conflicts:
|
|
647
|
+
|
|
648
|
+
```bash
|
|
649
|
+
# User A's environment
|
|
650
|
+
export MATTERMOST_OWNER_USER_ID="user_a_id_here"
|
|
651
|
+
|
|
652
|
+
# User B's environment
|
|
653
|
+
export MATTERMOST_OWNER_USER_ID="user_b_id_here"
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Each OpenCode instance will only respond to DMs from its configured owner. To find your user ID, check your Mattermost profile or ask an admin.
|
|
657
|
+
|
|
658
|
+
### Emoji Commands
|
|
659
|
+
|
|
660
|
+
React to any bot message with these emojis:
|
|
661
|
+
|
|
662
|
+
| Emoji | Action |
|
|
663
|
+
|-------|--------|
|
|
664
|
+
| ✅ | Approve pending permission request |
|
|
665
|
+
| ❌ | Deny pending permission request |
|
|
666
|
+
| 🛑 | Cancel current operation |
|
|
667
|
+
| 🔁 | Retry the last prompt |
|
|
668
|
+
| 🗑️ | Clear session temporary files |
|
|
669
|
+
|
|
670
|
+
### AI Question Tool Support
|
|
671
|
+
|
|
672
|
+
When OpenCode uses the question tool to ask for clarification, the question appears directly in your Mattermost thread with numbered options:
|
|
673
|
+
|
|
674
|
+
```
|
|
675
|
+
### ❓ Language
|
|
676
|
+
|
|
677
|
+
Which language would you like to use?
|
|
678
|
+
|
|
679
|
+
**1.** TypeScript - _Modern JavaScript with types_
|
|
680
|
+
**2.** Python - _Great for data science_
|
|
681
|
+
**3.** Other - _Type your own answer_
|
|
682
|
+
|
|
683
|
+
---
|
|
684
|
+
_Reply with a number or type your answer_
|
|
685
|
+
_Use `!reject` to skip this question_
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**How to respond:**
|
|
689
|
+
- Reply with a **number** (e.g., `1`) to select an option
|
|
690
|
+
- Reply with **multiple numbers** separated by commas for multi-select questions (e.g., `1, 3`)
|
|
691
|
+
- Type a **custom answer** directly (e.g., `Rust`)
|
|
692
|
+
- Use `!reject` or `!cancel` to skip the question (sends empty response to AI)
|
|
693
|
+
|
|
694
|
+
**Notes:**
|
|
695
|
+
- Questions expire after 30 minutes if not answered
|
|
696
|
+
- Only one question can be pending per session at a time
|
|
697
|
+
- Multi-question flows are supported (answer each question in sequence)
|
|
698
|
+
|
|
699
|
+
### Session Monitoring
|
|
700
|
+
|
|
701
|
+
Monitor OpenCode sessions and receive DM alerts when they need attention. Works without requiring an active Mattermost connection.
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
# Start monitoring the current session
|
|
705
|
+
> /mattermost-monitor
|
|
706
|
+
|
|
707
|
+
# Or use the tool directly
|
|
708
|
+
> mattermost_monitor targetUser="your-username"
|
|
709
|
+
|
|
710
|
+
# Stop monitoring
|
|
711
|
+
> mattermost_unmonitor
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
**Alert types:**
|
|
715
|
+
- **Permission requested** - Session is waiting for permission approval
|
|
716
|
+
- **Session idle** - Session finished and is waiting for input
|
|
717
|
+
- **Question asked** - Session is asking a clarifying question
|
|
718
|
+
|
|
719
|
+
**Example alert:**
|
|
720
|
+
```
|
|
721
|
+
🔔 OpenCode Session Alert
|
|
722
|
+
|
|
723
|
+
Project: business-automation
|
|
724
|
+
Session: ses_4426 - Mattermost plugin codebase review
|
|
725
|
+
Directory: /root/gitrepos/business-automation
|
|
726
|
+
|
|
727
|
+
⏳ Alert: Session is idle (waiting for input)
|
|
728
|
+
|
|
729
|
+
Use `!use ses_4426` in DM to connect to this session.
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Options:**
|
|
733
|
+
- `sessionId` - Monitor a specific session (defaults to current)
|
|
734
|
+
- `targetUser` - Mattermost username to notify (required if not connected)
|
|
735
|
+
- `persistent` - Keep monitoring after each alert (default: true). Set to false for one-time alerts.
|
|
736
|
+
|
|
737
|
+
### Scheduled Tasks
|
|
738
|
+
|
|
739
|
+
Create cron-based scheduled tasks that run prompts at specified times and DM you the results. Useful for automated reports, periodic checks, or recurring tasks.
|
|
740
|
+
|
|
741
|
+
**Available tools:**
|
|
742
|
+
|
|
743
|
+
| Tool | Description |
|
|
744
|
+
|------|-------------|
|
|
745
|
+
| `mattermost_schedule_add` | Create a new scheduled task |
|
|
746
|
+
| `mattermost_schedule_list` | List all scheduled tasks |
|
|
747
|
+
| `mattermost_schedule_remove` | Delete a scheduled task |
|
|
748
|
+
| `mattermost_schedule_enable` | Enable a disabled task |
|
|
749
|
+
| `mattermost_schedule_disable` | Disable a task without deleting |
|
|
750
|
+
| `mattermost_schedule_run` | Run a task immediately for testing |
|
|
751
|
+
|
|
752
|
+
**Example - Daily status report:**
|
|
753
|
+
```
|
|
754
|
+
> mattermost_schedule_add name="morning-status" cron="0 9 * * *" prompt="Give me a summary of pending PRs and open issues" timezone="America/New_York"
|
|
755
|
+
|
|
756
|
+
✅ Schedule created: morning-status
|
|
757
|
+
Cron: 0 9 * * * (America/New_York)
|
|
758
|
+
Next run: 2026-01-27 09:00:00
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
**Example - Periodic health check:**
|
|
762
|
+
```
|
|
763
|
+
> mattermost_schedule_add name="health-check" cron="0 */4 * * *" prompt="Check if all services are running and report any issues"
|
|
764
|
+
|
|
765
|
+
✅ Schedule created: health-check
|
|
766
|
+
Cron: 0 */4 * * * (UTC)
|
|
767
|
+
Next run: 2026-01-26 20:00:00
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
**Cron expression examples:**
|
|
771
|
+
- `0 9 * * *` - Every day at 9am
|
|
772
|
+
- `0 9,17 * * 1-5` - 9am and 5pm on weekdays
|
|
773
|
+
- `0 */4 * * *` - Every 4 hours
|
|
774
|
+
- `30 8 * * 1` - Every Monday at 8:30am
|
|
775
|
+
|
|
776
|
+
**Managing schedules:**
|
|
777
|
+
```
|
|
778
|
+
> mattermost_schedule_list
|
|
779
|
+
📋 Scheduled Tasks (2)
|
|
780
|
+
|
|
781
|
+
1. ✅ morning-status
|
|
782
|
+
Cron: 0 9 * * * (America/New_York)
|
|
783
|
+
Next: 2026-01-27 09:00:00
|
|
784
|
+
|
|
785
|
+
2. ✅ health-check
|
|
786
|
+
Cron: 0 */4 * * * (UTC)
|
|
787
|
+
Next: 2026-01-26 20:00:00
|
|
788
|
+
|
|
789
|
+
> mattermost_schedule_disable name="health-check"
|
|
790
|
+
✅ Schedule disabled: health-check
|
|
791
|
+
|
|
792
|
+
> mattermost_schedule_run name="morning-status"
|
|
793
|
+
🚀 Running schedule: morning-status
|
|
794
|
+
[Results are sent to your DM]
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Multi-Session Setup (Shared Server)
|
|
798
|
+
|
|
799
|
+
When controlling multiple OpenCode sessions via Mattermost, you need to run them on a **shared server** so that events (like incoming prompts) are visible across all TUIs.
|
|
800
|
+
|
|
801
|
+
**Why?** By default, each `opencode` command starts its own isolated server. Messages sent to one session won't appear in another session's TUI because they don't share the same event bus.
|
|
802
|
+
|
|
803
|
+
**Solution:** Use OpenCode's shared server mode:
|
|
804
|
+
|
|
805
|
+
#### Option 1: Manual Setup
|
|
806
|
+
|
|
807
|
+
```bash
|
|
808
|
+
# Terminal 1 - Start the shared server
|
|
809
|
+
opencode serve --port 4096
|
|
810
|
+
|
|
811
|
+
# Terminal 2 - Attach first TUI (run mattermost_connect here)
|
|
812
|
+
cd /path/to/project-a
|
|
813
|
+
opencode attach http://localhost:4096
|
|
814
|
+
|
|
815
|
+
# Terminal 3 - Attach second TUI
|
|
816
|
+
cd /path/to/project-b
|
|
817
|
+
opencode attach http://localhost:4096
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
#### Option 2: Using the Helper Script
|
|
821
|
+
|
|
822
|
+
A convenience script `opencode-shared` is provided that automatically manages the shared server:
|
|
823
|
+
|
|
824
|
+
```bash
|
|
825
|
+
# First run - starts server in background, then attaches TUI
|
|
826
|
+
./opencode-shared
|
|
827
|
+
|
|
828
|
+
# Subsequent runs - attaches to existing server
|
|
829
|
+
./opencode-shared
|
|
830
|
+
|
|
831
|
+
# With optional password for security
|
|
832
|
+
OPENCODE_SERVER_PASSWORD="your-secret" ./opencode-shared
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
The script:
|
|
836
|
+
- Checks if a shared server is already running on port 4096
|
|
837
|
+
- If not, starts one as a background process
|
|
838
|
+
- Attaches a TUI to the shared server
|
|
839
|
+
- Logs server output to `/tmp/opencode-server.log`
|
|
840
|
+
|
|
841
|
+
#### Restarting with Plugin Updates
|
|
842
|
+
|
|
843
|
+
Use `opencode-shared-restart` to restart OpenCode with optional plugin version changes:
|
|
844
|
+
|
|
845
|
+
```bash
|
|
846
|
+
# Restart with current plugin version
|
|
847
|
+
opencode-shared-restart
|
|
848
|
+
|
|
849
|
+
# Update plugin and restart
|
|
850
|
+
opencode-shared-restart -v 0.2.59
|
|
851
|
+
|
|
852
|
+
# Rollback to a previous version
|
|
853
|
+
opencode-shared-restart --rollback 0.2.55
|
|
854
|
+
|
|
855
|
+
# Check current installed version
|
|
856
|
+
opencode-shared-restart --current
|
|
857
|
+
|
|
858
|
+
# List available versions
|
|
859
|
+
opencode-shared-restart --list
|
|
860
|
+
|
|
861
|
+
# Check status of last restart (after reconnecting)
|
|
862
|
+
opencode-shared-restart --status
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
**How it works:**
|
|
866
|
+
The restart runs in a **detached background process** so it survives the OpenCode server restart. This is especially useful when an AI agent triggers the restart - the agent will be disconnected but the restart completes successfully.
|
|
867
|
+
|
|
868
|
+
After reconnecting to OpenCode, use `--status` to verify the restart succeeded:
|
|
869
|
+
|
|
870
|
+
```bash
|
|
871
|
+
$ opencode-shared-restart --status
|
|
872
|
+
=== Last Restart Status ===
|
|
873
|
+
Status: SUCCESS
|
|
874
|
+
Time: 2026-01-18T12:14:32+00:00
|
|
875
|
+
Plugin Version: 0.2.59
|
|
876
|
+
Message: Server running with v0.2.59
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
**Automatic rollback:** If the specified version fails to start, the script automatically attempts to restore the previous working version and start the server with that instead.
|
|
880
|
+
|
|
881
|
+
#### Security Note
|
|
882
|
+
|
|
883
|
+
For production use, set `OPENCODE_SERVER_PASSWORD` environment variable on both server and client:
|
|
884
|
+
|
|
885
|
+
```bash
|
|
886
|
+
export OPENCODE_SERVER_PASSWORD="your-secure-password"
|
|
887
|
+
opencode serve --port 4096
|
|
888
|
+
|
|
889
|
+
# In another terminal (same password required)
|
|
890
|
+
export OPENCODE_SERVER_PASSWORD="your-secure-password"
|
|
891
|
+
opencode attach http://localhost:4096
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
## Architecture
|
|
895
|
+
|
|
896
|
+

|
|
897
|
+
|
|
898
|
+
**Data Flow:**
|
|
899
|
+
1. User sends a DM to the bot in Mattermost
|
|
900
|
+
2. Plugin receives the message via WebSocket
|
|
901
|
+
3. Plugin forwards the prompt to OpenCode CLI
|
|
902
|
+
4. OpenCode processes and streams responses back
|
|
903
|
+
5. Plugin delivers chunked responses to Mattermost DM
|
|
904
|
+
|
|
905
|
+
### Components
|
|
906
|
+
|
|
907
|
+
| Component | Description |
|
|
908
|
+
|-----------|-------------|
|
|
909
|
+
| `MattermostClient` | HTTP API client for posts, channels, files, reactions |
|
|
910
|
+
| `WebSocketClient` | Real-time event streaming for instant message detection |
|
|
911
|
+
| `SessionManager` | Per-user session tracking with timeout management |
|
|
912
|
+
| `OpenCodeSessionRegistry` | Discovers and tracks all available OpenCode sessions |
|
|
913
|
+
| `ThreadManager` | Creates and manages session threads, handles lifecycle |
|
|
914
|
+
| `ThreadMappingStore` | Persists thread-to-session mappings with indexes |
|
|
915
|
+
| `MessageRouter` | Routes messages to correct sessions based on thread context |
|
|
916
|
+
| `CommandHandler` | Processes `!commands` for session management |
|
|
917
|
+
| `ResponseStreamer` | Chunked message delivery to correct thread |
|
|
918
|
+
| `NotificationService` | Completion, error, and status notifications |
|
|
919
|
+
| `FileHandler` | Inbound/outbound file attachment processing |
|
|
920
|
+
| `ReactionHandler` | Emoji-based command execution |
|
|
921
|
+
| `MonitorService` | Session event monitoring and DM alerts |
|
|
922
|
+
| `QuestionHandler` | AI question tool support with user responses |
|
|
923
|
+
| `ContextBuilder` | Builds thread context for group DMs, with optional Haiku summarization |
|
|
924
|
+
| `MergeHandler` | Thread merging with AI summarization and source thread locking |
|
|
925
|
+
| `FileCompletionHandler` | `!!` file path completion with fuzzy matching |
|
|
926
|
+
| `GuestApprovalHandler` | Cross-user approval for shared channel sessions |
|
|
927
|
+
| `SessionOwnershipHandler` | Ownership confirmation for group DM sessions |
|
|
928
|
+
| `SchedulerService` | Cron-based scheduled task execution |
|
|
929
|
+
|
|
930
|
+
## Project Structure
|
|
931
|
+
|
|
932
|
+
```
|
|
933
|
+
opencode-mattermost-plugin/
|
|
934
|
+
├── package.json
|
|
935
|
+
├── tsconfig.json
|
|
936
|
+
├── opencode.json
|
|
937
|
+
├── .opencode/
|
|
938
|
+
│ └── plugin/
|
|
939
|
+
│ └── mattermost-control/
|
|
940
|
+
│ ├── index.ts # Main plugin entry point
|
|
941
|
+
│ └── package.json
|
|
942
|
+
└── src/
|
|
943
|
+
├── clients/
|
|
944
|
+
│ ├── mattermost-client.ts # HTTP API client
|
|
945
|
+
│ └── websocket-client.ts # WebSocket client
|
|
946
|
+
├── persistence/
|
|
947
|
+
│ └── thread-mapping-store.ts # Thread mapping persistence
|
|
948
|
+
├── models/
|
|
949
|
+
│ ├── index.ts # TypeScript types
|
|
950
|
+
│ ├── thread-mapping.ts # Thread mapping Zod schemas
|
|
951
|
+
│ └── routing.ts # Message routing types
|
|
952
|
+
├── scheduler/
|
|
953
|
+
│ ├── schedule-store.ts # Schedule persistence
|
|
954
|
+
│ └── scheduler-service.ts # Cron job execution
|
|
955
|
+
├── command-handler.ts # !command processing
|
|
956
|
+
├── config.ts # Configuration loading
|
|
957
|
+
├── context-builder.ts # Group DM context building & summarization
|
|
958
|
+
├── file-completion-handler.ts # !! file path completion
|
|
959
|
+
├── file-handler.ts # File uploads/downloads
|
|
960
|
+
├── guest-approval-handler.ts # Cross-user approval for channels
|
|
961
|
+
├── logger.ts # File-based logging
|
|
962
|
+
├── merge-handler.ts # Thread merging with AI summarization
|
|
963
|
+
├── message-router.ts # Thread-aware message routing
|
|
964
|
+
├── monitor-service.ts # Session monitoring and alerts
|
|
965
|
+
├── notification-service.ts # Status notifications
|
|
966
|
+
├── opencode-session-registry.ts # OpenCode session discovery
|
|
967
|
+
├── question-handler.ts # AI question tool responses
|
|
968
|
+
├── reaction-handler.ts # Emoji reaction handling
|
|
969
|
+
├── response-streamer.ts # Streams responses to MM
|
|
970
|
+
├── session-manager.ts # User session management
|
|
971
|
+
├── session-ownership-handler.ts # Group DM ownership confirmation
|
|
972
|
+
├── status-indicator.ts # Real-time status display
|
|
973
|
+
├── thread-manager.ts # Thread lifecycle management
|
|
974
|
+
└── todo-manager.ts # Todo list tracking
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
## Updating the Plugin
|
|
978
|
+
|
|
979
|
+
OpenCode caches plugins in `~/.config/opencode/node_modules/`. Simply running `bun add -g` or `npm install -g` does **not** update the running plugin. Follow these steps:
|
|
980
|
+
|
|
981
|
+
### Step 1: Update the Version Pin
|
|
982
|
+
|
|
983
|
+
Edit `~/.config/opencode/package.json` and update the version:
|
|
984
|
+
|
|
985
|
+
```json
|
|
986
|
+
{
|
|
987
|
+
"dependencies": {
|
|
988
|
+
"@opencode-ai/plugin": "1.1.21",
|
|
989
|
+
"opencode-mattermost-control": "0.2.19" // <- Update this version
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
### Step 2: Clear the Cache
|
|
995
|
+
|
|
996
|
+
```bash
|
|
997
|
+
# Remove cached package and lockfile
|
|
998
|
+
rm -rf ~/.config/opencode/node_modules/opencode-mattermost-control
|
|
999
|
+
rm -f ~/.config/opencode/bun.lock
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
### Step 3: Install the New Version
|
|
1003
|
+
|
|
1004
|
+
```bash
|
|
1005
|
+
cd ~/.config/opencode
|
|
1006
|
+
bun install
|
|
1007
|
+
|
|
1008
|
+
# If using a private registry (e.g., Verdaccio):
|
|
1009
|
+
bun install --registry http://your-registry-url:4873
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
### Step 4: Restart OpenCode
|
|
1013
|
+
|
|
1014
|
+
**Critical**: You must completely restart OpenCode for the new plugin code to load. A `mattermost_disconnect` / `mattermost_connect` cycle only reconnects the WebSocket—it does **not** reload plugin code from disk.
|
|
1015
|
+
|
|
1016
|
+
```bash
|
|
1017
|
+
# Exit OpenCode completely (Ctrl+C or close terminal)
|
|
1018
|
+
# Then start fresh:
|
|
1019
|
+
opencode
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
### Step 5: Verify
|
|
1023
|
+
|
|
1024
|
+
After reconnecting, check the logs to confirm the new version is running:
|
|
1025
|
+
|
|
1026
|
+
```bash
|
|
1027
|
+
tail -f /tmp/opencode-mattermost-plugin.log
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
### Quick Update Script
|
|
1031
|
+
|
|
1032
|
+
```bash
|
|
1033
|
+
#!/bin/bash
|
|
1034
|
+
# update-mattermost-plugin.sh
|
|
1035
|
+
VERSION="${1:-latest}"
|
|
1036
|
+
|
|
1037
|
+
echo "Updating opencode-mattermost-control to $VERSION..."
|
|
1038
|
+
|
|
1039
|
+
# Update package.json
|
|
1040
|
+
if [ "$VERSION" = "latest" ]; then
|
|
1041
|
+
# Fetch latest version from registry
|
|
1042
|
+
VERSION=$(curl -s http://verdaccio.hyperplane-verdaccio.svc.cluster.local:4873/opencode-mattermost-control | jq -r '.["dist-tags"].latest')
|
|
1043
|
+
fi
|
|
1044
|
+
|
|
1045
|
+
# Update version in package.json
|
|
1046
|
+
cd ~/.config/opencode
|
|
1047
|
+
cat package.json | jq ".dependencies[\"opencode-mattermost-control\"] = \"$VERSION\"" > package.json.tmp
|
|
1048
|
+
mv package.json.tmp package.json
|
|
1049
|
+
|
|
1050
|
+
# Clear cache and reinstall
|
|
1051
|
+
rm -rf node_modules/opencode-mattermost-control bun.lock
|
|
1052
|
+
bun install --registry http://verdaccio.hyperplane-verdaccio.svc.cluster.local:4873
|
|
1053
|
+
|
|
1054
|
+
echo "Updated to version $VERSION"
|
|
1055
|
+
echo "IMPORTANT: Restart OpenCode for changes to take effect!"
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
---
|
|
1059
|
+
|
|
1060
|
+
## Troubleshooting
|
|
1061
|
+
|
|
1062
|
+
### "Not connected to Mattermost"
|
|
1063
|
+
Run `mattermost_connect` first.
|
|
1064
|
+
|
|
1065
|
+
### "MATTERMOST_TOKEN environment variable is required"
|
|
1066
|
+
Set the `MATTERMOST_TOKEN` environment variable with your bot's access token.
|
|
1067
|
+
|
|
1068
|
+
### WebSocket disconnects frequently
|
|
1069
|
+
Check network connectivity to the Mattermost server. The client auto-reconnects with exponential backoff.
|
|
1070
|
+
|
|
1071
|
+
### Messages not appearing
|
|
1072
|
+
Ensure you're DMing the bot user directly, not posting in a channel.
|
|
1073
|
+
|
|
1074
|
+
### Permission errors
|
|
1075
|
+
Verify your bot token has the required permissions:
|
|
1076
|
+
- Post messages
|
|
1077
|
+
- Read channels
|
|
1078
|
+
- Upload files (if using file attachments)
|
|
1079
|
+
|
|
1080
|
+
### View logs
|
|
1081
|
+
```bash
|
|
1082
|
+
tail -f /tmp/opencode-mattermost-plugin.log
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
### Plugin not updating after install
|
|
1086
|
+
If you installed a new version but the old code is still running:
|
|
1087
|
+
|
|
1088
|
+
1. OpenCode caches plugins in `~/.config/opencode/node_modules/`
|
|
1089
|
+
2. Check the cached version: `cat ~/.config/opencode/node_modules/opencode-mattermost-control/package.json | grep version`
|
|
1090
|
+
3. Follow the [Updating the Plugin](#updating-the-plugin) section above
|
|
1091
|
+
4. **You must restart OpenCode completely** - disconnect/reconnect only refreshes the WebSocket, not the plugin code
|
|
1092
|
+
|
|
1093
|
+
### Bot not responding to DMs
|
|
1094
|
+
|
|
1095
|
+
If your bot is connected but not responding to messages, check these common issues:
|
|
1096
|
+
|
|
1097
|
+
#### 1. Wrong Owner User ID
|
|
1098
|
+
|
|
1099
|
+
If you're using `MATTERMOST_OWNER_USER_ID` for multi-user setups, ensure it's set to your **actual Mattermost user ID**, not someone else's or a non-existent ID.
|
|
1100
|
+
|
|
1101
|
+
**Symptoms:**
|
|
1102
|
+
- Plugin log shows: `Ignoring 1:1 DM from non-owner user <your-user-id>`
|
|
1103
|
+
- Bot appears online but never responds
|
|
1104
|
+
|
|
1105
|
+
**Fix:**
|
|
1106
|
+
```bash
|
|
1107
|
+
# Find your user ID (ask admin or check Mattermost profile API)
|
|
1108
|
+
# Then update your environment:
|
|
1109
|
+
export MATTERMOST_OWNER_USER_ID="your-actual-user-id"
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
#### 2. OpenCode Version Compatibility
|
|
1113
|
+
|
|
1114
|
+
Some OpenCode versions have plugin loading issues in server mode. If plugins aren't loading:
|
|
1115
|
+
|
|
1116
|
+
**Symptoms:**
|
|
1117
|
+
- `opencode serve` starts but plugin log shows no activity
|
|
1118
|
+
- No "Connected to Mattermost" message in logs
|
|
1119
|
+
|
|
1120
|
+
**Fix:**
|
|
1121
|
+
Try downgrading to a known working version:
|
|
1122
|
+
```bash
|
|
1123
|
+
npm install -g opencode-ai@1.1.28
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
#### 3. Plugins Only Load When Session Created
|
|
1127
|
+
|
|
1128
|
+
**Critical:** Plugins don't load when `opencode serve` starts—they only initialize when a session is created.
|
|
1129
|
+
|
|
1130
|
+
**Symptoms:**
|
|
1131
|
+
- Server starts successfully
|
|
1132
|
+
- Plugin log is empty or shows no connection
|
|
1133
|
+
- Works fine with `opencode` (non-server mode)
|
|
1134
|
+
|
|
1135
|
+
**Fix:**
|
|
1136
|
+
After starting the server, create a session to bootstrap plugin loading:
|
|
1137
|
+
```bash
|
|
1138
|
+
# Start server
|
|
1139
|
+
opencode serve --port 4096
|
|
1140
|
+
|
|
1141
|
+
# In another terminal, create a session to trigger plugin load
|
|
1142
|
+
curl -s -X POST http://localhost:4096/session \
|
|
1143
|
+
-H "Content-Type: application/json" \
|
|
1144
|
+
-d '{"directory": "/path/to/your/project"}'
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
**Startup script example:**
|
|
1148
|
+
```bash
|
|
1149
|
+
#!/bin/bash
|
|
1150
|
+
# start-opencode-with-plugin.sh
|
|
1151
|
+
|
|
1152
|
+
PORT="${OPENCODE_SERVER_PORT:-4096}"
|
|
1153
|
+
|
|
1154
|
+
# Kill existing and start fresh
|
|
1155
|
+
pkill -f opencode 2>/dev/null || true
|
|
1156
|
+
sleep 2
|
|
1157
|
+
|
|
1158
|
+
# Start server
|
|
1159
|
+
cd /your/project/directory
|
|
1160
|
+
nohup opencode serve --port $PORT > /tmp/opencode-server.log 2>&1 &
|
|
1161
|
+
echo "Started OpenCode server on port $PORT"
|
|
1162
|
+
|
|
1163
|
+
# Wait for server
|
|
1164
|
+
for i in {1..30}; do
|
|
1165
|
+
curl -s http://localhost:$PORT/ > /dev/null 2>&1 && break
|
|
1166
|
+
sleep 1
|
|
1167
|
+
done
|
|
1168
|
+
|
|
1169
|
+
# Bootstrap plugins by creating a session
|
|
1170
|
+
curl -s -X POST http://localhost:$PORT/session \
|
|
1171
|
+
-H "Content-Type: application/json" \
|
|
1172
|
+
-d '{"directory": "/your/project/directory"}' > /dev/null 2>&1
|
|
1173
|
+
|
|
1174
|
+
echo "Created session to bootstrap plugins"
|
|
1175
|
+
|
|
1176
|
+
# Verify
|
|
1177
|
+
sleep 3
|
|
1178
|
+
if grep -q "Connected to Mattermost" /tmp/opencode-mattermost-plugin.log 2>/dev/null; then
|
|
1179
|
+
echo "✓ Plugin connected successfully!"
|
|
1180
|
+
else
|
|
1181
|
+
echo "⚠ Check /tmp/opencode-mattermost-plugin.log for issues"
|
|
1182
|
+
fi
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
#### 4. Missing Dependencies
|
|
1186
|
+
|
|
1187
|
+
If you see import errors in the plugin log:
|
|
1188
|
+
|
|
1189
|
+
**Fix:**
|
|
1190
|
+
```bash
|
|
1191
|
+
cd ~/.config/opencode
|
|
1192
|
+
bun add axios # or other missing packages
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
### Verifying Plugin is Working
|
|
1196
|
+
|
|
1197
|
+
Check these in order:
|
|
1198
|
+
|
|
1199
|
+
1. **Server running:**
|
|
1200
|
+
```bash
|
|
1201
|
+
curl http://localhost:4096/
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
2. **Plugin log exists and shows connection:**
|
|
1205
|
+
```bash
|
|
1206
|
+
tail /tmp/opencode-mattermost-plugin.log
|
|
1207
|
+
# Should show: "Connected to Mattermost as @your-bot"
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
3. **Sessions discovered:**
|
|
1211
|
+
```bash
|
|
1212
|
+
grep "session discovered" /tmp/opencode-mattermost-plugin.log
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
4. **Test message received:**
|
|
1216
|
+
Send a DM to the bot and check logs for:
|
|
1217
|
+
- `Ignoring 1:1 DM from non-owner` = Owner ID mismatch
|
|
1218
|
+
- `Processing DM from` = Message being handled
|
|
1219
|
+
- No log entry = WebSocket not receiving events
|
|
1220
|
+
|
|
1221
|
+
## Development
|
|
1222
|
+
|
|
1223
|
+
### Setup
|
|
1224
|
+
|
|
1225
|
+
```bash
|
|
1226
|
+
git clone https://github.com/Shakudo-io/opencode-mattermost-plugin.git
|
|
1227
|
+
cd opencode-mattermost-plugin
|
|
1228
|
+
bun install
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
### Type Check
|
|
1232
|
+
|
|
1233
|
+
```bash
|
|
1234
|
+
bun run typecheck
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
### Run Tests
|
|
1238
|
+
|
|
1239
|
+
```bash
|
|
1240
|
+
bun test
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
### Publish
|
|
1244
|
+
|
|
1245
|
+
```bash
|
|
1246
|
+
npm publish
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
## Contributing
|
|
1250
|
+
|
|
1251
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1252
|
+
|
|
1253
|
+
1. Fork the repository
|
|
1254
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
1255
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
1256
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1257
|
+
5. Open a Pull Request
|
|
1258
|
+
|
|
1259
|
+
### Guidelines
|
|
1260
|
+
|
|
1261
|
+
- Follow the existing code style
|
|
1262
|
+
- Add tests for new features
|
|
1263
|
+
- Update documentation as needed
|
|
1264
|
+
- Keep commits atomic and well-described
|
|
1265
|
+
|
|
1266
|
+
## Security
|
|
1267
|
+
|
|
1268
|
+
- Never commit tokens or credentials
|
|
1269
|
+
- Use environment variables for all sensitive configuration
|
|
1270
|
+
- Report security vulnerabilities privately
|
|
1271
|
+
|
|
1272
|
+
## License
|
|
1273
|
+
|
|
1274
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
1275
|
+
|
|
1276
|
+
## Links
|
|
1277
|
+
|
|
1278
|
+
- [OpenCode Documentation](https://opencode.ai/docs/)
|
|
1279
|
+
- [Mattermost API Reference](https://api.mattermost.com/)
|
|
1280
|
+
- [Report Issues](https://github.com/Shakudo-io/opencode-mattermost-plugin/issues)
|