Skip to content

Add mdToMrkdwn: convert outgoing markdown to Slack mrkdwn - #5

Merged
TheGreatAxios merged 3 commits into
mainfrom
tag-slack-markdown-to-mrkdwn
Aug 2, 2026
Merged

Add mdToMrkdwn: convert outgoing markdown to Slack mrkdwn#5
TheGreatAxios merged 3 commits into
mainfrom
tag-slack-markdown-to-mrkdwn

Conversation

@TheGreatAxios

@TheGreatAxios TheGreatAxios commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds mdToMrkdwn, a conservative regex-based transform from common markdown constructs (**bold**/__bold__, [text](url) links, # headers, -/* bullets) to Slack's mrkdwn dialect. No AST, no new dependency — code fences and inline code spans are protected with a NUL-delimited placeholder before conversion and restored byte-for-byte after. NUL can't appear in ordinary text, so a placeholder token never collides with literal text that happens to look like one (e.g. a model asked to echo something verbatim).
  • Wires it into toTagThread's post() in packages/tag-slack/src/wire.ts, so every TagThread.post() call — including the thinking-indicator's edit-in-place path — gets markdown normalized automatically before it reaches Slack.
  • Exports mdToMrkdwn from the package's public surface (index.ts) for hosts that want to call it directly.

Motivated by dispatch producers (e.g. an LLM) that emit standard markdown even when asked not to, which otherwise renders literally in Slack.

Closes CL-5012: add mdToMrkdwn to normalize outgoing markdown for Slack

Test plan

  • CI: bun run typecheck && bun run test
  • packages/tag-slack/src/mrkdwn.test.ts covers bold, links, headers, bullets, indentation, fenced code blocks, inline code spans, bare * (non-bold), combined constructs, a no-op on already-clean mrkdwn, and a placeholder-collision regression (literal text containing a MRKDWN_CODE_0-shaped substring survives untouched).
  • packages/tag-slack/src/wire.test.ts adds two cases: tagThread.post() converts markdown before the fake thread receives it, and the thinking-indicator's placeholder edit also receives converted text.
  • Not run locally (sandboxed dev machine); tests will run in this repo's CI.

Dispatch producers (e.g. an LLM) commonly emit standard markdown —
headers, **bold**, [text](url) links — none of which render as
intended in Slack. mdToMrkdwn is a conservative, dependency-free
regex transform applied in toTagThread's post() so every
TagThread.post() call gets it automatically. Code fences and inline
code spans are protected with a NUL-delimited placeholder token first
and restored untouched — NUL can't appear in ordinary text, so there
is no collision surface between a real placeholder and literal text
that happens to look like one (e.g. a model asked to echo something
verbatim).

Closes CL-5012: add mdToMrkdwn to normalize outgoing markdown for Slack
@TheGreatAxios
TheGreatAxios force-pushed the tag-slack-markdown-to-mrkdwn branch from e5b81e3 to 3f40545 Compare August 2, 2026 17:03
convertLinks had no guard against a preceding `!`, so `![alt](url)`
was silently rewritten to `!<url|alt>` — a clickable link with a
stray leading `!`, and the fact that it was an image lost entirely.
Untouched it would at least render as literal markdown; converted it
was actively wrong, and models emit image markdown constantly. Fixed
with a negative lookbehind excluding image syntax from link
conversion.

Also:
- Add `~~text~~` -> `~text~` strikethrough conversion (Slack's
  single-tilde form), same conservative regex approach as the other
  inline conversions.
- Add an escape hatch: `TagThread.post()` takes an optional
  `{ convertMarkdown: false }` to skip conversion. The caller
  composing a message is the one who knows whether it emitted
  markdown or an intentional literal; conversion firing unconditionally
  took that decision away from it. Conversion stays the default.
- Move the NUL-delimiter rationale from a test comment into mrkdwn.ts,
  next to the placeholder constants it explains — the place someone
  editing this file will actually see it before "cleaning up" the
  control character. Also notes the bytes are invisible to plain
  `grep`/most diff viewers.
- Document mdToMrkdwn's formatting contract: inline only, not tables,
  images, or nested lists — those need Block Kit.
- Move the mdToMrkdwn export onto its own line in the package index;
  it was wedged inside the slack-users.ts export group, breaking the
  file's group-by-source-module pattern.
convertMarkdown lives on tag-core's TagThreadPostOptions but so far only
tag-slack reads it; tag-core has no README, so the doc comment is the
authoritative spot. Notes that honoring the option is expected of any
TagThread implementation, not a Slack-specific detail.
@TheGreatAxios
TheGreatAxios merged commit c31a0a3 into main Aug 2, 2026
1 check passed
@TheGreatAxios
TheGreatAxios deleted the tag-slack-markdown-to-mrkdwn branch August 2, 2026 17:33
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.

1 participant