Skip to content

fix: write binary skill other-files without UTF-8 corruption - #2568

Merged
dyoshikawa merged 1 commit into
dyoshikawa:mainfrom
magellen-ai:fix/binary-other-files-utf8-corruption
Aug 4, 2026
Merged

fix: write binary skill other-files without UTF-8 corruption#2568
dyoshikawa merged 1 commit into
dyoshikawa:mainfrom
magellen-ai:fix/binary-other-files-utf8-corruption

Conversation

@magellen-ai

Copy link
Copy Markdown

Problem

Skill other files (anything besides SKILL.md inside a skill directory — e.g. wechat-qrcode.jpg, assets/hero.gif) are corrupted on both import and generate.

DirFeatureProcessor.writeAiDirs reads and writes those files through a UTF-8 text path:

  • comparison: readFileContentOrNull (UTF-8) + fileContentsEquivalent
  • write: file.fileBuffer.toString("utf-8") then writeFileContent (UTF-8)

Invalid byte sequences in binary content are replaced with U+FFFD during the round-trip, so a 50 KB JPEG silently becomes a ~87 KB file with a broken header:

$ file .rulesync/skills/andrej/wechat-qrcode.jpg
.rulesync/skills/andrej/wechat-qrcode.jpg: data

Fix

Route binary other files through a buffer path while keeping text files on the existing text path:

  • isBinaryBuffer() (new, in utils/file.ts) — detects whether a UTF-8 toString + Buffer.from round-trip would alter the bytes (valid UTF-8 text incl. CJK round-trips unchanged; JPEG/GIF etc. do not)
  • change detection for binary files now compares raw bytes via Buffer.equals using a new readFileBufferOrNull()
  • writes go through the existing-but-unused writeFileBuffer()

Verification

  • Added unit tests: binary other-file written via buffer path / skipped when unchanged / detected when changed; existing text-path tests unchanged
  • Full suite: 8232 passed (2 pre-existing failures are spawn bun ENOENT — bun not installed in this environment, unrelated)
  • oxfmt, oxlint, tsgo (typecheck), tsdown (build) all clean
  • End-to-end check on a real workspace: import + generate round-trip of skills containing wechat-qrcode.jpg and assets/hero.gif — files remain valid (file reports JPEG/GIF) and md5 matches the source

Skill other files (e.g. images inside a skill directory) were read and
written through a UTF-8 text path: the buffer was decoded with
toString('utf-8') and written back with writeFileContent, so invalid byte
sequences were replaced with U+FFFD and binary files were silently
corrupted on both import and generate.

Route binary other files through a buffer path instead:
- isBinaryBuffer() detects content that a UTF-8 round-trip would corrupt
- change detection compares bytes via Buffer.equals (readFileBufferOrNull)
- writes go through writeFileBuffer, keeping text files on the existing
  text path (trailing-newline normalization + structured equivalence)
@dyoshikawa
dyoshikawa merged commit e7b8de9 into dyoshikawa:main Aug 4, 2026
6 checks passed
@dyoshikawa

Copy link
Copy Markdown
Owner

@greenhands-always Thank you!

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.

2 participants