@dreki-gg/pi-slack 0.2.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @dreki-gg/pi-slack
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add a `slack_delete_message` tool that deletes a message the bot previously
8
+ posted via Slack's `chat.delete` API. Pass the channel ID and the message `ts`;
9
+ bots can only delete their own messages. No new scope is required — it uses the
10
+ existing `chat:write` permission.
11
+
12
+ ## 0.3.0
13
+
14
+ ### Minor Changes
15
+
16
+ - Add `slack_edit_message` tool so the agent can edit messages it previously posted. Pass the `channel` ID and the message `ts` (returned by `slack_post_message`) along with the new `text` to rewrite a message via Slack's `chat.update`. Reuses the existing `chat:write` scope — bots can only edit their own messages.
17
+
3
18
  ## 0.2.0
4
19
 
5
20
  ### Minor Changes
package/README.md CHANGED
@@ -13,6 +13,9 @@ Uses [Effect](https://effect.website) for the Slack client layer (typed errors,
13
13
  | `slack_read_thread` | Read replies in a thread |
14
14
  | `slack_search` | Full-text search across workspace (requires user token) |
15
15
  | `slack_download_file` | Download a shared file/image to temp directory |
16
+ | `slack_post_message` | Post a message to a channel or reply in a thread (requires `chat:write`) |
17
+ | `slack_edit_message` | Edit a message the bot previously posted (requires `chat:write`) |
18
+ | `slack_delete_message` | Delete a message the bot previously posted (requires `chat:write`) |
16
19
 
17
20
  ## Setup
18
21
 
@@ -12,6 +12,20 @@ export interface PostMessageResult {
12
12
  channel: string;
13
13
  }
14
14
 
15
+ export interface EditMessageResult {
16
+ /** Timestamp of the edited message. */
17
+ ts: string;
18
+ /** Channel the message lives in. */
19
+ channel: string;
20
+ }
21
+
22
+ export interface DeleteMessageResult {
23
+ /** Timestamp of the deleted message. */
24
+ ts: string;
25
+ /** Channel the message lived in. */
26
+ channel: string;
27
+ }
28
+
15
29
  // ---------------------------------------------------------------------------
16
30
  // Effects
17
31
  // ---------------------------------------------------------------------------
@@ -41,3 +55,32 @@ export function postMessage(params: {
41
55
  } satisfies PostMessageResult;
42
56
  });
43
57
  }
58
+
59
+ export function editMessage(params: { channel: string; ts: string; text: string }) {
60
+ return Effect.gen(function* () {
61
+ const resp = yield* slackPost<{ ok: true; ts: string; channel: string }>('chat.update', {
62
+ channel: params.channel,
63
+ ts: params.ts,
64
+ text: params.text,
65
+ });
66
+
67
+ return {
68
+ ts: resp.ts,
69
+ channel: resp.channel,
70
+ } satisfies EditMessageResult;
71
+ });
72
+ }
73
+
74
+ export function deleteMessage(params: { channel: string; ts: string }) {
75
+ return Effect.gen(function* () {
76
+ const resp = yield* slackPost<{ ok: true; ts: string; channel: string }>('chat.delete', {
77
+ channel: params.channel,
78
+ ts: params.ts,
79
+ });
80
+
81
+ return {
82
+ ts: resp.ts,
83
+ channel: resp.channel,
84
+ } satisfies DeleteMessageResult;
85
+ });
86
+ }
@@ -1,7 +1,11 @@
1
1
  import type { SlackChannel, SlackMessage, ReadMessagesResult } from './client/channels.js';
2
2
  import type { SearchResult } from './client/search.js';
3
3
  import type { DownloadedFile, SlackFileInfo } from './client/files.js';
4
- import type { PostMessageResult } from './client/messages.js';
4
+ import type {
5
+ PostMessageResult,
6
+ EditMessageResult,
7
+ DeleteMessageResult,
8
+ } from './client/messages.js';
5
9
 
