Nanoclaw channel-formatting
Convert Claude's Markdown output to each channel's native text syntax before delivery. Adds zero-dependency formatting for WhatsApp, Telegram, and Slack (marker substitution). Also ships a Signal rich-text helper (parseSignalStyles) used by the Signal skill.
git clone https://github.com/qwibitai/nanoclaw
T=$(mktemp -d) && git clone --depth=1 https://github.com/qwibitai/nanoclaw "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/channel-formatting" ~/.claude/skills/qwibitai-nanoclaw-channel-formatting && rm -rf "$T"
.claude/skills/channel-formatting/SKILL.mdChannel Formatting
This skill wires channel-aware Markdown conversion into the outbound pipeline so Claude's responses render natively on each platform — no more literal
**asterisks** in WhatsApp or
Telegram.
| Channel | Transformation |
|---|---|
→ , → , headings → bold, links → | |
| Telegram | same as WhatsApp, but links are preserved (Markdown v1 renders them natively) |
| Slack | same as WhatsApp, but links become |
| Discord | passthrough (Discord already renders Markdown) |
| Signal | passthrough for ; in produces plain text + native ranges for use by the Signal skill |
Code blocks (fenced and inline) are always protected — their content is never transformed.
Phase 1: Pre-flight
Check if already applied
test -f src/text-styles.ts && echo "already applied" || echo "not yet applied"
If
already applied, skip to Phase 3 (Verify).
Phase 2: Apply Code Changes
Ensure the upstream remote
git remote -v
If an
upstream remote pointing to https://github.com/qwibitai/nanoclaw.git is missing,
add it:
git remote add upstream https://github.com/qwibitai/nanoclaw.git
Merge the skill branch
git fetch upstream skill/channel-formatting git merge upstream/skill/channel-formatting
If there are merge conflicts on
package-lock.json, resolve them by accepting the incoming
version and continuing:
git checkout --theirs package-lock.json git add package-lock.json git merge --continue
For any other conflict, read the conflicted file and reconcile both sides manually.
This merge adds:
—src/text-styles.ts
for marker substitution andparseTextStyles(text, channel)
for Signal native rich textparseSignalStyles(text)
—src/router.ts
gains an optionalformatOutbound
parameter; when provided it callschannel
after strippingparseTextStyles
tags<internal>
— both outboundsrc/index.ts
paths passsendMessage
tochannel.nameformatOutbound
— test coverage for both functions across all channelssrc/formatting.test.ts
Validate
npm install npm run build npx vitest run src/formatting.test.ts
All 73 tests should pass and the build should be clean before continuing.
Phase 3: Verify
Rebuild and restart
npm run build launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS # Linux: systemctl --user restart nanoclaw
Spot-check formatting
Send a message through any registered WhatsApp or Telegram chat that will trigger a response from Claude. Ask something that will produce formatted output, such as:
Summarise the three main advantages of TypeScript using bullet points and bold headings.
Confirm that the response arrives with native bold (
*text*) rather than raw double
asterisks.
Check logs if needed
tail -f logs/nanoclaw.log
Signal Skill Integration
If you have the Signal skill installed,
src/channels/signal.ts can import
parseSignalStyles from the newly present src/text-styles.ts:
import { parseSignalStyles, SignalTextStyle } from '../text-styles.js';
parseSignalStyles returns { text: string, textStyle: SignalTextStyle[] } where
textStyle is an array of { style, start, length } objects suitable for the
signal-cli JSON-RPC textStyles parameter (format: "start:length:STYLE").
Removal
# Remove the new file rm src/text-styles.ts # Revert router.ts to remove the channel param git diff upstream/main src/router.ts # review changes git checkout upstream/main -- src/router.ts # Revert the index.ts sendMessage call sites to plain formatOutbound(rawText) # (edit manually or: git checkout upstream/main -- src/index.ts) npm run build