agent-messenger 1.2.0 → 1.3.0

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.
Files changed (80) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +1 -1
  3. package/dist/package.json +3 -2
  4. package/dist/src/platforms/slackbot/cli.d.ts +5 -0
  5. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -0
  6. package/dist/src/platforms/slackbot/cli.js +19 -0
  7. package/dist/src/platforms/slackbot/cli.js.map +1 -0
  8. package/dist/src/platforms/slackbot/client.d.ts +43 -0
  9. package/dist/src/platforms/slackbot/client.d.ts.map +1 -0
  10. package/dist/src/platforms/slackbot/client.js +347 -0
  11. package/dist/src/platforms/slackbot/client.js.map +1 -0
  12. package/dist/src/platforms/slackbot/commands/auth.d.ts +35 -0
  13. package/dist/src/platforms/slackbot/commands/auth.d.ts.map +1 -0
  14. package/dist/src/platforms/slackbot/commands/auth.js +185 -0
  15. package/dist/src/platforms/slackbot/commands/auth.js.map +1 -0
  16. package/dist/src/platforms/slackbot/commands/channel.d.ts +3 -0
  17. package/dist/src/platforms/slackbot/commands/channel.d.ts.map +1 -0
  18. package/dist/src/platforms/slackbot/commands/channel.js +40 -0
  19. package/dist/src/platforms/slackbot/commands/channel.js.map +1 -0
  20. package/dist/src/platforms/slackbot/commands/index.d.ts +6 -0
  21. package/dist/src/platforms/slackbot/commands/index.d.ts.map +1 -0
  22. package/dist/src/platforms/slackbot/commands/index.js +6 -0
  23. package/dist/src/platforms/slackbot/commands/index.js.map +1 -0
  24. package/dist/src/platforms/slackbot/commands/message.d.ts +3 -0
  25. package/dist/src/platforms/slackbot/commands/message.d.ts.map +1 -0
  26. package/dist/src/platforms/slackbot/commands/message.js +135 -0
  27. package/dist/src/platforms/slackbot/commands/message.js.map +1 -0
  28. package/dist/src/platforms/slackbot/commands/reaction.d.ts +3 -0
  29. package/dist/src/platforms/slackbot/commands/reaction.d.ts.map +1 -0
  30. package/dist/src/platforms/slackbot/commands/reaction.js +43 -0
  31. package/dist/src/platforms/slackbot/commands/reaction.js.map +1 -0
  32. package/dist/src/platforms/slackbot/commands/shared.d.ts +9 -0
  33. package/dist/src/platforms/slackbot/commands/shared.d.ts.map +1 -0
  34. package/dist/src/platforms/slackbot/commands/shared.js +13 -0
  35. package/dist/src/platforms/slackbot/commands/shared.js.map +1 -0
  36. package/dist/src/platforms/slackbot/commands/user.d.ts +3 -0
  37. package/dist/src/platforms/slackbot/commands/user.d.ts.map +1 -0
  38. package/dist/src/platforms/slackbot/commands/user.js +40 -0
  39. package/dist/src/platforms/slackbot/commands/user.js.map +1 -0
  40. package/dist/src/platforms/slackbot/credential-manager.d.ts +18 -0
  41. package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -0
  42. package/dist/src/platforms/slackbot/credential-manager.js +187 -0
  43. package/dist/src/platforms/slackbot/credential-manager.js.map +1 -0
  44. package/dist/src/platforms/slackbot/index.d.ts +4 -0
  45. package/dist/src/platforms/slackbot/index.d.ts.map +1 -0
  46. package/dist/src/platforms/slackbot/index.js +4 -0
  47. package/dist/src/platforms/slackbot/index.js.map +1 -0
  48. package/dist/src/platforms/slackbot/types.d.ts +460 -0
  49. package/dist/src/platforms/slackbot/types.d.ts.map +1 -0
  50. package/dist/src/platforms/slackbot/types.js +114 -0
  51. package/dist/src/platforms/slackbot/types.js.map +1 -0
  52. package/docs/content/docs/integrations/meta.json +1 -1
  53. package/docs/content/docs/integrations/slackbot.mdx +204 -0
  54. package/docs/src/app/page.tsx +18 -1
  55. package/e2e/config.ts +26 -0
  56. package/e2e/helpers.ts +6 -1
  57. package/e2e/slackbot.e2e.test.ts +306 -0
  58. package/package.json +3 -2
  59. package/skills/agent-slackbot/SKILL.md +285 -0
  60. package/skills/agent-slackbot/references/authentication.md +253 -0
  61. package/skills/agent-slackbot/references/common-patterns.md +218 -0
  62. package/skills/agent-slackbot/templates/monitor-channel.sh +98 -0
  63. package/skills/agent-slackbot/templates/post-message.sh +107 -0
  64. package/skills/agent-slackbot/templates/workspace-summary.sh +113 -0
  65. package/src/platforms/slackbot/cli.ts +30 -0
  66. package/src/platforms/slackbot/client.test.ts +282 -0
  67. package/src/platforms/slackbot/client.ts +401 -0
  68. package/src/platforms/slackbot/commands/auth.test.ts +245 -0
  69. package/src/platforms/slackbot/commands/auth.ts +240 -0
  70. package/src/platforms/slackbot/commands/channel.ts +46 -0
  71. package/src/platforms/slackbot/commands/index.ts +5 -0
  72. package/src/platforms/slackbot/commands/message.ts +182 -0
  73. package/src/platforms/slackbot/commands/reaction.ts +59 -0
  74. package/src/platforms/slackbot/commands/shared.ts +23 -0
  75. package/src/platforms/slackbot/commands/user.ts +46 -0
  76. package/src/platforms/slackbot/credential-manager.test.ts +264 -0
  77. package/src/platforms/slackbot/credential-manager.ts +218 -0
  78. package/src/platforms/slackbot/index.ts +19 -0
  79. package/src/platforms/slackbot/types.test.ts +90 -0
  80. package/src/platforms/slackbot/types.ts +222 -0
