From 2d56dfd73db47e455e06461132e7f68530fb49cf Mon Sep 17 00:00:00 2001 From: Ali jawwad <33836051+jawwad-ali@users.noreply.github.com> Date: Wed, 1 Jul 2026 01:44:49 +0500 Subject: [PATCH] fix(integrations): cline hook note collapses onto instruction at EOF (#3263) 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 ab839b9b5..a9b43e99a 100644 --- a/src/specify_cli/integrations/cline/__init__.py +++ b/src/specify_cli/integrations/cline/__init__.py @@ -96,7 +96,11 @@ class ClineIntegration(MarkdownIntegration): 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 21148df86..7475b5ad0 100644 --- a/tests/integrations/test_integration_cline.py +++ b/tests/integrations/test_integration_cline.py @@ -90,6 +90,22 @@ class TestClineIntegration(MarkdownIntegrationTests): 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):