.claude/skills/utf8-double-encoding-fix/SKILL.md
Fix UTF-8 double-encoding corruption where special characters like arrows (→, ↔) become garbled sequences like "âÂÂ" or "â†Â". Use when: (1) Non-ASCII characters display as mojibake after migration/serialization, (2) Arrows, emojis, or accented characters become Ã-prefixed garbage, (3) Content looks correct in source but corrupted after processing through gray-matter, YAML, or text pipelines. Covers detection via hex inspection and fix via latin-1 decode chain.
npx skillsauth add Dbochman/dotfiles utf8-double-encoding-fixInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
UTF-8 characters become garbled after passing through text processing pipelines.
For example, the arrow → (U+2192) becomes â or similar mojibake sequences.
This happens when UTF-8 bytes are incorrectly interpreted as Latin-1 (ISO-8859-1) and then re-encoded as UTF-8, sometimes multiple times.
âÂÂ, é, ü instead of →, é, üCheck the raw bytes to understand the corruption level:
with open('corrupted-file.md', 'rb') as f:
content = f.read()
# Find corrupted section
pos = content.find(b'55 ') # or other known text near corruption
print("Bytes:", content[pos:pos+20].hex())
print("As UTF-8:", content[pos:pos+20].decode('utf-8', errors='replace'))
Corruption patterns:
c3 a2 c2 86 c2 92 = double-encoded → (one decode needed)c3 83 c2 a2 c3 82 c2 86 c3 82 c2 92 = triple-encoded → (two decodes needed)The fix is to decode as Latin-1 then re-encode as UTF-8, repeating until clean:
import os
files_to_fix = ['file1.md', 'file2.md']
for filepath in files_to_fix:
with open(filepath, 'rb') as f:
content = f.read()
text = content.decode('utf-8')
# Check if it contains double-encoded UTF-8 (Ã pattern)
while 'Ã' in text:
try:
text = text.encode('latin-1').decode('utf-8')
except (UnicodeDecodeError, UnicodeEncodeError):
break # Can't decode further
with open(filepath, 'w', encoding='utf-8') as f:
f.write(text)
print(f"Fixed: {filepath}")
with open('fixed-file.md', 'r', encoding='utf-8') as f:
content = f.read()
# Check for proper arrow character
if '→' in content:
print("✓ Arrow character restored")
elif 'Ã' in content:
print("✗ Still corrupted - may need another decode pass")
After fixing:
grep "→" should find the arrows (not grep "Ã")Before (corrupted):
55 â 98 Lighthouse, system fonts
Blog LCP 5.6s â 3.1s (45% faster)
After (fixed):
55 → 98 Lighthouse, system fonts
Blog LCP 5.6s → 3.1s (45% faster)
The encoding chain that causes this:
→ stored as UTF-8 bytes e2 86 92â, †, 'c3 a2 c2 86 c2 92â when displayedIf this happens twice (triple-encoding), you need two decode passes.
charset=utf-8 in Content-Type header when sending to GitHub APIencoding='utf-8' when reading/writing files in Pythonfs.readFileSync(path, 'utf-8') explicitlyContent-Type: application/json; charset=utf-8\xe2\x86\x92 (escaped bytes), it's a different issue -
the file was written in binary mode or bytes weren't decoded at all? or �, the data was actually lost (replacement character)
and may not be recoverabletools
Use exact configured Reolink cameras through the local Home Hub for availability and power status, fresh stills, visual commentary, protected Dylan/Julia/household sharing, and reversible spotlight control. Supports trusted owner tasks and explicitly scoped proactive automations; not for Nest or Ring cameras, arbitrary recipients, recordings, account changes, or raw camera APIs.
data-ai
Privately manage Dylan and Julia's household plant inventory and care history by physical location, bed, and exact Flower Cam view. Use for confirmed plant onboarding from camera conversations, camera- or bed-filtered inventory, record corrections, individual or whole-bed care, and private filtered exports. Pair with reolink-camera when an owner asks about plants visible in Flower Cam images.
testing
Inspect and control the physically secured Reachy Mini at Crosstown through ClawBody. Use for requests to check Reachy, look around, express an emotion, play any official emotion or dance preset, speak proactively, mute or unmute its microphone, stop movement, or describe what its camera sees.
tools
Handle Reachy/iMessage handoffs, selective durable memory, forgetting, and diagnostics; automatic context comes from the gateway plugin.