From 075934a7197b8d06963550ef512dddad410d3dae Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Tue, 30 Jun 2026 21:19:27 +0500 Subject: [PATCH] fix(integrations): cline hook note collapses onto instruction at EOF The hook-note injection regex matches the line terminator via (\r\n|\n|$), so the captured eol group is empty when the instruction is the final line of a file with no trailing newline. The cline integration emitted the note with that empty eol, mashing the note text and the instruction onto a single line. Default eol to '\n', matching the agy integration twin which already guards this case. Co-Authored-By: Claude Opus 4.8 --- src/specify_cli/integrations/cline/__init__.py | 6 +++++- tests/integrations/test_integration_cline.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/specify_cli/integrations/cline/__init__.py b/src/specify_cli/integrations/cline/__init__.py index ab839b9b56..a9b43e99ad 100644 --- a/src/specify_cli/integrations/cline/__init__.py +++ b/src/specify_cli/integrations/cline/__init__.py @@ -96,7 +96,11 @@ def _inject_hook_command_note(content: str) -> str: def repl(m: re.Match[str]) -> str: indent = m.group(1) instruction = m.group(2) - eol = m.group(3) + # ``eol`` is empty when the regex matched via ``$`` because the + # instruction was the final line of a file with no trailing + # newline. Default to ``\n`` so the note never collapses onto + # the same line as the instruction. + eol = m.group(3) or "\n" return ( indent + _HOOK_COMMAND_NOTE.rstrip("\n") diff --git a/tests/integrations/test_integration_cline.py b/tests/integrations/test_integration_cline.py index 21148df865..7475b5ad02 100644 --- a/tests/integrations/test_integration_cline.py +++ b/tests/integrations/test_integration_cline.py @@ -90,6 +90,22 @@ def test_cline_hook_instruction_injection(self): assert "replace dots (`.`) with hyphens (`-`)" in injected assert "- For each executable hook, output the following:" in injected + def test_cline_hook_instruction_injection_no_trailing_newline(self): + """Note must not collapse onto the instruction line when the + instruction is the final line with no trailing newline. + + The injection regex matches the end-of-line via ``(\\r\\n|\\n|$)``, so + the captured ``eol`` is empty on a file's last line that lacks a + trailing newline. Without an ``or "\\n"`` fallback the note text and + the instruction are emitted on the same line. + """ + cline = get_integration("cline") + content = "- For each executable hook, output the following:" # no trailing \n + injected = cline._inject_hook_command_note(content) + assert "replace dots (`.`) with hyphens (`-`)" in injected + # Instruction stays on its own line rather than being mashed onto the note. + assert "\n- For each executable hook, output the following:" in injected + # -- Overrides for MarkdownIntegrationTests --------------------------- def test_setup_creates_files(self, tmp_path):