Skip to content

fix(messages): convert Markdown to Slack mrkdwn on send (closes #1) - #3

Merged
codesoda merged 3 commits into
mainfrom
fix/markdown-to-mrkdwn
Sep 8, 2026
Merged

fix(messages): convert Markdown to Slack mrkdwn on send (closes #1)#3
codesoda merged 3 commits into
mainfrom
fix/markdown-to-mrkdwn

Conversation

@codesoda

@codesoda codesoda commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1. slack messages send defaults to --format markdown but performed no conversion — Slack parses the text field as mrkdwn (not Markdown), so **bold** and [text](url) rendered as literal characters.

This adds a dependency-free Markdown→mrkdwn converter applied on the send path.

Conversions

Markdown mrkdwn
**bold**, __bold__ *bold*
*italic*, _italic_ _italic_
~~strike~~ ~strike~
# Heading *Heading* (bold line)
[text](url), ![alt](url) <url|text>
- item / * item / + item • item
1. item / 1) item 1. item (normalized)

The bullet and kept-number ordered lists mirror what Slack's own composer produces (confirmed against a real posted message).

Protected (passed through untouched): inline code `…`, fenced code blocks, and existing mrkdwn spans (<@U…> mentions, <url|text> links) — so nothing gets double-encoded.

--format plain is unchanged (sends verbatim, mrkdwn parsing disabled).

Implementation

  • New src/utils/mrkdwn.rs — a small byte-scanning converter (no regex/deps), handling fenced/inline code, block-level line transforms (headings, lists, blockquotes), and inline spans with UTF-8-safe scanning.
  • src/cli/messages.rs — the MessageFormat::Markdown arm now converts before sending.

Testing

  • 24 converter unit tests (incl. the exact issue example, code protection, Unicode, nested formatting, multi-line composer-style doc).
  • Full suite green; clippy + fmt + doc clean.
  • Verified live against a real workspace: bold/italic/strike/links/numbered+bullet lists all render correctly; inline code stays literal.

🤖 Generated with pi


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

'messages send' defaults to --format markdown but did no conversion, so
standard Markdown (**bold**, [text](url), lists) rendered as literal
characters in Slack. Add a dependency-free Markdown->mrkdwn converter
(src/utils/mrkdwn.rs) applied on the send path:

- **bold**/__bold__ -> *bold*; *italic*/_italic_ -> _italic_; ~~s~~ -> ~s~
- # heading -> bold line; [t](url)/![alt](url) -> <url|t>
- bullets -/*/+ -> '• '; ordered 1./1) -> normalized '1. '
- inline code, fenced code, and existing <@U…>/<url|text> spans preserved

--format plain still sends verbatim with mrkdwn disabled. 24 converter unit
tests + docs (README, skill). Verified live against a real workspace.
Comment thread src/utils/mrkdwn.rs Outdated
Comment thread src/utils/mrkdwn.rs Outdated
Comment thread src/utils/mrkdwn.rs Outdated
Web API requests are sent as application/x-www-form-urlencoded (since the
form-encoding change on main), but test_files_list_in_channel still matched a
PartialJson body. The mock never matched, the request errored, and the test
panicked under SLACK_INTEGRATION_TESTS=1 (CI). Switch to a UrlEncoded matcher.

This was failing on main and on every open PR's Test/Coverage jobs.
Comment thread src/utils/mrkdwn.rs Outdated
…nmatched **

Four bugs flagged by the Sentry bug-prediction reviewer on #3:

1. Spaced asterisks (a * b * c) were wrongly italicized. Add simplified
   CommonMark flanking: a single * only opens emphasis when not followed by
   whitespace and only closes when not preceded by whitespace (find_emphasis_close).
2. Angle brackets were greedily preserved, so <...> around comparisons
   (x < 5 and **bold** > 2) blocked conversion. Only preserve when the content
   is a real mrkdwn span — mention (@/#/!) or scheme URL (is_mrkdwn_span);
   otherwise emit < literally and keep converting.
3. parse_link truncated URLs containing parentheses (…/Topic_(disambiguation)).
   Match the balanced closing paren instead of the first ) (find_matching_paren).
4. Unmatched ** emitted one literal * then re-parsed the second as italic.
   Emit both asterisks and advance by two.

Adds regression tests for all four.
Comment thread src/utils/mrkdwn.rs
@codesoda
codesoda merged commit cc0432c into main Sep 8, 2026
10 checks passed
@codesoda
codesoda deleted the fix/markdown-to-mrkdwn branch September 8, 2026 11:15
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.

messages send: default --format markdown does not convert Markdown to mrkdwn (bold/links render literally)

1 participant