From 2e93bff4a67d8d867731c23a563a2ccd028ae849 Mon Sep 17 00:00:00 2001 From: Ben Delaney Date: Wed, 13 May 2026 20:51:15 +1000 Subject: [PATCH] Add Block Kit support to message edits --- skills/agent-slack/SKILL.md | 5 +- skills/agent-slack/references/commands.md | 1 + src/cli/message-actions.ts | 5 +- src/cli/message-command.ts | 3 +- test/message-send.test.ts | 85 ++++++++++++++++++++++- 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/skills/agent-slack/SKILL.md b/skills/agent-slack/SKILL.md index 8711385..7488414 100644 --- a/skills/agent-slack/SKILL.md +++ b/skills/agent-slack/SKILL.md @@ -158,10 +158,7 @@ agent-slack message edit "general" "Updated text" --workspace "myteam" --ts "177 agent-slack message delete "general" --workspace "myteam" --ts "1770165109.628379" ``` -Attach options for `message send`: - -- `--attach ` upload a local file (repeatable) -- `--blocks ` send raw [Block Kit](https://docs.slack.dev/block-kit/) blocks from a JSON file (or `-` for stdin). Enables headers, dividers, table blocks, and other structured layouts. Incompatible with `--attach`. +Message options: `message send --attach ` uploads files; `message send|edit --blocks ` uses raw Block Kit JSON (`-` for stdin). `message send` returns `channel_id` plus the posted `ts` and a `permalink` (for non-attachment sends). `thread_ts` appears only when replying in a thread. diff --git a/skills/agent-slack/references/commands.md b/skills/agent-slack/references/commands.md index 8cb7491..bbcaa3c 100644 --- a/skills/agent-slack/references/commands.md +++ b/skills/agent-slack/references/commands.md @@ -75,6 +75,7 @@ Run `agent-slack --help` (or `agent-slack --help`) for the full option - Options: - `--workspace ` (needed for channel _names_ across multiple workspaces) - `--ts .` (required for channel targets) + - `--blocks ` raw Block Kit blocks JSON (`-` for stdin) - `agent-slack message delete ` - URL target deletes that exact message. diff --git a/src/cli/message-actions.ts b/src/cli/message-actions.ts index ea0547e..b592e8d 100644 --- a/src/cli/message-actions.ts +++ b/src/cli/message-actions.ts @@ -258,7 +258,7 @@ export async function editMessage(input: { ctx: CliContext; targetInput: string; text: string; - options: { workspace?: string; ts?: string }; + options: { workspace?: string; ts?: string; blocks?: string }; }): Promise> { const target = parseMsgTarget(String(input.targetInput)); if (target.kind === "user") { @@ -268,6 +268,7 @@ export async function editMessage(input: { } const workspaceUrl = input.ctx.effectiveWorkspaceUrl(input.options.workspace); const formattedText = formatOutboundSlackText(input.text); + const blocks = input.options.blocks ? loadBlocksFromPath(input.options.blocks) : null; await input.ctx.withAutoRefresh({ workspaceUrl: target.kind === "url" ? target.ref.workspace_url : workspaceUrl, @@ -280,6 +281,7 @@ export async function editMessage(input: { channel: ref.channel_id, ts: ref.message_ts, text: formattedText, + ...(blocks ? { blocks } : {}), }); return; } @@ -295,6 +297,7 @@ export async function editMessage(input: { channel: channelId, ts, text: formattedText, + ...(blocks ? { blocks } : {}), }); }, }); diff --git a/src/cli/message-command.ts b/src/cli/message-command.ts index cd642c3..62b9d19 100644 --- a/src/cli/message-command.ts +++ b/src/cli/message-command.ts @@ -109,11 +109,12 @@ export function registerMessageCommand(input: { program: Command; ctx: CliContex "Workspace selector (full URL or unique substring; needed when using #channel/channel id across multiple workspaces)", ) .option("--ts ", "Message ts (required when using #channel/channel id)") + .option("--blocks ", "Read Block Kit blocks JSON from file or '-'") .action(async (...args) => { const [targetInput, text, options] = args as [ string, string, - { workspace?: string; ts?: string }, + { workspace?: string; ts?: string; blocks?: string }, ]; try { const payload = await editMessage({ diff --git a/test/message-send.test.ts b/test/message-send.test.ts index 2c33610..3b15153 100644 --- a/test/message-send.test.ts +++ b/test/message-send.test.ts @@ -3,7 +3,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import type { CliContext } from "../src/cli/context.ts"; -import { sendMessage } from "../src/cli/message-actions.ts"; +import { editMessage, sendMessage } from "../src/cli/message-actions.ts"; function createContext(calls: { method: string; params: Record }[]) { const client = { @@ -352,3 +352,86 @@ describe("sendMessage", () => { } }); }); + +describe("editMessage", () => { + test("edits a normal message without blocks when no --blocks file is passed", async () => { + const calls: { method: string; params: Record }[] = []; + const ctx = createContext(calls); + + const result = await editMessage({ + ctx, + targetInput: "C12345678", + text: "hello", + options: { workspace: "https://workspace.slack.com", ts: "1770165109.628379" }, + }); + + expect(result).toEqual({ ok: true }); + expect(calls).toHaveLength(1); + expect(calls[0]?.method).toBe("chat.update"); + expect(calls[0]?.params).toEqual({ + channel: "C12345678", + ts: "1770165109.628379", + text: "hello", + }); + }); + + test("edits a message URL with Block Kit JSON from file", async () => { + const calls: { method: string; params: Record }[] = []; + const ctx = createContext(calls); + const dir = await mkdtemp(join(tmpdir(), "agent-slack-edit-test-")); + const blocksPath = join(dir, "blocks.json"); + const blocks = [ + { type: "header", text: { type: "plain_text", text: "Edited" } }, + { type: "section", text: { type: "mrkdwn", text: "Updated body" } }, + ]; + await writeFile(blocksPath, JSON.stringify(blocks)); + + try { + await editMessage({ + ctx, + targetInput: "https://workspace.slack.com/archives/C12345678/p1770165109628379", + text: "fallback text", + options: { blocks: blocksPath }, + }); + } finally { + await rm(dir, { recursive: true, force: true }); + } + + expect(calls).toHaveLength(1); + expect(calls[0]?.method).toBe("chat.update"); + expect(calls[0]?.params).toEqual({ + channel: "C12345678", + ts: "1770165109.628379", + text: "fallback text", + blocks, + }); + }); + + test("edits a channel target with --ts and Block Kit JSON from file", async () => { + const calls: { method: string; params: Record }[] = []; + const ctx = createContext(calls); + const dir = await mkdtemp(join(tmpdir(), "agent-slack-edit-test-")); + const blocksPath = join(dir, "blocks.json"); + const blocks = [{ type: "divider" }]; + await writeFile(blocksPath, JSON.stringify(blocks)); + + try { + await editMessage({ + ctx, + targetInput: "C12345678", + text: "fallback text", + options: { + workspace: "https://workspace.slack.com", + ts: "1770165109.628379", + blocks: blocksPath, + }, + }); + } finally { + await rm(dir, { recursive: true, force: true }); + } + + expect(calls).toHaveLength(1); + expect(calls[0]?.method).toBe("chat.update"); + expect(calls[0]?.params.blocks).toEqual(blocks); + }); +});