Skip to content

feat(message send): add --blocks flag for raw Block Kit payloads#82

Closed
thehesiod wants to merge 1 commit intostablyai:mainfrom
thehesiod-forks:feat/blocks-flag
Closed

feat(message send): add --blocks flag for raw Block Kit payloads#82
thehesiod wants to merge 1 commit intostablyai:mainfrom
thehesiod-forks:feat/blocks-flag

Conversation

@thehesiod
Copy link
Copy Markdown

Summary

Adds a --blocks <path> option to message send that reads a JSON array of Block Kit blocks from a file (or - for stdin) and passes it to chat.postMessage as-is, bypassing the built-in markdown-to-rich-text conversion.

This unlocks structured layouts that the markdown converter can't express — header, divider, context, section (with accessory), and especially the table block introduced by Slack in August 2025. User tokens already carry the chat:write scope needed; no new auth is required.

Example

cat > blocks.json <<'JSON'
[
  { "type": "header", "text": { "type": "plain_text", "text": "Weekly digest" } },
  {
    "type": "table",
    "rows": [
      [{ "type": "raw_text", "text": "Name" }, { "type": "raw_text", "text": "Why" }],
      [{ "type": "raw_text", "text": "Caveman MCP" }, { "type": "raw_text", "text": "~80% token cut" }]
    ]
  }
]
JSON
agent-slack message send "#alerts-staging" --blocks blocks.json

Behavior

  • --blocks and --attach are mutually exclusive (uploads take a separate code path that doesn't carry blocks)
  • Positional <text> remains optional when --blocks is set, but if provided it is sent as the message text fallback used for notifications and unfurls
  • Malformed JSON and non-array payloads produce clear CLI errors
  • When --blocks is set, the automatic markdown→rich_text conversion is skipped

Files

  • src/cli/message-command.ts — add --blocks option + mutual-exclusion check
  • src/cli/message-actions.tsloadBlocksFromPath() reads/parses/validates; sendMessage() prefers loaded blocks over text-derived ones
  • README.md, skills/agent-slack/SKILL.md, skills/agent-slack/references/commands.md — document the flag with a table-block example
  • test/message-send.test.ts — 4 new cases (happy path, non-array JSON, malformed JSON, precedence over text)

Test plan

  • bun test — all 185 tests pass (4 new)
  • bun run typecheck — clean
  • bun run lint — no new warnings (5 pre-existing line-count warnings unchanged)
  • bun run format:check — clean
  • Built locally with bun run build and sent a real message to a DM with a header + table block; table rendered natively in the Slack client

Adds a `--blocks <path>` option to `message send` that reads a JSON array
of Block Kit blocks from a file (or `-` for stdin) and passes it to
chat.postMessage as-is, bypassing the built-in markdown-to-rich-text
conversion.

This unlocks structured layouts that the markdown converter can't
express — headers, dividers, context blocks, and especially the
`table` block introduced by Slack in August 2025. User tokens already
have the `chat:write` scope needed, so no new auth setup is required.

The positional <text> argument (when provided alongside --blocks) is
still sent as the message `text` fallback for notifications and
unfurls. `--blocks` cannot be combined with `--attach` since uploads
take a separate code path.

- CLI: add --blocks option with mutual-exclusion check vs --attach
- Actions: loadBlocksFromPath() reads/parses/validates JSON array;
  sendMessage() prefers loaded blocks over markdown conversion
- README + skill docs: document the flag with a table-block example
- Tests: 4 new cases covering happy path, non-array JSON, malformed
  JSON, and precedence over text-derived blocks
@AmethystLiang
Copy link
Copy Markdown
Contributor

will review and get this in today

@AmethystLiang
Copy link
Copy Markdown
Contributor

Hi @thehesiod — thanks for this! I merged a continuation of your work as #89 (released in v0.9.3) since I couldn't push directly to your fork to resolve the merge conflict with main.

The squash commit on main credits you, and I added one small follow-up: loadBlocksFromPath now validates each array element is an object so non-object entries fail with a clear local error rather than a Slack-side rejection. Closing this in favor of the merged PR.

@thehesiod
Copy link
Copy Markdown
Author

thanks to @claude :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants