diff --git a/src/specify_cli/agents.py b/src/specify_cli/agents.py index bb25b3fc1..4b869283c 100644 --- a/src/specify_cli/agents.py +++ b/src/specify_cli/agents.py @@ -191,8 +191,9 @@ def render_toml_command( toml_lines = [] if "description" in frontmatter: - desc = frontmatter["description"].replace('"', '\\"') - toml_lines.append(f'description = "{desc}"') + toml_lines.append( + f'description = {self._render_basic_toml_string(frontmatter["description"])}' + ) toml_lines.append("") toml_lines.append(f"# Source: {source_id}") @@ -209,17 +210,22 @@ def render_toml_command( toml_lines.append(body) toml_lines.append("'''") else: - escaped_body = ( - body.replace("\\", "\\\\") - .replace('"', '\\"') - .replace("\n", "\\n") - .replace("\r", "\\r") - .replace("\t", "\\t") - ) - toml_lines.append(f'prompt = "{escaped_body}"') + toml_lines.append(f"prompt = {self._render_basic_toml_string(body)}") return "\n".join(toml_lines) + @staticmethod + def _render_basic_toml_string(value: str) -> str: + """Render *value* as a TOML basic string literal.""" + escaped = ( + value.replace("\\", "\\\\") + .replace('"', '\\"') + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t") + ) + return f'"{escaped}"' + def render_skill_command( self, agent_name: str, diff --git a/tests/test_extensions.py b/tests/test_extensions.py index 350b368ea..6ebb39789 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -13,6 +13,7 @@ import json import tempfile import shutil +import tomllib from pathlib import Path from datetime import datetime, timezone @@ -1014,6 +1015,21 @@ def test_render_toml_command_escapes_when_both_triple_quote_styles_exist(self): assert "\\n" in output assert "\\\"\\\"\\\"" in output + def test_render_toml_command_preserves_multiline_description(self): + """Multiline descriptions should render as parseable TOML with preserved semantics.""" + from specify_cli.agents import CommandRegistrar as AgentCommandRegistrar + + registrar = AgentCommandRegistrar() + output = registrar.render_toml_command( + {"description": "first line\nsecond line\n"}, + "body", + "extension:test-ext", + ) + + parsed = tomllib.loads(output) + + assert parsed["description"] == "first line\nsecond line\n" + def test_register_commands_for_claude(self, extension_dir, project_dir): """Test registering commands for Claude agent.""" # Create .claude directory