@@ -141,6 +141,91 @@ def test_skill_uses_template_descriptions(self, tmp_path):
141141 assert isinstance (fm ["description" ], str )
142142 assert len (fm ["description" ]) > 0 , f"{ f } has empty description"
143143
144+ def test_skill_frontmatter_preserves_multiline_description (
145+ self , tmp_path , monkeypatch
146+ ):
147+ """A multiline (block-scalar) description must round-trip exactly.
148+
149+ The hand-built SKILL.md frontmatter used to only escape backslash and
150+ quote, so a block-scalar description was emitted with raw newlines inside
151+ a double-quoted scalar and reparsed with those newlines collapsed to
152+ spaces. The description must survive byte-for-byte."""
153+ from pathlib import Path
154+
155+ i = get_integration (self .KEY )
156+ # Hermes writes to ~/.hermes/skills/ — isolate Path.home() to prevent
157+ # overwriting a developer's real global skill directory.
158+ if self .KEY == "hermes" :
159+ home = tmp_path / "home"
160+ home .mkdir (exist_ok = True )
161+ monkeypatch .setattr (Path , "home" , lambda : home )
162+
163+ template = tmp_path / "sample.md"
164+ template .write_text (
165+ "---\n "
166+ "description: |\n "
167+ " first line\n "
168+ " second line\n "
169+ "scripts:\n "
170+ " sh: scripts/bash/x.sh\n "
171+ "---\n "
172+ "Body\n " ,
173+ encoding = "utf-8" ,
174+ )
175+ monkeypatch .setattr (i , "list_command_templates" , lambda : [template ])
176+
177+ m = IntegrationManifest (self .KEY , tmp_path )
178+ created = i .setup (tmp_path , m )
179+ skill_files = [f for f in created if f .name == "SKILL.md" ]
180+ assert len (skill_files ) == 1
181+
182+ content = skill_files [0 ].read_text (encoding = "utf-8" )
183+ fm = yaml .safe_load (content .split ("---" , 2 )[1 ])
184+ assert "\n " in fm ["description" ]
185+ assert fm ["description" ] == "first line\n second line\n "
186+
187+ def test_skill_frontmatter_preserves_control_characters (
188+ self , tmp_path , monkeypatch
189+ ):
190+ """A description carrying a C0/DEL control char must round-trip exactly.
191+
192+ A control character can reach ``description`` via a YAML escape in the
193+ source template (``"a\\ x08b"`` parses to a real U+0008). The old
194+ hand-built frontmatter only escaped backslash and quote, so the raw
195+ control char landed inside the emitted double-quoted scalar and made the
196+ SKILL.md unparseable / lossy. ``yaml_quote`` must escape it so the
197+ value survives byte-for-byte."""
198+ from pathlib import Path
199+
200+ i = get_integration (self .KEY )
201+ # Hermes writes to ~/.hermes/skills/ — isolate Path.home() to prevent
202+ # overwriting a developer's real global skill directory.
203+ if self .KEY == "hermes" :
204+ home = tmp_path / "home"
205+ home .mkdir (exist_ok = True )
206+ monkeypatch .setattr (Path , "home" , lambda : home )
207+
208+ template = tmp_path / "sample.md"
209+ template .write_text (
210+ "---\n "
211+ 'description: "a\\ x08b\\ ttab"\n '
212+ "scripts:\n "
213+ " sh: scripts/bash/x.sh\n "
214+ "---\n "
215+ "Body\n " ,
216+ encoding = "utf-8" ,
217+ )
218+ monkeypatch .setattr (i , "list_command_templates" , lambda : [template ])
219+
220+ m = IntegrationManifest (self .KEY , tmp_path )
221+ created = i .setup (tmp_path , m )
222+ skill_files = [f for f in created if f .name == "SKILL.md" ]
223+ assert len (skill_files ) == 1
224+
225+ content = skill_files [0 ].read_text (encoding = "utf-8" )
226+ fm = yaml .safe_load (content .split ("---" , 2 )[1 ])
227+ assert fm ["description" ] == "a\x08 b\t tab"
228+
144229 def test_templates_are_processed (self , tmp_path ):
145230 """Skill body must have placeholders replaced, not raw templates."""
146231 i = get_integration (self .KEY )
0 commit comments