@@ -0,0 +1,306 @@
1
+ import { describe, test, expect, beforeAll, afterEach } from 'bun:test'
2
+ import { runCLI, parseJSON, generateTestId, waitForRateLimit } from './helpers'
3
+ import {
4
+ SLACKBOT_TEST_CHANNEL_ID,
5
+ SLACKBOT_TEST_CHANNEL,
6
+ validateSlackBotEnvironment,
7
+ } from './config'
8
+ import { SlackBotClient } from '../src/platforms/slackbot/client'
9
+ import { SlackBotCredentialManager } from '../src/platforms/slackbot/credential-manager'
10
+
11
+ let testMessages: string[] = []
12
+ let cleanupClient: SlackBotClient
13
+
14
+ async function getClient(): Promise<SlackBotClient> {
15
+ const credManager = new SlackBotCredentialManager()
16
+ const creds = await credManager.getCredentials()
17
+ if (!creds) throw new Error('No slackbot credentials')
18
+ return new SlackBotClient(creds.token)
19
+ }
20
+
21
+ async function cleanupBotMessages(channel: string, timestamps: string[]) {
22
+ for (const ts of timestamps) {
23
+ try {
24
+ await cleanupClient.deleteMessage(channel, ts)
25
+ await waitForRateLimit(500)
26
+ } catch {
27
+ // best-effort cleanup
28
+ }
29
+ }
30
+ }
31
+
32
+ describe('SlackBot E2E Tests', () => {
33
+ beforeAll(async () => {
34
+ await validateSlackBotEnvironment()
35
+ cleanupClient = await getClient()
36
+
37
+ // Bot must be in the channel to send messages
38
+ try {
39
+ await cleanupClient.joinChannel(SLACKBOT_TEST_CHANNEL_ID)
40
+ } catch {
41
+ // already in channel
42
+ }
43
+ })
44
+
45
+ afterEach(async () => {
46
+ if (testMessages.length > 0) {
47
+ await cleanupBotMessages(SLACKBOT_TEST_CHANNEL_ID, testMessages)
48
+ testMessages = []
49
+ }
50
+ await waitForRateLimit()
51
+ })
52
+
53
+ describe('auth', () => {
54
+ test('auth status returns valid bot info', async () => {
55
+ const result = await runCLI('slackbot', ['auth', 'status'])
56
+ expect(result.exitCode).toBe(0)
57
+
58
+ const data = parseJSON<{
59
+ valid: boolean
60
+ workspace_id: string
61
+ workspace_name: string
62
+ bot_id: string
63
+ }>(result.stdout)
64
+ expect(data).not.toBeNull()
65
+ expect(data?.valid).toBe(true)
66
+ expect(data?.workspace_id).toBeTruthy()
67
+ expect(data?.workspace_name).toBeTruthy()
68
+ expect(data?.bot_id).toBeTruthy()
69
+ })
70
+ })
71
+
72
+ describe('message', () => {
73
+ test('message send creates message and returns ts', async () => {
74
+ const testId = generateTestId()
75
+ const result = await runCLI('slackbot', [
76
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Bot test ${testId}`,
77
+ ])
78
+ expect(result.exitCode).toBe(0)
79
+
80
+ const data = parseJSON<{ ts: string; channel: string }>(result.stdout)
81
+ expect(data?.ts).toBeTruthy()
82
+ expect(data?.channel).toBe(SLACKBOT_TEST_CHANNEL_ID)
83
+
84
+ if (data?.ts) testMessages.push(data.ts)
85
+ })
86
+
87
+ test('message list returns messages array', async () => {
88
+ const result = await runCLI('slackbot', [
89
+ 'message', 'list', SLACKBOT_TEST_CHANNEL_ID, '--limit', '5',
90
+ ])
91
+ expect(result.exitCode).toBe(0)
92
+
93
+ const data = parseJSON<Array<{ ts: string }>>(result.stdout)
94
+ expect(Array.isArray(data)).toBe(true)
95
+ })
96
+
97
+ test('message get retrieves specific message', async () => {
98
+ const testId = generateTestId()
99
+ const sendResult = await runCLI('slackbot', [
100
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Get test ${testId}`,
101
+ ])
102
+ const sent = parseJSON<{ ts: string }>(sendResult.stdout)
103
+ expect(sent?.ts).toBeTruthy()
104
+ if (sent?.ts) testMessages.push(sent.ts)
105
+
106
+ await waitForRateLimit()
107
+
108
+ const result = await runCLI('slackbot', [
109
+ 'message', 'get', SLACKBOT_TEST_CHANNEL_ID, sent!.ts,
110
+ ])
111
+ expect(result.exitCode).toBe(0)
112
+
113
+ const data = parseJSON<{ text: string; ts: string }>(result.stdout)
114
+ expect(data?.text).toContain(testId)
115
+ expect(data?.ts).toBe(sent!.ts)
116
+ })
117
+
118
+ test('message update modifies message', async () => {
119
+ const testId = generateTestId()
120
+ const sendResult = await runCLI('slackbot', [
121
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Original ${testId}`,
122
+ ])
123
+ const sent = parseJSON<{ ts: string }>(sendResult.stdout)
124
+ expect(sent?.ts).toBeTruthy()
125
+ if (sent?.ts) testMessages.push(sent.ts)
126
+
127
+ await waitForRateLimit()
128
+
129
+ const result = await runCLI('slackbot', [
130
+ 'message', 'update', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, `Updated ${testId}`,
131
+ ])
132
+ expect(result.exitCode).toBe(0)
133
+
134
+ await waitForRateLimit()
135
+
136
+ const getResult = await runCLI('slackbot', [
137
+ 'message', 'get', SLACKBOT_TEST_CHANNEL_ID, sent!.ts,
138
+ ])
139
+ const data = parseJSON<{ text: string }>(getResult.stdout)
140
+ expect(data?.text).toContain('Updated')
141
+ })
142
+
143
+ test('message delete removes message', async () => {
144
+ const testId = generateTestId()
145
+ const sendResult = await runCLI('slackbot', [
146
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Delete me ${testId}`,
147
+ ])
148
+ const sent = parseJSON<{ ts: string }>(sendResult.stdout)
149
+ expect(sent?.ts).toBeTruthy()
150
+
151
+ await waitForRateLimit()
152
+
153
+ const result = await runCLI('slackbot', [
154
+ 'message', 'delete', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, '--force',
155
+ ])
156
+ expect(result.exitCode).toBe(0)
157
+
158
+ const data = parseJSON<{ deleted: string }>(result.stdout)
159
+ expect(data?.deleted).toBe(sent!.ts)
160
+ })
161
+
162
+ test('message send with --thread creates reply', async () => {
163
+ const testId = generateTestId()
164
+ const sendResult = await runCLI('slackbot', [
165
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Parent ${testId}`,
166
+ ])
167
+ const parent = parseJSON<{ ts: string }>(sendResult.stdout)
168
+ expect(parent?.ts).toBeTruthy()
169
+ if (parent?.ts) testMessages.push(parent.ts)
170
+
171
+ await waitForRateLimit()
172
+
173
+ const replyResult = await runCLI('slackbot', [
174
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Reply ${testId}`,
175
+ '--thread', parent!.ts,
176
+ ])
177
+ expect(replyResult.exitCode).toBe(0)
178
+
179
+ const reply = parseJSON<{ ts: string; thread_ts: string }>(replyResult.stdout)
180
+ expect(reply?.thread_ts).toBe(parent!.ts)
181
+
182
+ if (reply?.ts) testMessages.push(reply.ts)
183
+ }, 30000)
184
+
185
+ test('message replies gets thread replies', async () => {
186
+ const testId = generateTestId()
187
+ const sendResult = await runCLI('slackbot', [
188
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Thread parent ${testId}`,
189
+ ])
190
+ const parent = parseJSON<{ ts: string }>(sendResult.stdout)
191
+ expect(parent?.ts).toBeTruthy()
192
+ if (parent?.ts) testMessages.push(parent.ts)
193
+
194
+ await waitForRateLimit()
195
+
196
+ const replyResult = await runCLI('slackbot', [
197
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Thread reply ${testId}`,
198
+ '--thread', parent!.ts,
199
+ ])
200
+ expect(replyResult.exitCode).toBe(0)
201
+ const reply = parseJSON<{ ts: string }>(replyResult.stdout)
202
+ if (reply?.ts) testMessages.push(reply.ts)
203
+
204
+ await waitForRateLimit()
205
+
206
+ const result = await runCLI('slackbot', [
207
+ 'message', 'replies', SLACKBOT_TEST_CHANNEL_ID, parent!.ts,
208
+ ])
209
+ expect(result.exitCode).toBe(0)
210
+
211
+ const data = parseJSON<Array<{ ts: string }>>(result.stdout)
212
+ expect(Array.isArray(data)).toBe(true)
213
+ expect(data!.length).toBeGreaterThanOrEqual(2)
214
+ })
215
+ })
216
+
217
+ describe('channel', () => {
218
+ test('channel list returns channels array', async () => {
219
+ const result = await runCLI('slackbot', ['channel', 'list', '--limit', '10'])
220
+ expect(result.exitCode).toBe(0)
221
+
222
+ const data = parseJSON<Array<{ id: string; name: string }>>(result.stdout)
223
+ expect(Array.isArray(data)).toBe(true)
224
+ expect(data!.length).toBeGreaterThan(0)
225
+ })
226
+
227
+ test('channel info returns channel details', async () => {
228
+ const result = await runCLI('slackbot', ['channel', 'info', SLACKBOT_TEST_CHANNEL_ID])
229
+ expect(result.exitCode).toBe(0)
230
+
231
+ const data = parseJSON<{ id: string; name: string }>(result.stdout)
232
+ expect(data?.id).toBe(SLACKBOT_TEST_CHANNEL_ID)
233
+ expect(data?.name).toBe(SLACKBOT_TEST_CHANNEL)
234
+ })
235
+ })
236
+
237
+ describe('user', () => {
238
+ test('user list returns users array', async () => {
239
+ const result = await runCLI('slackbot', ['user', 'list', '--limit', '10'])
240
+ expect(result.exitCode).toBe(0)
241
+
242
+ const data = parseJSON<Array<{ id: string; name: string }>>(result.stdout)
243
+ expect(Array.isArray(data)).toBe(true)
244
+ expect(data!.length).toBeGreaterThan(0)
245
+ })
246
+
247
+ test('user info returns user details', async () => {
248
+ // given: get the bot's own user_id via auth status
249
+ const statusResult = await runCLI('slackbot', ['auth', 'status'])
250
+ const status = parseJSON<{ bot_id: string; user: string }>(statusResult.stdout)
251
+ expect(status?.user).toBeTruthy()
252
+
253
+ await waitForRateLimit()
254
+
255
+ // when: list users and find the bot user
256
+ const listResult = await runCLI('slackbot', ['user', 'list', '--limit', '50'])
257
+ const users = parseJSON<Array<{ id: string; name: string }>>(listResult.stdout)
258
+ const botUser = users?.find((u) => u.name === status!.user)
259
+ expect(botUser).toBeTruthy()
260
+
261
+ await waitForRateLimit()
262
+
263
+ // then: user info returns matching data
264
+ const result = await runCLI('slackbot', ['user', 'info', botUser!.id])
265
+ expect(result.exitCode).toBe(0)
266
+
267
+ const data = parseJSON<{ id: string; name: string }>(result.stdout)
268
+ expect(data?.id).toBe(botUser!.id)
269
+ })
270
+ })
271
+
272
+ describe('reaction', () => {
273
+ test('reaction add and remove lifecycle', async () => {
274
+ // given: a message to react to
275
+ const testId = generateTestId()
276
+ const sendResult = await runCLI('slackbot', [
277
+ 'message', 'send', SLACKBOT_TEST_CHANNEL_ID, `Reaction test ${testId}`,
278
+ ])
279
+ const sent = parseJSON<{ ts: string }>(sendResult.stdout)
280
+ expect(sent?.ts).toBeTruthy()
281
+ if (sent?.ts) testMessages.push(sent.ts)
282
+
283
+ await waitForRateLimit(2000)
284
+
285
+ // when: add reaction
286
+ const addResult = await runCLI('slackbot', [
287
+ 'reaction', 'add', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, 'thumbsup',
288
+ ])
289
+ expect(addResult.exitCode).toBe(0)
290
+
291
+ const addData = parseJSON<{ success: boolean }>(addResult.stdout)
292
+ expect(addData?.success).toBe(true)
293
+
294
+ await waitForRateLimit(2000)
295
+
296
+ // then: remove reaction
297
+ const removeResult = await runCLI('slackbot', [
298
+ 'reaction', 'remove', SLACKBOT_TEST_CHANNEL_ID, sent!.ts, 'thumbsup',
299
+ ])
300
+ expect(removeResult.exitCode).toBe(0)
301
+
302
+ const removeData = parseJSON<{ success: boolean }>(removeResult.stdout)
303
+ expect(removeData?.success).toBe(true)
304
+ }, 15000)
305
+ })
306
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-messenger",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams)",
5
5
  "type": "module",
6
6
  "module": "dist/cli.js",
@@ -10,7 +10,8 @@
10
10
  "amsg": "./src/cli.ts",
11
11
  "agent-slack": "./src/platforms/slack/cli.ts",
12
12
  "agent-discord": "./src/platforms/discord/cli.ts",
13
- "agent-teams": "./src/platforms/teams/cli.ts"
13
+ "agent-teams": "./src/platforms/teams/cli.ts",
14
+ "agent-slackbot": "./src/platforms/slackbot/cli.ts"
14
15
  },
15
16
  "scripts": {
16
17
  "build": "tsc",
@@ -0,0 +1,285 @@
1
+ ---
2
+ name: agent-slackbot
3
+ description: Interact with Slack workspaces using bot tokens - send messages, read channels, manage reactions
4
+ allowed-tools: Bash(agent-slackbot:*)
5
+ ---
6
+
7
+ # Agent SlackBot
8
+
9
+ A TypeScript CLI tool that enables AI agents and humans to interact with Slack workspaces using bot tokens (xoxb-). Unlike agent-slack which extracts user tokens from the desktop app, agent-slackbot uses standard Slack Bot tokens for server-side and CI/CD integrations.
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Set your bot token
15
+ agent-slackbot auth set xoxb-your-bot-token
16
+
17
+ # Or set with a custom bot identifier for multi-bot setups
18
+ agent-slackbot auth set xoxb-your-bot-token --bot deploy --name "Deploy Bot"
19
+
20
+ # Verify authentication
21
+ agent-slackbot auth status
22
+
23
+ # Send a message
24
+ agent-slackbot message send C0ACZKTDDC0 "Hello from bot!"
25
+
26
+ # List channels
27
+ agent-slackbot channel list
28
+ ```
29
+
30
+ ## Authentication
31
+
32
+ ### Bot Token Setup
33
+
34
+ agent-slackbot uses Slack Bot tokens (xoxb-) which you get from the Slack App configuration:
35
+
36
+ ```bash
37
+ # Set bot token (validates against Slack API before saving)
38
+ agent-slackbot auth set xoxb-your-bot-token
39
+
40
+ # Set with a custom bot identifier
41
+ agent-slackbot auth set xoxb-your-bot-token --bot deploy --name "Deploy Bot"
42
+
43
+ # Check auth status
44
+ agent-slackbot auth status
45
+
46
+ # Clear stored credentials
47
+ agent-slackbot auth clear
48
+ ```
49
+
50
+ ### Multi-Bot Management
51
+
52
+ Store multiple bot tokens and switch between them:
53
+
54
+ ```bash
55
+ # Add multiple bots
56
+ agent-slackbot auth set xoxb-deploy-token --bot deploy --name "Deploy Bot"
57
+ agent-slackbot auth set xoxb-alert-token --bot alert --name "Alert Bot"
58
+
59
+ # List all stored bots
60
+ agent-slackbot auth list
61
+
62
+ # Switch active bot
63
+ agent-slackbot auth use deploy
64
+
65
+ # Use a specific bot for one command (without switching)
66
+ agent-slackbot message send C0ACZKTDDC0 "Alert!" --bot alert
67
+
68
+ # Remove a stored bot
69
+ agent-slackbot auth remove deploy
70
+
71
+ # Disambiguate bots with same ID across workspaces
72
+ agent-slackbot auth use T123456/deploy
73
+ ```
74
+
75
+ The `--bot <id>` flag is available on all commands to override the active bot for a single invocation.
76
+
77
+ ### Getting a Bot Token
78
+
79
+ 1. Go to [api.slack.com/apps](https://api.slack.com/apps)
80
+ 2. Create New App (or select existing)
81
+ 3. Go to **OAuth & Permissions**
82
+ 4. Add required bot token scopes (see below)
83
+ 5. Install app to workspace
84
+ 6. Copy the **Bot User OAuth Token** (starts with `xoxb-`)
85
+
86
+ ### Required Bot Token Scopes
87
+
88
+ | Scope | Used For |
89
+ |-------|----------|
90
+ | `chat:write` | Sending messages |
91
+ | `channels:history` | Reading public channel messages |
92
+ | `channels:read` | Listing public channels |
93
+ | `channels:join` | Joining public channels |
94
+ | `groups:history` | Reading private channel messages |
95
+ | `groups:read` | Listing private channels |
96
+ | `users:read` | Listing users |
97
+ | `users:read.email` | Reading user email addresses |
98
+ | `reactions:write` | Adding/removing reactions |
99
+ | `reactions:read` | Listing reactions |
100
+
101
+ ### Environment Variables (CI/CD)
102
+
103
+ For CI/CD pipelines, set these environment variables instead of using `auth set`:
104
+
105
+ ```bash
106
+ export E2E_SLACKBOT_TOKEN=xoxb-your-bot-token
107
+ export E2E_SLACKBOT_WORKSPACE_ID=T123456
108
+ export E2E_SLACKBOT_WORKSPACE_NAME="My Workspace"
109
+ ```
110
+
111
+ ## Commands
112
+
113
+ ### Message Commands
114
+
115
+ ```bash
116
+ # Send a message
117
+ agent-slackbot message send <channel> <text>
118
+ agent-slackbot message send C0ACZKTDDC0 "Hello world"
119
+
120
+ # Send a threaded reply
121
+ agent-slackbot message send C0ACZKTDDC0 "Reply" --thread <ts>
122
+
123
+ # List messages
124
+ agent-slackbot message list <channel>
125
+ agent-slackbot message list C0ACZKTDDC0 --limit 50
126
+
127
+ # Get a single message by timestamp
128
+ agent-slackbot message get <channel> <ts>
129
+
130
+ # Get thread replies (includes parent message)
131
+ agent-slackbot message replies <channel> <thread_ts>
132
+ agent-slackbot message replies C0ACZKTDDC0 1234567890.123456 --limit 50
133
+
134
+ # Update a message (bot's own messages only)
135
+ agent-slackbot message update <channel> <ts> <new-text>
136
+
137
+ # Delete a message (bot's own messages only)
138
+ agent-slackbot message delete <channel> <ts> --force
139
+ ```
140
+
141
+ ### Channel Commands
142
+
143
+ ```bash
144
+ # List channels the bot can see
145
+ agent-slackbot channel list
146
+ agent-slackbot channel list --limit 50
147
+
148
+ # Get channel info
149
+ agent-slackbot channel info <channel>
150
+ agent-slackbot channel info C0ACZKTDDC0
151
+ ```
152
+
153
+ ### User Commands
154
+
155
+ ```bash
156
+ # List users
157
+ agent-slackbot user list
158
+ agent-slackbot user list --limit 50
159
+
160
+ # Get user info
161
+ agent-slackbot user info <user-id>
162
+ ```
163
+
164
+ ### Reaction Commands
165
+
166
+ ```bash
167
+ # Add reaction
168
+ agent-slackbot reaction add <channel> <ts> <emoji>
169
+ agent-slackbot reaction add C0ACZKTDDC0 1234567890.123456 thumbsup
170
+
171
+ # Remove reaction
172
+ agent-slackbot reaction remove <channel> <ts> <emoji>
173
+ ```
174
+
175
+ ## Output Format
176
+
177
+ ### JSON (Default)
178
+
179
+ All commands output JSON by default for AI consumption:
180
+
181
+ ```json
182
+ {
183
+ "ts": "1234567890.123456",
184
+ "channel": "C0ACZKTDDC0",
185
+ "text": "Hello world"
186
+ }
187
+ ```
188
+
189
+ ### Pretty (Human-Readable)
190
+
191
+ Use `--pretty` flag for formatted output:
192
+
193
+ ```bash
194
+ agent-slackbot channel list --pretty
195
+ ```
196
+
197
+ ## Common Patterns
198
+
199
+ See `references/common-patterns.md` for typical AI agent workflows.
200
+
201
+ ## Templates
202
+
203
+ See `templates/` directory for runnable examples:
204
+ - `post-message.sh` - Send messages with error handling
205
+ - `monitor-channel.sh` - Monitor channel for new messages
206
+ - `workspace-summary.sh` - Generate workspace summary
207
+
208
+ ## Error Handling
209
+
210
+ All commands return consistent error format:
211
+
212
+ ```json
213
+ {
214
+ "error": "No credentials. Run \"auth set\" first."
215
+ }
216
+ ```
217
+
218
+ Common errors:
219
+ - `missing_token`: No credentials configured
220
+ - `invalid_token_type`: Token is not a bot token (must start with xoxb-)
221
+ - `not_in_channel`: Bot needs to join the channel first
222
+ - `slack_webapi_rate_limited_error`: Hit rate limit (auto-retries with backoff)
223
+
224
+ ## Configuration
225
+
226
+ Credentials stored in: `~/.config/agent-messenger/slackbot-credentials.json`
227
+
228
+ Format:
229
+ ```json
230
+ {
231
+ "current": {
232
+ "workspace_id": "T123456",
233
+ "bot_id": "deploy"
234
+ },
235
+ "workspaces": {
236
+ "T123456": {
237
+ "workspace_id": "T123456",
238
+ "workspace_name": "My Workspace",
239
+ "bots": {
240
+ "deploy": {
241
+ "bot_id": "deploy",
242
+ "bot_name": "Deploy Bot",
243
+ "token": "xoxb-..."
244
+ },
245
+ "alert": {
246
+ "bot_id": "alert",
247
+ "bot_name": "Alert Bot",
248
+ "token": "xoxb-..."
249
+ }
250
+ }
251
+ }
252
+ }
253
+ }
254
+ ```
255
+
256
+ **Security**: File permissions set to 0600 (owner read/write only)
257
+
258
+ ## Key Differences from agent-slack
259
+
260
+ | Feature | agent-slack | agent-slackbot |
261
+ |---------|------------|----------------|
262
+ | Token type | User token (xoxc-) | Bot token (xoxb-) |
263
+ | Token source | Auto-extracted from desktop app | Manual from Slack App config |
264
+ | Message search | Yes | No (requires user token) |
265
+ | File operations | Yes | No |
266
+ | Snapshot | Yes | No |
267
+ | Edit/delete messages | Any message | Bot's own messages only |
268
+ | Workspace management | Multi-workspace | Multi-bot, multi-workspace |
269
+ | CI/CD friendly | Requires desktop app | Yes (just set token) |
270
+
271
+ ## Limitations
272
+
273
+ - No real-time events / Socket Mode
274
+ - No message search (requires user token scope)
275
+ - No file upload/download
276
+ - No workspace snapshot
277
+ - Bot can only edit/delete its own messages
278
+ - Bot must be invited to private channels
279
+ - No scheduled messages
280
+ - Plain text messages only (no blocks/formatting)
281
+
282
+ ## References
283
+
284
+ - [Authentication Guide](references/authentication.md)
285
+ - [Common Patterns](references/common-patterns.md)