Skip to content

Commit 47c0d2d

Browse files
authored
feat: add slack relay adapter (#31)
* docs: add slack adapter design and plan * feat(core): register slack relay platform * feat(slack): add package scaffolding and helpers * feat(slack): add adapter and thread mapping * feat(slack): add socket mode runtime * fix(slack): harden runtime startup and cleanup
1 parent 2e11782 commit 47c0d2d

33 files changed

Lines changed: 3759 additions & 3 deletions
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# Slack Adapter Implementation Plan
2+
3+
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
4+
5+
**Goal:** Add a first-party Slack platform package with Socket Mode, thread-first conversation handling, Block Kit backend/model selection, slash commands, Slack markdown formatting, and core Slack platform registration.
6+
7+
**Architecture:** Build a dedicated `packages/slack` package that mirrors the existing Discord/Feishu package split: config/bootstrap, runtime/event routing, adapter, conversation helpers, cards, formatting, and focused tests. Keep core changes small and explicit by registering Slack as a relay platform, extending persistence/path helpers for Slack state, and only extracting Feishu run-gating logic when the shared boundary is transport-agnostic.
8+
9+
**Tech Stack:** TypeScript, pnpm workspaces, Vitest, `@slack/bolt`, Block Kit, existing `@agent-im-relay/core`
10+
11+
---
12+
13+
## Chunk 1: Core Slack platform plumbing
14+
15+
### Task 1: Make core recognize Slack conversations and scoped Slack state
16+
17+
**Files:**
18+
- Modify: `packages/core/src/relay-platform.ts`
19+
- Modify: `packages/core/src/paths.ts`
20+
- Modify: `packages/core/src/__tests__/persist.test.ts`
21+
- Modify: `packages/core/src/__tests__/config.test.ts` or add a new focused test file if path helpers need direct coverage
22+
23+
- [ ] **Step 1: Write the failing core tests**
24+
25+
Add tests that prove:
26+
- `relayPlatforms` includes `slack`
27+
- Slack conversation ids are inferable without colliding with legacy Discord numeric ids or Feishu ids
28+
- Slack-scoped state loads/saves preserve other platform state in the shared `sessions.json`
29+
- Slack helper paths resolve to sibling files/directories under the existing relay state directory
30+
31+
- [ ] **Step 2: Run the targeted core tests to verify they fail**
32+
33+
Run: `pnpm vitest run packages/core/src/__tests__/persist.test.ts packages/core/src/__tests__/config.test.ts`
34+
Expected: FAIL because Slack is not yet a known relay platform and no Slack-specific path helper exists.
35+
36+
- [ ] **Step 3: Write the minimal core implementation**
37+
38+
Implement the smallest possible core changes:
39+
- add `'slack'` to `relayPlatforms`
40+
- teach `inferRelayPlatformFromConversationId()` to recognize the Slack conversation id format chosen for the new adapter
41+
- add any Slack-specific path helper needed for package-local persistence files
42+
- preserve scoped save/load behavior in `persist.ts` so Slack state coexists with Discord and Feishu state
43+
44+
- [ ] **Step 4: Run the targeted core tests to verify they pass**
45+
46+
Run: `pnpm vitest run packages/core/src/__tests__/persist.test.ts packages/core/src/__tests__/config.test.ts`
47+
Expected: PASS
48+
49+
- [ ] **Step 5: Commit the core plumbing slice**
50+
51+
```bash
52+
git add packages/core/src/relay-platform.ts packages/core/src/paths.ts packages/core/src/__tests__/persist.test.ts packages/core/src/__tests__/config.test.ts
53+
git commit -m "feat(core): register slack relay platform"
54+
```
55+
56+
## Chunk 2: Scaffold the Slack package and its pure helpers
57+
58+
### Task 2: Add package bootstrap, config parsing, formatting, and Block Kit builders
59+
60+
**Files:**
61+
- Create: `packages/slack/package.json`
62+
- Create: `packages/slack/tsconfig.json`
63+
- Create: `packages/slack/tsdown.config.ts`
64+
- Create: `packages/slack/vitest.config.ts`
65+
- Create: `packages/slack/src/index.ts`
66+
- Create: `packages/slack/src/config.ts`
67+
- Create: `packages/slack/src/cards.ts`
68+
- Create: `packages/slack/src/formatting.ts`
69+
- Create: `packages/slack/src/__tests__/config.test.ts`
70+
- Create: `packages/slack/src/__tests__/cards.test.ts`
71+
- Create: `packages/slack/src/__tests__/formatting.test.ts`
72+
73+
- [ ] **Step 1: Write the failing Slack helper tests**
74+
75+
Cover these behaviors first:
76+
- config parsing for bot token, app token, signing secret, and Socket Mode defaults
77+
- Slack-local state-file helper paths
78+
- backend/model Block Kit payload shape
79+
- Markdown -> Slack `mrkdwn` conversion for headings, links, code fences, lists, and table fallback
80+
81+
- [ ] **Step 2: Run the helper tests to verify they fail**
82+
83+
Run: `pnpm vitest run packages/slack/src/__tests__/config.test.ts packages/slack/src/__tests__/cards.test.ts packages/slack/src/__tests__/formatting.test.ts`
84+
Expected: FAIL because `packages/slack` does not exist yet.
85+
86+
- [ ] **Step 3: Write the minimal Slack package and helper implementation**
87+
88+
Create the new package using the same build/test conventions as `packages/discord` and `packages/feishu`:
89+
- package metadata and workspace dependency on `@agent-im-relay/core`
90+
- `@slack/bolt` as the runtime dependency for Socket Mode, slash commands, actions, and events
91+
- config helpers that layer Slack env vars over core config
92+
- pure Block Kit builders for backend/model selection and status/control messages
93+
- a conservative Markdown formatter that outputs readable Slack `mrkdwn`
94+
95+
- [ ] **Step 4: Run the helper tests to verify they pass**
96+
97+
Run: `pnpm vitest run packages/slack/src/__tests__/config.test.ts packages/slack/src/__tests__/cards.test.ts packages/slack/src/__tests__/formatting.test.ts`
98+
Expected: PASS
99+
100+
- [ ] **Step 5: Commit the Slack helper slice**
101+
102+
```bash
103+
git add packages/slack/package.json packages/slack/tsconfig.json packages/slack/tsdown.config.ts packages/slack/vitest.config.ts packages/slack/src/index.ts packages/slack/src/config.ts packages/slack/src/cards.ts packages/slack/src/formatting.ts packages/slack/src/__tests__/config.test.ts packages/slack/src/__tests__/cards.test.ts packages/slack/src/__tests__/formatting.test.ts
104+
git commit -m "feat(slack): add package scaffolding and helpers"
105+
```
106+
107+
## Chunk 3: Implement the adapter and thread-mapping layer
108+
109+
### Task 3: Cover Slack adapter capabilities and conversation filtering
110+
111+
**Files:**
112+
- Create: `packages/slack/src/adapter.ts`
113+
- Create: `packages/slack/src/conversation.ts`
114+
- Create: `packages/slack/src/state.ts`
115+
- Create: `packages/slack/src/__tests__/adapter.test.ts`
116+
- Create: `packages/slack/src/__tests__/conversation.test.ts`
117+
- Modify: `packages/slack/src/index.ts`
118+
119+
- [ ] **Step 1: Write the failing adapter and conversation tests**
120+
121+
Add tests for:
122+
- `MessageSender.send()` / `edit()` targeting a Slack thread with optional `blocks`
123+
- `ConversationManager.createConversation()` always creating a new thread for `/code` and `/ask`
124+
- `ConversationManager.getConversationId()` resolving only mapped active conversation threads
125+
- `StatusIndicator` updating visible thread status state
126+
- `InteractiveUI` waiting on Block Kit selection state without accepting unrelated actions
127+
- message filtering rules: slash commands global, ordinary messages only in mapped conversation threads, bot-authored messages ignored
128+
129+
- [ ] **Step 2: Run the targeted adapter tests to verify they fail**
130+
131+
Run: `pnpm vitest run packages/slack/src/__tests__/adapter.test.ts packages/slack/src/__tests__/conversation.test.ts`
132+
Expected: FAIL because the adapter, conversation mapper, and Slack state store are not implemented.
133+
134+
- [ ] **Step 3: Write the minimal adapter implementation**
135+
136+
Implement:
137+
- a Slack conversation id format based on the created thread root timestamp, with enough side-state to recover channel id and root message ts
138+
- a small persisted Slack state store for thread mappings, pending UI state, and any card message ids needed for in-place updates
139+
- adapter implementations for `MessageSender`, `ConversationManager`, `StatusIndicator`, `InteractiveUI`, and `MarkdownFormatter`
140+
- exports from `packages/slack/src/index.ts` that match the package conventions used elsewhere
141+
142+
- [ ] **Step 4: Run the targeted adapter tests to verify they pass**
143+
144+
Run: `pnpm vitest run packages/slack/src/__tests__/adapter.test.ts packages/slack/src/__tests__/conversation.test.ts`
145+
Expected: PASS
146+
147+
- [ ] **Step 5: Commit the adapter slice**
148+
149+
```bash
150+
git add packages/slack/src/index.ts packages/slack/src/adapter.ts packages/slack/src/conversation.ts packages/slack/src/state.ts packages/slack/src/__tests__/adapter.test.ts packages/slack/src/__tests__/conversation.test.ts
151+
git commit -m "feat(slack): add adapter and thread mapping"
152+
```
153+
154+
## Chunk 4: Implement Socket Mode runtime, slash commands, and run gating
155+
156+
### Task 4: Handle Slack events, Block Kit interactions, and pending runs end-to-end
157+
158+
**Files:**
159+
- Create: `packages/slack/src/runtime.ts`
160+
- Create: `packages/slack/src/commands/code.ts`
161+
- Create: `packages/slack/src/commands/ask.ts`
162+
- Create: `packages/slack/src/commands/interrupt.ts`
163+
- Create: `packages/slack/src/commands/done.ts`
164+
- Create: `packages/slack/src/commands/skill.ts`
165+
- Create: `packages/slack/src/__tests__/runtime.test.ts`
166+
- Create: `packages/slack/src/__tests__/commands.test.ts`
167+
- Modify: `packages/slack/src/state.ts`
168+
- Modify: `packages/slack/src/cards.ts`
169+
- Modify: `packages/slack/src/index.ts`
170+
- Modify: `packages/core/src/index.ts` if new core helpers are extracted for shared run gating
171+
- Modify: `packages/feishu/src/runtime.ts` and related tests only if a clearly shared helper is extracted and Feishu behavior is preserved
172+
173+
- [ ] **Step 1: Write the failing runtime and command tests**
174+
175+
Cover:
176+
- Socket Mode slash-command ack + async thread creation
177+
- `/code` and `/ask` always creating a fresh thread, even when invoked from an existing thread
178+
- `/interrupt`, `/done`, and `/skill` operating only on mapped conversations
179+
- backend/model gate behavior with a single pending run per conversation
180+
- model-selection timeout resuming the pending run when a safe default exists
181+
- in-place Block Kit card updates after backend/model choice
182+
- ordinary thread replies resuming the mapped conversation
183+
184+
- [ ] **Step 2: Run the targeted runtime tests to verify they fail**
185+
186+
Run: `pnpm vitest run packages/slack/src/__tests__/runtime.test.ts packages/slack/src/__tests__/commands.test.ts`
187+
Expected: FAIL because the Slack runtime, command dispatch, and pending-run flow are not implemented.
188+
189+
- [ ] **Step 3: Write the minimal runtime implementation**
190+
191+
Build the runtime around `@slack/bolt` Socket Mode primitives:
192+
- bootstrap a Bolt `App` configured for Socket Mode
193+
- register slash commands, event listeners, and action handlers
194+
- create a new Slack thread for `/code` and `/ask`, persist the mapping, then dispatch the run through core
195+
- enforce the message filter rules for ordinary Slack messages
196+
- reuse Feishu-style pending-run/model-selection semantics where the code can be shared cleanly; otherwise mirror the behavior locally with identical tests
197+
- clean up pending runs, timers, and visible card state on `/interrupt` and `/done`
198+
199+
- [ ] **Step 4: Run the targeted runtime tests to verify they pass**
200+
201+
Run: `pnpm vitest run packages/slack/src/__tests__/runtime.test.ts packages/slack/src/__tests__/commands.test.ts`
202+
Expected: PASS
203+
204+
- [ ] **Step 5: Commit the runtime slice**
205+
206+
```bash
207+
git add packages/slack/src/runtime.ts packages/slack/src/commands/code.ts packages/slack/src/commands/ask.ts packages/slack/src/commands/interrupt.ts packages/slack/src/commands/done.ts packages/slack/src/commands/skill.ts packages/slack/src/state.ts packages/slack/src/cards.ts packages/slack/src/index.ts packages/slack/src/__tests__/runtime.test.ts packages/slack/src/__tests__/commands.test.ts packages/core/src/index.ts packages/feishu/src/runtime.ts
208+
git commit -m "feat(slack): add socket mode runtime"
209+
```
210+
211+
## Chunk 5: Verify integration, docs, and PR readiness
212+
213+
### Task 5: Prove Slack is integrated without regressing accepted baselines
214+
215+
**Files:**
216+
- Modify: `docs/superpowers/specs/2026-03-12-slack-adapter-design.md`
217+
- Modify: `docs/superpowers/plans/2026-03-12-slack-adapter.md`
218+
- Modify: any implementation and test files touched above
219+
220+
- [ ] **Step 1: Run the focused green suite**
221+
222+
Run: `pnpm vitest run packages/core/src/__tests__/persist.test.ts packages/slack/src/__tests__/config.test.ts packages/slack/src/__tests__/cards.test.ts packages/slack/src/__tests__/formatting.test.ts packages/slack/src/__tests__/adapter.test.ts packages/slack/src/__tests__/conversation.test.ts packages/slack/src/__tests__/runtime.test.ts packages/slack/src/__tests__/commands.test.ts packages/feishu/src/__tests__/runtime.test.ts`
223+
Expected: PASS
224+
225+
- [ ] **Step 2: Run package-level verification**
226+
227+
Run:
228+
- `pnpm --filter @agent-im-relay/core test`
229+
- `pnpm --filter @agent-im-relay/slack test`
230+
- `pnpm --filter @agent-im-relay/feishu test`
231+
Expected: PASS
232+
233+
Note: The repository currently has an accepted pre-existing red baseline in `packages/discord/src/__tests__/index.test.ts`. Do not claim a fully green workspace unless those known Discord failures are separately addressed.
234+
235+
- [ ] **Step 3: Run build verification**
236+
237+
Run:
238+
- `pnpm --filter @agent-im-relay/core build`
239+
- `pnpm --filter @agent-im-relay/slack build`
240+
- `pnpm --filter @agent-im-relay/feishu build`
241+
Expected: PASS
242+
243+
- [ ] **Step 4: Review the diff and write the PR summary**
244+
245+
Run:
246+
- `git diff --stat`
247+
- `git diff -- packages/core packages/slack packages/feishu docs/superpowers`
248+
Expected: Only Slack-related platform, shared gating, and documentation changes appear.
249+
250+
Prepare the PR notes with:
251+
- Slack package overview
252+
- core registration/state updates
253+
- pending-run/model-selection behavior
254+
- verification results and the accepted known Discord baseline failure
255+
256+
- [ ] **Step 5: Create the final commit, push, and open the PR**
257+
258+
```bash
259+
git add packages/core packages/slack packages/feishu docs/superpowers
260+
git commit -m "feat: add slack relay adapter"
261+
git push -u origin feat/slack-adapter
262+
gh pr create --fill
263+
```

0 commit comments

Comments
 (0)