@buzzie-ai/slack-claude 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Buzzie AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # @buzzie-ai/slack-claude
2
+
3
+ Slack ↔ Claude Code bridge. Connects to Slack over Socket Mode, forwards messages to a persistent Claude Code session per thread (via [`@buzzie-ai/claude-inject`](https://www.npmjs.com/package/@buzzie-ai/claude-inject)), and posts the replies back into the same thread.
4
+
5
+ ```
6
+ ┌──────────┐ WebSocket ┌──────────────────┐ stdin/stdout ┌──────────────┐
7
+ │ Slack │ ◄──────────► │ slack-claude │ ◄────────────► │ claude -p │
8
+ │ events │ Socket Mode │ (this package) │ stream-json │ (logged in) │
9
+ └──────────┘ └──────────────────┘ └──────────────┘
10
+ ```
11
+
12
+ One Claude session per Slack thread (or per DM channel), kept alive across follow-up messages so the conversation has memory.
13
+
14
+ ## Quick start
15
+
16
+ Either export the tokens:
17
+
18
+ ```bash
19
+ export SLACK_APP_TOKEN=xapp-... # app-level token (Socket Mode)
20
+ export SLACK_BOT_TOKEN=xoxb-... # bot OAuth token
21
+
22
+ npx @buzzie-ai/slack-claude
23
+ ```
24
+
25
+ …or drop them in a `.env` file in your current directory (auto-loaded on
26
+ startup; real env vars win over file values):
27
+
28
+ ```bash
29
+ cp .env.example .env # then edit .env
30
+ npx @buzzie-ai/slack-claude
31
+ ```
32
+
33
+ Use `DOTENV_CONFIG_PATH=/path/to/file` to load from a non-default location.
34
+
35
+ That's it. The bot connects, prints `⚡️ slack-claude bot online …`, and starts handling:
36
+
37
+ - **@-mentions** in any channel it's been invited to
38
+ - **Direct messages** in DMs
39
+ - **Follow-up messages** in any thread it has already replied in (no re-mention needed)
40
+
41
+ ## Requirements
42
+
43
+ - **Node ≥ 20**
44
+ - **Claude Code installed and logged in.** The `claude` CLI must be on your `PATH` and already authenticated (`claude` should run interactively without prompting for login).
45
+ - **A Slack app** with Socket Mode enabled — see [Slack app setup](#slack-app-setup) below.
46
+
47
+ ## Slack app setup
48
+
49
+ One-time, ~3 minutes. From [api.slack.com/apps](https://api.slack.com/apps):
50
+
51
+ 1. **Create app** → "From scratch", pick a name and workspace.
52
+ 2. **Socket Mode** → toggle on. Generate an **app-level token** with the `connections:write` scope. Save the `xapp-…` token.
53
+ 3. **OAuth & Permissions** → add these **bot scopes**:
54
+ - `app_mentions:read`
55
+ - `chat:write`
56
+ - `channels:history`
57
+ - `groups:history`
58
+ - `im:history`
59
+ - `mpim:history`
60
+ - `im:read`
61
+ - `reactions:read`
62
+ - `reactions:write`
63
+ 4. **Event Subscriptions** → Enable Events. Subscribe to bot events:
64
+ - `app_mention`
65
+ - `message.channels`
66
+ - `message.groups`
67
+ - `message.im`
68
+ - `message.mpim`
69
+ 5. **Install to workspace** → grab the **bot token** (`xoxb-…`).
70
+ 6. Invite the bot to a channel (`/invite @your-bot`) or DM it.
71
+
72
+ ## Usage
73
+
74
+ ### CLI
75
+
76
+ ```bash
77
+ npx @buzzie-ai/slack-claude [options]
78
+ ```
79
+
80
+ Options:
81
+
82
+ | Flag | Env | Default | Notes |
83
+ |---|---|---|---|
84
+ | `--app-token <xapp-…>` | `SLACK_APP_TOKEN` | *(required)* | App-level token for Socket Mode |
85
+ | `--bot-token <xoxb-…>` | `SLACK_BOT_TOKEN` | *(required)* | Bot OAuth token |
86
+ | `--cwd <path>` | | `process.cwd()` | Working directory passed to each Claude session |
87
+ | `--system-prompt <text>` | | sensible default | Sent on first message of every new session |
88
+ | `--model <id\|alias>` | | | `opus`, `sonnet`, `haiku`, or a full model ID |
89
+ | `--permission-mode <mode>` | | | `acceptEdits` \| `auto` \| `bypassPermissions` \| `default` \| `dontAsk` \| `plan` |
90
+ | `--dangerously-skip-permissions` | | | Bypass all Claude permission prompts |
91
+ | `--tools <spec>` | | | `""` (none), `"default"` (all), or `"Bash,Edit,Read"` |
92
+ | `--log-level <level>` | | `info` | `debug` \| `info` \| `warn` \| `error` |
93
+ | `-h, --help` | | | Print help |
94
+ | `-v, --version` | | | Print version |
95
+
96
+ ### Programmatic
97
+
98
+ ```ts
99
+ import { SlackClaudeBot } from '@buzzie-ai/slack-claude';
100
+
101
+ const bot = new SlackClaudeBot({
102
+ appToken: process.env.SLACK_APP_TOKEN!,
103
+ botToken: process.env.SLACK_BOT_TOKEN!,
104
+ cwd: '/path/to/repo',
105
+ systemPrompt: 'You are a code review bot. Be terse.',
106
+ claudeOptions: {
107
+ model: 'sonnet',
108
+ dangerouslySkipPermissions: true,
109
+ },
110
+ });
111
+
112
+ await bot.start();
113
+ // ... later
114
+ await bot.stop();
115
+ ```
116
+
117
+ ## How it works
118
+
119
+ - Bolt opens a **Socket Mode** WebSocket — no public HTTP endpoint needed.
120
+ - For each incoming Slack event, the bot computes a **thread key**:
121
+ - DM at top level → `dm:<channel>`
122
+ - Anything threaded → `<channel>:<thread_ts>`
123
+ - New @-mention → `<channel>:<message_ts>` (the reply opens the thread)
124
+ - A `ClaudeSession` (from `@buzzie-ai/claude-inject`) is created on first hit and cached under that key. Follow-up messages reuse the same persistent `claude -p` subprocess, so context carries.
125
+ - The bot adds a 👀 reaction on receipt, calls `session.send(text)`, then posts the full reply in the matching thread and removes the reaction.
126
+
127
+ ## Limits & gotchas
128
+
129
+ - **One in-flight turn per thread.** `ClaudeSession.send()` queues; if a user fires three messages in quick succession to the same thread, Claude answers them in order, not in parallel.
130
+ - **Sessions live in memory.** Restarting the process resets all conversations.
131
+ - **Slack message size cap is 40 000 chars.** Very long Claude replies will be truncated by Slack; chunking isn't done yet.
132
+ - **No streaming yet.** Replies post once Claude finishes the turn. (Open issue: stream-and-edit mode.)
133
+ - **The `claude` CLI must already be logged in.** This bridge does not handle auth.
134
+
135
+ ## License
136
+
137
+ MIT
package/dist/bot.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ import { LogLevel } from '@slack/bolt';
2
+ import { type ClaudeSessionOptions } from '@buzzie-ai/claude-inject';
3
+ export interface SlackClaudeBotOptions {
4
+ /** Slack app-level token (xapp-...). Required for Socket Mode. */
5
+ appToken: string;
6
+ /** Slack bot OAuth token (xoxb-...). */
7
+ botToken: string;
8
+ /**
9
+ * Options forwarded to every ClaudeSession spawned by the bot.
10
+ * `cwd`, `systemPrompt`, `model`, `dangerouslySkipPermissions`, etc.
11
+ * `cwd` defaults to process.cwd().
12
+ */
13
+ claudeOptions?: ClaudeSessionOptions;
14
+ /** Override the working dir passed to ClaudeSession. Convenience for the CLI. */
15
+ cwd?: string;
16
+ /** Override the system prompt. Convenience for the CLI. */
17
+ systemPrompt?: string;
18
+ /** Bolt log level. Defaults to INFO. */
19
+ logLevel?: LogLevel;
20
+ }
21
+ /**
22
+ * Bridges a Slack workspace to Claude Code over Socket Mode.
23
+ *
24
+ * One ClaudeSession is spawned per Slack thread (or per DM channel for non-threaded DMs)
25
+ * and kept alive across follow-up messages so context is preserved.
26
+ */
27
+ export declare class SlackClaudeBot {
28
+ private readonly app;
29
+ private readonly opts;
30
+ private readonly sessions;
31
+ private botUserId;
32
+ private started;
33
+ constructor(opts: SlackClaudeBotOptions);
34
+ /** Connect to Slack and start receiving events. Resolves once the socket is up. */
35
+ start(): Promise<void>;
36
+ /** Disconnect from Slack and close every Claude session. */
37
+ stop(): Promise<void>;
38
+ private registerHandlers;
39
+ private handleIncoming;
40
+ private createSession;
41
+ private threadKey;
42
+ /** Strip leading bot mention and trim. Slack delivers mentions as `<@U12345>`. */
43
+ private cleanText;
44
+ private swapReaction;
45
+ private postError;
46
+ }
47
+ //# sourceMappingURL=bot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,QAAQ,EAAuB,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAIpF,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAOD;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,qBAAqB;IAevC,mFAAmF;IAC7E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAc5B,4DAA4D;IACtD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,OAAO,CAAC,gBAAgB;YA2DV,cAAc;IAgF5B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,SAAS;IAIjB,kFAAkF;IAClF,OAAO,CAAC,SAAS;YASH,YAAY;YAWZ,SAAS;CAgBxB"}
package/dist/bot.js ADDED
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SlackClaudeBot = void 0;
4
+ const bolt_1 = require("@slack/bolt");
5
+ const claude_inject_1 = require("@buzzie-ai/claude-inject");
6
+ const DEFAULT_SYSTEM_PROMPT = 'You are a helpful AI assistant talking to people through Slack. ' +
7
+ 'Be concise and clear. Use Slack-flavoured markdown (bold with *one star*, italic with _underscore_, code with `backticks`, code blocks with ```). ' +
8
+ 'Do not invent links or @-mentions.';
9
+ /**
10
+ * Bridges a Slack workspace to Claude Code over Socket Mode.
11
+ *
12
+ * One ClaudeSession is spawned per Slack thread (or per DM channel for non-threaded DMs)
13
+ * and kept alive across follow-up messages so context is preserved.
14
+ */
15
+ class SlackClaudeBot {
16
+ app;
17
+ opts;
18
+ sessions = new Map();
19
+ botUserId = null;
20
+ started = false;
21
+ constructor(opts) {
22
+ if (!opts.appToken)
23
+ throw new Error('SlackClaudeBot: appToken is required (xapp-...)');
24
+ if (!opts.botToken)
25
+ throw new Error('SlackClaudeBot: botToken is required (xoxb-...)');
26
+ this.opts = opts;
27
+ this.app = new bolt_1.App({
28
+ token: opts.botToken,
29
+ appToken: opts.appToken,
30
+ socketMode: true,
31
+ logLevel: opts.logLevel ?? bolt_1.LogLevel.INFO,
32
+ });
33
+ this.registerHandlers();
34
+ }
35
+ /** Connect to Slack and start receiving events. Resolves once the socket is up. */
36
+ async start() {
37
+ if (this.started)
38
+ return;
39
+ await this.app.start();
40
+ // Resolve the bot's user ID so we can strip self-mentions and ignore our own posts.
41
+ const auth = await this.app.client.auth.test({ token: this.opts.botToken });
42
+ this.botUserId = auth.user_id ?? null;
43
+ this.started = true;
44
+ console.log(`⚡️ slack-claude bot online as ${auth.user ?? 'unknown'} (${this.botUserId ?? '?'}) in workspace ${auth.team ?? '?'}`);
45
+ }
46
+ /** Disconnect from Slack and close every Claude session. */
47
+ async stop() {
48
+ const closing = Array.from(this.sessions.values()).map((s) => s.close().catch((err) => console.error('Error closing Claude session:', err)));
49
+ this.sessions.clear();
50
+ await Promise.all(closing);
51
+ if (this.started) {
52
+ await this.app.stop();
53
+ this.started = false;
54
+ }
55
+ }
56
+ // ─────────────────────────────────────────────────────────────────────────
57
+ // internals
58
+ // ─────────────────────────────────────────────────────────────────────────
59
+ registerHandlers() {
60
+ // Direct @-mentions in channels the bot is invited to.
61
+ this.app.event('app_mention', async ({ event, client }) => {
62
+ try {
63
+ await this.handleIncoming({
64
+ channel: event.channel,
65
+ ts: event.ts,
66
+ threadTs: event.thread_ts,
67
+ text: event.text ?? '',
68
+ user: event.user,
69
+ channelType: 'channel',
70
+ client,
71
+ });
72
+ }
73
+ catch (err) {
74
+ console.error('app_mention handler error:', err);
75
+ }
76
+ });
77
+ // Every message event: DMs, threads, channels.
78
+ // We filter inside the handler to decide whether to actually respond.
79
+ this.app.message(async ({ message, client }) => {
80
+ try {
81
+ // Skip non-user messages (edits, joins, our own bot posts, etc.).
82
+ if (message.subtype !== undefined)
83
+ return;
84
+ const m = message;
85
+ if (m.bot_id)
86
+ return;
87
+ if (this.botUserId && m.user === this.botUserId)
88
+ return;
89
+ const isDm = m.channel_type === 'im';
90
+ const inExistingThread = m.thread_ts !== undefined && this.sessions.has(this.threadKey(m.channel, m.thread_ts, false));
91
+ const mentionsBot = this.botUserId !== undefined && this.botUserId !== null && (m.text ?? '').includes(`<@${this.botUserId}>`);
92
+ // Skip messages that app_mention will already handle.
93
+ if (mentionsBot)
94
+ return;
95
+ // Outside DMs and existing threads, ignore.
96
+ if (!isDm && !inExistingThread)
97
+ return;
98
+ await this.handleIncoming({
99
+ channel: m.channel,
100
+ ts: m.ts,
101
+ threadTs: m.thread_ts,
102
+ text: m.text ?? '',
103
+ user: m.user,
104
+ channelType: isDm ? 'im' : 'channel',
105
+ client,
106
+ });
107
+ }
108
+ catch (err) {
109
+ console.error('message handler error:', err);
110
+ }
111
+ });
112
+ this.app.error(async (err) => {
113
+ console.error('Bolt error:', err);
114
+ });
115
+ }
116
+ async handleIncoming(args) {
117
+ const { channel, ts, threadTs, text, channelType, client } = args;
118
+ const isDm = channelType === 'im';
119
+ const replyThreadTs = threadTs ?? (isDm ? undefined : ts);
120
+ const sessionKey = this.threadKey(channel, threadTs ?? ts, isDm && !threadTs);
121
+ const cleaned = this.cleanText(text);
122
+ if (!cleaned) {
123
+ // Empty after stripping mention — nothing to ask Claude.
124
+ return;
125
+ }
126
+ // 👀 reaction so the user knows we received it.
127
+ let reactionAdded = false;
128
+ try {
129
+ await client.reactions.add({ channel, timestamp: ts, name: 'eyes' });
130
+ reactionAdded = true;
131
+ }
132
+ catch {
133
+ // Ignore — we may not have permission, the message may be deleted, or we
134
+ // already reacted. Not worth failing the whole turn.
135
+ }
136
+ let session = this.sessions.get(sessionKey);
137
+ let isNewSession = false;
138
+ if (!session) {
139
+ session = this.createSession();
140
+ this.sessions.set(sessionKey, session);
141
+ isNewSession = true;
142
+ try {
143
+ await session.start();
144
+ }
145
+ catch (err) {
146
+ this.sessions.delete(sessionKey);
147
+ await this.postError(client, channel, replyThreadTs, err);
148
+ if (reactionAdded)
149
+ await this.swapReaction(client, channel, ts, 'eyes', 'x');
150
+ return;
151
+ }
152
+ }
153
+ let reply;
154
+ try {
155
+ reply = await session.send(cleaned);
156
+ }
157
+ catch (err) {
158
+ // If the very first send fails, the subprocess is likely dead — drop the session
159
+ // so the next message gets a fresh one.
160
+ if (isNewSession) {
161
+ this.sessions.delete(sessionKey);
162
+ await session.close().catch(() => { });
163
+ }
164
+ await this.postError(client, channel, replyThreadTs, err);
165
+ if (reactionAdded)
166
+ await this.swapReaction(client, channel, ts, 'eyes', 'x');
167
+ return;
168
+ }
169
+ const text_out = reply.trim() || '_(Claude returned an empty response.)_';
170
+ try {
171
+ await client.chat.postMessage({
172
+ channel,
173
+ thread_ts: replyThreadTs,
174
+ text: text_out,
175
+ });
176
+ }
177
+ catch (err) {
178
+ console.error('chat.postMessage failed:', err);
179
+ }
180
+ finally {
181
+ if (reactionAdded) {
182
+ await client.reactions
183
+ .remove({ channel, timestamp: ts, name: 'eyes' })
184
+ .catch(() => { });
185
+ }
186
+ }
187
+ }
188
+ createSession() {
189
+ const co = this.opts.claudeOptions ?? {};
190
+ return new claude_inject_1.ClaudeSession({
191
+ cwd: this.opts.cwd ?? co.cwd ?? process.cwd(),
192
+ systemPrompt: this.opts.systemPrompt ?? co.systemPrompt ?? DEFAULT_SYSTEM_PROMPT,
193
+ ...co,
194
+ });
195
+ }
196
+ threadKey(channel, anchor, isDmRoot) {
197
+ return isDmRoot ? `dm:${channel}` : `${channel}:${anchor}`;
198
+ }
199
+ /** Strip leading bot mention and trim. Slack delivers mentions as `<@U12345>`. */
200
+ cleanText(text) {
201
+ let out = text;
202
+ if (this.botUserId) {
203
+ const mention = new RegExp(`<@${this.botUserId}>`, 'g');
204
+ out = out.replace(mention, '');
205
+ }
206
+ return out.trim();
207
+ }
208
+ async swapReaction(client, channel, ts, from, to) {
209
+ await client.reactions.remove({ channel, timestamp: ts, name: from }).catch(() => { });
210
+ await client.reactions.add({ channel, timestamp: ts, name: to }).catch(() => { });
211
+ }
212
+ async postError(client, channel, thread_ts, err) {
213
+ const msg = err instanceof Error ? err.message : String(err);
214
+ console.error('Claude turn failed:', err);
215
+ await client.chat
216
+ .postMessage({
217
+ channel,
218
+ thread_ts,
219
+ text: `:warning: Claude failed to respond: \`${msg}\``,
220
+ })
221
+ .catch((postErr) => console.error('Failed to post error message:', postErr));
222
+ }
223
+ }
224
+ exports.SlackClaudeBot = SlackClaudeBot;
225
+ //# sourceMappingURL=bot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":";;;AAAA,sCAAiE;AAEjE,4DAAoF;AAuBpF,MAAM,qBAAqB,GACzB,kEAAkE;IAClE,oJAAoJ;IACpJ,oCAAoC,CAAC;AAEvC;;;;;GAKG;AACH,MAAa,cAAc;IACR,GAAG,CAAM;IACT,IAAI,CAAwB;IAC5B,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IACrD,SAAS,GAAkB,IAAI,CAAC;IAChC,OAAO,GAAG,KAAK,CAAC;IAExB,YAAY,IAA2B;QACrC,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACvF,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACvF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAEjB,IAAI,CAAC,GAAG,GAAG,IAAI,UAAG,CAAC;YACjB,KAAK,EAAE,IAAI,CAAC,QAAQ;YACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI;YAChB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,eAAQ,CAAC,IAAI;SACzC,CAAC,CAAC;QAEH,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAED,mFAAmF;IACnF,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QAEvB,oFAAoF;QACpF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,SAAS,GAAI,IAAI,CAAC,OAA8B,IAAI,IAAI,CAAC;QAE9D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,CAAC,GAAG,CACT,iCAAiC,IAAI,CAAC,IAAI,IAAI,SAAS,KAAK,IAAI,CAAC,SAAS,IAAI,GAAG,kBAAkB,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CACtH,CAAC;IACJ,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,IAAI;QACR,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3D,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC,CAC9E,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,YAAY;IACZ,4EAA4E;IAEpE,gBAAgB;QACtB,uDAAuD;QACvD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;YACxD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,cAAc,CAAC;oBACxB,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,EAAE,EAAE,KAAK,CAAC,EAAE;oBACZ,QAAQ,EAAE,KAAK,CAAC,SAAS;oBACzB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;oBACtB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,WAAW,EAAE,SAAS;oBACtB,MAAM;iBACP,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,+CAA+C;QAC/C,sEAAsE;QACtE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE;YAC7C,IAAI,CAAC;gBACH,kEAAkE;gBAClE,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;oBAAE,OAAO;gBAC1C,MAAM,CAAC,GAAG,OAA8B,CAAC;gBAEzC,IAAI,CAAC,CAAC,MAAM;oBAAE,OAAO;gBACrB,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,SAAS;oBAAE,OAAO;gBAExD,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,KAAK,IAAI,CAAC;gBACrC,MAAM,gBAAgB,GACpB,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;gBAChG,MAAM,WAAW,GACf,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;gBAE7G,sDAAsD;gBACtD,IAAI,WAAW;oBAAE,OAAO;gBACxB,4CAA4C;gBAC5C,IAAI,CAAC,IAAI,IAAI,CAAC,gBAAgB;oBAAE,OAAO;gBAEvC,MAAM,IAAI,CAAC,cAAc,CAAC;oBACxB,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,QAAQ,EAAE,CAAC,CAAC,SAAS;oBACrB,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE;oBAClB,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;oBACpC,MAAM;iBACP,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC3B,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,IAQ5B;QACC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAElE,MAAM,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC;QAClC,MAAM,aAAa,GAAG,QAAQ,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE9E,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,yDAAyD;YACzD,OAAO;QACT,CAAC;QAED,gDAAgD;QAChD,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACrE,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,qDAAqD;QACvD,CAAC;QAED,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,YAAY,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACvC,YAAY,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACxB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBACjC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;gBAC1D,IAAI,aAAa;oBAAE,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;gBAC7E,OAAO;YACT,CAAC;QACH,CAAC;QAED,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iFAAiF;YACjF,wCAAwC;YACxC,IAAI,YAAY,EAAE,CAAC;gBACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBACjC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACxC,CAAC;YACD,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;YAC1D,IAAI,aAAa;gBAAE,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YAC7E,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,wCAAwC,CAAC;QAC1E,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;gBAC5B,OAAO;gBACP,SAAS,EAAE,aAAa;gBACxB,IAAI,EAAE,QAAQ;aACf,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC;gBAAS,CAAC;YACT,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,MAAM,CAAC,SAAS;qBACnB,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;qBAChD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;QACzC,OAAO,IAAI,6BAAa,CAAC;YACvB,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;YAC7C,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,YAAY,IAAI,qBAAqB;YAChF,GAAG,EAAE;SACN,CAAC,CAAC;IACL,CAAC;IAEO,SAAS,CAAC,OAAe,EAAE,MAAc,EAAE,QAAiB;QAClE,OAAO,QAAQ,CAAC,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC;IAC7D,CAAC;IAED,kFAAkF;IAC1E,SAAS,CAAC,IAAY;QAC5B,IAAI,GAAG,GAAG,IAAI,CAAC;QACf,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;YACxD,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAEO,KAAK,CAAC,YAAY,CACxB,MAAiB,EACjB,OAAe,EACf,EAAU,EACV,IAAY,EACZ,EAAU;QAEV,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtF,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACnF,CAAC;IAEO,KAAK,CAAC,SAAS,CACrB,MAAiB,EACjB,OAAe,EACf,SAA6B,EAC7B,GAAY;QAEZ,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;QAC1C,MAAM,MAAM,CAAC,IAAI;aACd,WAAW,CAAC;YACX,OAAO;YACP,SAAS;YACT,IAAI,EAAE,yCAAyC,GAAG,IAAI;SACvD,CAAC;aACD,KAAK,CAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1F,CAAC;CACF;AAnPD,wCAmPC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("node:fs"));
38
+ const path = __importStar(require("node:path"));
39
+ const bolt_1 = require("@slack/bolt");
40
+ const bot_1 = require("./bot");
41
+ /**
42
+ * Auto-load `.env` from the cwd before reading process.env.
43
+ *
44
+ * Uses Node's built-in `process.loadEnvFile()` (stable in 20.12+; experimental
45
+ * in 20.6+). Falls back to a tiny manual parser on older Node so we don't have
46
+ * to bump the engines field or pull in dotenv.
47
+ *
48
+ * Honours $DOTENV_CONFIG_PATH if set, otherwise looks for `<cwd>/.env`.
49
+ * Real environment variables always win over the file (we never overwrite).
50
+ */
51
+ function loadEnvFileIfPresent() {
52
+ const envPath = process.env.DOTENV_CONFIG_PATH ?? path.resolve(process.cwd(), '.env');
53
+ if (!fs.existsSync(envPath))
54
+ return;
55
+ const loader = process.loadEnvFile;
56
+ if (typeof loader === 'function') {
57
+ try {
58
+ loader(envPath);
59
+ return;
60
+ }
61
+ catch {
62
+ // Fall through to the manual parser.
63
+ }
64
+ }
65
+ // Minimal KEY=VALUE parser for older Node. Skips comments and blanks; does
66
+ // not handle multi-line values or shell expansion — keep .env files simple.
67
+ const text = fs.readFileSync(envPath, 'utf8');
68
+ for (const rawLine of text.split(/\r?\n/)) {
69
+ const line = rawLine.trim();
70
+ if (!line || line.startsWith('#'))
71
+ continue;
72
+ const eq = line.indexOf('=');
73
+ if (eq < 0)
74
+ continue;
75
+ const key = line.slice(0, eq).trim();
76
+ if (!key || key in process.env)
77
+ continue;
78
+ let value = line.slice(eq + 1).trim();
79
+ if ((value.startsWith('"') && value.endsWith('"')) ||
80
+ (value.startsWith("'") && value.endsWith("'"))) {
81
+ value = value.slice(1, -1);
82
+ }
83
+ process.env[key] = value;
84
+ }
85
+ }
86
+ const HELP = `
87
+ slack-claude — Slack ↔ Claude Code bridge (Socket Mode)
88
+
89
+ Usage:
90
+ npx @buzzie-ai/slack-claude [options]
91
+
92
+ Required (env, .env file, or flag):
93
+ SLACK_APP_TOKEN, --app-token <xapp-...> Slack app-level token (Socket Mode)
94
+ SLACK_BOT_TOKEN, --bot-token <xoxb-...> Slack bot OAuth token
95
+
96
+ A .env file in the current directory is loaded automatically. Real environment
97
+ variables take precedence over .env values. Set DOTENV_CONFIG_PATH to point
98
+ at a different file.
99
+
100
+ Optional:
101
+ --cwd <path> Working directory for Claude Code (default: cwd)
102
+ --system-prompt <text> System prompt sent to each new Claude session
103
+ --model <id|alias> Claude model: opus | sonnet | haiku | full ID
104
+ --permission-mode <mode> acceptEdits | auto | bypassPermissions | default | dontAsk | plan
105
+ --dangerously-skip-permissions
106
+ Skip permission prompts in Claude Code
107
+ --tools <spec> "" (none), "default" (all), or "Bash,Edit,Read"
108
+ --log-level <level> debug | info | warn | error (default: info)
109
+ -h, --help Show this help
110
+ -v, --version Show version
111
+
112
+ Slack app setup (one-time):
113
+ 1. Create a Slack app at https://api.slack.com/apps
114
+ 2. Enable Socket Mode → generate an app-level token (xapp-...) with connections:write
115
+ 3. OAuth & Permissions → bot scopes:
116
+ app_mentions:read, chat:write, channels:history, groups:history,
117
+ im:history, mpim:history, im:read, reactions:read, reactions:write
118
+ 4. Event Subscriptions → subscribe to bot events:
119
+ app_mention, message.channels, message.groups, message.im, message.mpim
120
+ 5. Install the app to your workspace, copy the bot token (xoxb-...)
121
+ 6. Export tokens, then: npx @buzzie-ai/slack-claude
122
+
123
+ Claude Code must already be installed and authenticated on this machine
124
+ (the \`claude\` CLI must be on PATH).
125
+ `.trim();
126
+ function parseArgs(argv) {
127
+ const out = {};
128
+ for (let i = 0; i < argv.length; i++) {
129
+ const a = argv[i];
130
+ const next = () => {
131
+ const v = argv[++i];
132
+ if (v === undefined) {
133
+ console.error(`Missing value for ${a}`);
134
+ process.exit(2);
135
+ }
136
+ return v;
137
+ };
138
+ switch (a) {
139
+ case '-h':
140
+ case '--help':
141
+ return { help: true };
142
+ case '-v':
143
+ case '--version':
144
+ return { version: true };
145
+ case '--app-token':
146
+ out.appToken = next();
147
+ break;
148
+ case '--bot-token':
149
+ out.botToken = next();
150
+ break;
151
+ case '--cwd':
152
+ out.cwd = next();
153
+ break;
154
+ case '--system-prompt':
155
+ out.systemPrompt = next();
156
+ break;
157
+ case '--model':
158
+ out.model = next();
159
+ break;
160
+ case '--permission-mode':
161
+ out.permissionMode = next();
162
+ break;
163
+ case '--dangerously-skip-permissions':
164
+ out.dangerouslySkipPermissions = true;
165
+ break;
166
+ case '--tools':
167
+ out.tools = next();
168
+ break;
169
+ case '--log-level':
170
+ out.logLevel = next();
171
+ break;
172
+ default:
173
+ console.error(`Unknown option: ${a}`);
174
+ console.error('Run with --help for usage.');
175
+ process.exit(2);
176
+ }
177
+ }
178
+ return out;
179
+ }
180
+ function resolveLogLevel(s) {
181
+ if (!s)
182
+ return undefined;
183
+ switch (s.toLowerCase()) {
184
+ case 'debug':
185
+ return bolt_1.LogLevel.DEBUG;
186
+ case 'info':
187
+ return bolt_1.LogLevel.INFO;
188
+ case 'warn':
189
+ case 'warning':
190
+ return bolt_1.LogLevel.WARN;
191
+ case 'error':
192
+ return bolt_1.LogLevel.ERROR;
193
+ default:
194
+ console.error(`Unknown log level: ${s}`);
195
+ process.exit(2);
196
+ }
197
+ }
198
+ async function main() {
199
+ loadEnvFileIfPresent();
200
+ const parsed = parseArgs(process.argv.slice(2));
201
+ if ('help' in parsed) {
202
+ console.log(HELP);
203
+ return;
204
+ }
205
+ if ('version' in parsed) {
206
+ // Read package.json without bundling it into the build output.
207
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
208
+ const pkg = require('../package.json');
209
+ console.log(pkg.version);
210
+ return;
211
+ }
212
+ const appToken = parsed.appToken ?? process.env.SLACK_APP_TOKEN;
213
+ const botToken = parsed.botToken ?? process.env.SLACK_BOT_TOKEN;
214
+ if (!appToken || !botToken) {
215
+ console.error('Error: SLACK_APP_TOKEN and SLACK_BOT_TOKEN must be set (env or --app-token/--bot-token).\n');
216
+ console.error(HELP);
217
+ process.exit(1);
218
+ }
219
+ if (!appToken.startsWith('xapp-')) {
220
+ console.error('Warning: SLACK_APP_TOKEN should start with "xapp-" (app-level token for Socket Mode).');
221
+ }
222
+ if (!botToken.startsWith('xoxb-')) {
223
+ console.error('Warning: SLACK_BOT_TOKEN should start with "xoxb-" (bot OAuth token).');
224
+ }
225
+ const claudeOptions = {};
226
+ if (parsed.model)
227
+ claudeOptions.model = parsed.model;
228
+ if (parsed.permissionMode)
229
+ claudeOptions.permissionMode = parsed.permissionMode;
230
+ if (parsed.dangerouslySkipPermissions)
231
+ claudeOptions.dangerouslySkipPermissions = true;
232
+ if (parsed.tools !== undefined)
233
+ claudeOptions.tools = parsed.tools;
234
+ const bot = new bot_1.SlackClaudeBot({
235
+ appToken,
236
+ botToken,
237
+ cwd: parsed.cwd,
238
+ systemPrompt: parsed.systemPrompt,
239
+ claudeOptions: claudeOptions,
240
+ logLevel: resolveLogLevel(parsed.logLevel),
241
+ });
242
+ const shutdown = async (signal) => {
243
+ console.log(`\nReceived ${signal}, shutting down…`);
244
+ try {
245
+ await bot.stop();
246
+ }
247
+ catch (err) {
248
+ console.error('Error during shutdown:', err);
249
+ }
250
+ finally {
251
+ process.exit(0);
252
+ }
253
+ };
254
+ process.on('SIGINT', () => void shutdown('SIGINT'));
255
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
256
+ await bot.start();
257
+ console.log('Listening for Slack events. Press Ctrl+C to stop.');
258
+ }
259
+ main().catch((err) => {
260
+ console.error('Fatal:', err);
261
+ process.exit(1);
262
+ });
263
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Programmatic entry point for @buzzie-ai/slack-claude.
3
+ *
4
+ * Most users should use the CLI:
5
+ *
6
+ * npx @buzzie-ai/slack-claude
7
+ *
8
+ * If you want to embed the bot in your own process — to share a Bolt App, add
9
+ * custom handlers, or wrap session creation — import the SlackClaudeBot class
10
+ * directly:
11
+ *
12
+ * import { SlackClaudeBot } from '@buzzie-ai/slack-claude';
13
+ *
14
+ * const bot = new SlackClaudeBot({
15
+ * appToken: process.env.SLACK_APP_TOKEN!,
16
+ * botToken: process.env.SLACK_BOT_TOKEN!,
17
+ * });
18
+ * await bot.start();
19
+ */
20
+ export { SlackClaudeBot } from './bot';
21
+ export type { SlackClaudeBotOptions } from './bot';
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AACvC,YAAY,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SlackClaudeBot = void 0;
4
+ /**
5
+ * Programmatic entry point for @buzzie-ai/slack-claude.
6
+ *
7
+ * Most users should use the CLI:
8
+ *
9
+ * npx @buzzie-ai/slack-claude
10
+ *
11
+ * If you want to embed the bot in your own process — to share a Bolt App, add
12
+ * custom handlers, or wrap session creation — import the SlackClaudeBot class
13
+ * directly:
14
+ *
15
+ * import { SlackClaudeBot } from '@buzzie-ai/slack-claude';
16
+ *
17
+ * const bot = new SlackClaudeBot({
18
+ * appToken: process.env.SLACK_APP_TOKEN!,
19
+ * botToken: process.env.SLACK_BOT_TOKEN!,
20
+ * });
21
+ * await bot.start();
22
+ */
23
+ var bot_1 = require("./bot");
24
+ Object.defineProperty(exports, "SlackClaudeBot", { enumerable: true, get: function () { return bot_1.SlackClaudeBot; } });
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,6BAAuC;AAA9B,qGAAA,cAAc,OAAA"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@buzzie-ai/slack-claude",
3
+ "version": "0.1.0",
4
+ "description": "Slack bot that bridges a Slack workspace to Claude Code via Socket Mode and @buzzie-ai/claude-inject. Receives Slack messages, forwards them to a persistent Claude Code session per thread, and posts replies back.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "slack-claude": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "prestart": "npm run build",
18
+ "start": "node dist/cli.js",
19
+ "dev": "tsx src/cli.ts",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "claude",
24
+ "claude-code",
25
+ "anthropic",
26
+ "slack",
27
+ "slack-bot",
28
+ "bolt",
29
+ "socket-mode",
30
+ "agent",
31
+ "automation"
32
+ ],
33
+ "author": "Buzzie AI (https://github.com/Buzzie-AI)",
34
+ "license": "MIT",
35
+ "homepage": "https://github.com/Buzzie-AI/slack-claude#readme",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/Buzzie-AI/slack-claude.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/Buzzie-AI/slack-claude/issues"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "engines": {
47
+ "node": ">=20"
48
+ },
49
+ "dependencies": {
50
+ "@buzzie-ai/claude-inject": "^0.1.0",
51
+ "@slack/bolt": "^4.7.2"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^20.0.0",
55
+ "tsx": "^4.0.0",
56
+ "typescript": "^5.4.0"
57
+ }
58
+ }