Skip to content

Commit 034fbfc

Browse files
jawwad-aliclaude
andauthored
fix: render valid TOML when a command body contains backslashes (#3135)
render_toml_command() emitted the body inside a multiline *basic* TOML string ("""..."""), which processes backslash escape sequences. A command body containing a backslash — e.g. a Windows path like C:\Users\... whose \U reads as an invalid unicode escape — therefore produced unparseable TOML ("Invalid hex value"), so the generated Gemini/Tabnine command file failed to load. A body ending in a backslash also silently ate the closing newline via TOML line-continuation. Route bodies containing a backslash to the multiline *literal* form ('''...'''), which does not process escapes, or to the escaped basic string when both triple-quote styles are present. Mirrors the escaping already done by base.py's TomlIntegration. Add tests covering a Windows path, a trailing backslash, and the backslash + both-triple-quote-styles fallback. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8e76ff3 commit 034fbfc

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

src/specify_cli/agents.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,14 @@ def render_toml_command(self, frontmatter: dict, body: str, source_id: str) -> s
236236
toml_lines.append(f"# Source: {source_id}")
237237
toml_lines.append("")
238238

239-
# Keep TOML output valid even when body contains triple-quote delimiters.
240-
# Prefer multiline forms, then fall back to escaped basic string.
241-
if '"""' not in body:
239+
# Keep TOML output valid even when body contains triple-quote delimiters
240+
# or backslashes. Prefer multiline forms, then fall back to escaped basic
241+
# string. A multiline *basic* string ("""...""") processes backslash escape
242+
# sequences, so a body containing a backslash (e.g. a Windows path
243+
# ``C:\\Users\\...`` whose ``\\U`` reads as an invalid unicode escape) would
244+
# produce unparseable TOML — route those to the *literal* form ('''...'''),
245+
# which does not process escapes, or to the escaped basic string.
246+
if '"""' not in body and "\\" not in body:
242247
toml_lines.append('prompt = """')
243248
toml_lines.append(body)
244249
toml_lines.append('"""')

tests/test_extensions.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,47 @@ def test_render_toml_command_preserves_multiline_description(self):
16691669

16701670
assert parsed["description"] == "first line\nsecond line\n"
16711671

1672+
def test_render_toml_command_preserves_backslashes_in_body(self):
1673+
"""A backslash in the body (e.g. a Windows path) must not break TOML.
1674+
1675+
A multiline basic string ("\"\"\"") processes backslash escapes, so
1676+
``C:\\Users`` (``\\U``) would render as invalid TOML; the body must
1677+
round-trip with backslashes intact.
1678+
"""
1679+
from specify_cli.agents import CommandRegistrar as AgentCommandRegistrar
1680+
1681+
registrar = AgentCommandRegistrar()
1682+
output = registrar.render_toml_command(
1683+
{"description": "x"},
1684+
r"Run C:\Users\dev\tool.exe then report.",
1685+
"extension:test-ext",
1686+
)
1687+
parsed = tomllib.loads(output) # must not raise
1688+
assert parsed["prompt"].strip() == r"Run C:\Users\dev\tool.exe then report."
1689+
1690+
def test_render_toml_command_handles_trailing_backslash(self):
1691+
"""A body ending in a backslash must round-trip without corruption."""
1692+
from specify_cli.agents import CommandRegistrar as AgentCommandRegistrar
1693+
1694+
registrar = AgentCommandRegistrar()
1695+
output = registrar.render_toml_command(
1696+
{"description": "x"},
1697+
"path ends with sep\\",
1698+
"extension:test-ext",
1699+
)
1700+
parsed = tomllib.loads(output)
1701+
assert parsed["prompt"].strip() == "path ends with sep\\"
1702+
1703+
def test_render_toml_command_backslash_with_both_triple_quotes_escapes(self):
1704+
"""Body with a backslash and both triple-quote styles → escaped basic string."""
1705+
from specify_cli.agents import CommandRegistrar as AgentCommandRegistrar
1706+
1707+
registrar = AgentCommandRegistrar()
1708+
body = "a \\ b\nc \"\"\" d\ne ''' f"
1709+
output = registrar.render_toml_command({"description": "x"}, body, "extension:test-ext")
1710+
parsed = tomllib.loads(output)
1711+
assert parsed["prompt"] == body
1712+
16721713
def test_register_commands_for_claude(self, extension_dir, project_dir):
16731714
"""Test registering commands for Claude agent."""
16741715
# Create .claude directory

0 commit comments

Comments
 (0)