6
10
  // ---------------------------------------------------------------------------
7
11
  // Channels
@@ -85,16 +89,19 @@ export function formatSearchResults(result: SearchResult, query: string): string
85
89
  // Posting
86
90
  // ---------------------------------------------------------------------------
87
91
 
88
- export function formatPostedMessage(
89
- result: PostMessageResult,
90
- threadTs?: string,
91
- ): string {
92
- const where = threadTs
93
- ? `thread ${threadTs} in ${result.channel}`
94
- : `${result.channel}`;
92
+ export function formatPostedMessage(result: PostMessageResult, threadTs?: string): string {
93
+ const where = threadTs ? `thread ${threadTs} in ${result.channel}` : `${result.channel}`;
95
94
  return `✅ Message posted to ${where} (ts: ${result.ts})`;
96
95
  }
97
96
 
97
+ export function formatEditedMessage(result: EditMessageResult): string {
98
+ return `✏️ Message edited in ${result.channel} (ts: ${result.ts})`;
99
+ }
100
+
101
+ export function formatDeletedMessage(result: DeleteMessageResult): string {
102
+ return `🗑️ Message deleted in ${result.channel} (ts: ${result.ts})`;
103
+ }
104
+
98
105
  // ---------------------------------------------------------------------------
99
106
  // Files
100
107
  // ---------------------------------------------------------------------------
@@ -14,7 +14,7 @@ import { listChannels } from './client/channels.js';
14
14
  import { readMessages, readThread } from './client/channels.js';
15
15
  import { searchMessages } from './client/search.js';
16
16
  import { downloadFile } from './client/files.js';
17
- import { postMessage } from './client/messages.js';
17
+ import { postMessage, editMessage, deleteMessage } from './client/messages.js';
18
18
  import {
19
19
  formatChannelList,
20
20
  formatMessages,
@@ -22,6 +22,8 @@ import {
22
22
  formatSearchResults,
23
23
  formatDownloadedFile,
24
24
  formatPostedMessage,
25
+ formatEditedMessage,
26
+ formatDeletedMessage,
25
27
  } from './format.js';
26
28
  import {
27
29
  TOOL_GUIDELINES,
@@ -31,6 +33,8 @@ import {
31
33
  searchParams,
32
34
  downloadFileParams,
33
35
  postMessageParams,
36
+ editMessageParams,
37
+ deleteMessageParams,
34
38
  } from './tools.js';
35
39
 
36
40
  // ---------------------------------------------------------------------------
@@ -334,6 +338,90 @@ export default function slackExtension(pi: ExtensionAPI) {
334
338
  },
335
339
  });
336
340
 
341
+ // -------------------------------------------------------------------------
342
+ // slack_edit_message
343
+ // -------------------------------------------------------------------------
344
+
345
+ pi.registerTool({
346
+ name: 'slack_edit_message',
347
+ label: 'Slack Edit Message',
348
+ description:
349
+ 'Edit a message the bot previously posted. Requires the bot token to have the `chat:write` scope. Bots can only edit their own messages.',
350
+ promptSnippet: 'Edit a message the bot previously posted',
351
+ promptGuidelines: TOOL_GUIDELINES,
352
+ parameters: editMessageParams,
353
+
354
+ async execute(
355
+ _toolCallId: string,
356
+ params: {
357
+ channel: string;
358
+ ts: string;
359
+ text: string;
360
+ },
361
+ ) {
362
+ const creds = getCredentials();
363
+ if (!creds) return missingCredentials('bot');
364
+
365
+ try {
366
+ const result = await runSlack(
367
+ creds,
368
+ editMessage({
369
+ channel: params.channel,
370
+ ts: params.ts,
371
+ text: params.text,
372
+ }),
373
+ );
374
+ return textResult(formatEditedMessage(result), {
375
+ channel: result.channel,
376
+ ts: result.ts,
377
+ });
378
+ } catch (err) {
379
+ return errorResult(`❌ ${(err as Error).message}`);
380
+ }
381
+ },
382
+ });
383
+
384
+ // -------------------------------------------------------------------------
385
+ // slack_delete_message
386
+ // -------------------------------------------------------------------------
387
+
388
+ pi.registerTool({
389
+ name: 'slack_delete_message',
390
+ label: 'Slack Delete Message',
391
+ description:
392
+ 'Delete a message the bot previously posted. Requires the bot token to have the `chat:write` scope. Bots can only delete their own messages.',
393
+ promptSnippet: 'Delete a message the bot previously posted',
394
+ promptGuidelines: TOOL_GUIDELINES,
395
+ parameters: deleteMessageParams,
396
+
397
+ async execute(
398
+ _toolCallId: string,
399
+ params: {
400
+ channel: string;
401
+ ts: string;
402
+ },
403
+ ) {
404
+ const creds = getCredentials();
405
+ if (!creds) return missingCredentials('bot');
406
+
407
+ try {
408
+ const result = await runSlack(
409
+ creds,
410
+ deleteMessage({
411
+ channel: params.channel,
412
+ ts: params.ts,
413
+ }),
414
+ );
415
+ return textResult(formatDeletedMessage(result), {
416
+ channel: result.channel,
417
+ ts: result.ts,
418
+ });
419
+ } catch (err) {
420
+ return errorResult(`❌ ${(err as Error).message}`);
421
+ }
422
+ },
423
+ });
424
+
337
425
  // -------------------------------------------------------------------------
338
426
  // /slack command — status check
339
427
  // -------------------------------------------------------------------------
@@ -12,6 +12,8 @@ export const TOOL_GUIDELINES = [
12
12
  'Use `slack_search` for full-text search across the workspace. Requires SLACK_USER_TOKEN to be set.',
13
13
  'Use `slack_download_file` to download images or files shared in Slack. The tool saves files to a temp directory and returns the path — use `read` to view images.',
14
14
  'Use `slack_post_message` to send a message to a channel or reply in a thread. The bot token must have the `chat:write` scope and the bot must be a member of the target channel.',
15
+ 'Use `slack_edit_message` to edit a message the bot previously posted. Pass the channel ID and the message `ts` (returned by `slack_post_message`). Bots can only edit their own messages.',
16
+ 'Use `slack_delete_message` to delete a message the bot previously posted. Pass the channel ID and the message `ts`. Bots can only delete their own messages.',
15
17
  'When messages reference files or images (shown with 📎), download them with `slack_download_file` using the file ID to get visual context.',
16
18
  'Channel IDs look like C0123ABC456. Thread timestamps look like 1512085950.000216.',
17
19
  ];
@@ -134,3 +136,24 @@ export const postMessageParams = Type.Object({
134
136
  }),
135
137
  ),
136
138
  });
139
+
140
+ export const editMessageParams = Type.Object({
141
+ channel: Type.String({
142
+ description: 'Channel ID (e.g. C0123ABC456) where the message lives.',
143
+ }),
144
+ ts: Type.String({
145
+ description: 'Timestamp of the message to edit (the `ts` returned by slack_post_message).',
146
+ }),
147
+ text: Type.String({
148
+ description: 'New message text. Supports Slack mrkdwn formatting.',
149
+ }),
150
+ });
151
+
152
+ export const deleteMessageParams = Type.Object({
153
+ channel: Type.String({
154
+ description: 'Channel ID (e.g. C0123ABC456) where the message lives.',
155
+ }),
156
+ ts: Type.String({
157
+ description: 'Timestamp of the message to delete (the `ts` returned by slack_post_message).',
158
+ }),
159
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreki-gg/pi-slack",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Slack read tools for pi — messages, threads, channels, search, and file/image downloads with Effect-powered client",
5
5
  "keywords": [
6
6
  "pi-package",