From 49803c1f6938e5168b2cbfd223692539f1fc1297 Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Mon, 20 Apr 2026 22:42:17 +0500 Subject: [PATCH 1/4] fix(integrations): strip UTF-8 BOM when reading agent context files --- src/specify_cli/integrations/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/specify_cli/integrations/base.py b/src/specify_cli/integrations/base.py index 4c71b165e5..a3d8a42aa2 100644 --- a/src/specify_cli/integrations/base.py +++ b/src/specify_cli/integrations/base.py @@ -482,7 +482,7 @@ def upsert_context_section( ) if ctx_path.exists(): - content = ctx_path.read_text(encoding="utf-8") + content = ctx_path.read_text(encoding="utf-8-sig") start_idx = content.find(self.CONTEXT_MARKER_START) end_idx = content.find( self.CONTEXT_MARKER_END, @@ -547,7 +547,7 @@ def remove_context_section(self, project_root: Path) -> bool: if not ctx_path.exists(): return False - content = ctx_path.read_text(encoding="utf-8") + content = ctx_path.read_text(encoding="utf-8-sig") start_idx = content.find(self.CONTEXT_MARKER_START) end_idx = content.find( self.CONTEXT_MARKER_END, From 26eda3e0aca18214b1770c0037b53b7e18122d17 Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Tue, 21 Apr 2026 01:23:30 +0500 Subject: [PATCH 2/4] test(integrations): add BOM regression tests for context file read/write --- tests/integrations/test_integration_claude.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/integrations/test_integration_claude.py b/tests/integrations/test_integration_claude.py index 153983dcf4..973a7a4e4a 100644 --- a/tests/integrations/test_integration_claude.py +++ b/tests/integrations/test_integration_claude.py @@ -1,5 +1,6 @@ """Tests for ClaudeIntegration.""" +import codecs import json import os from unittest.mock import patch @@ -74,6 +75,47 @@ def test_setup_upserts_context_section(self, tmp_path): assert "" in content assert "read the current plan" in content + def test_upsert_context_section_strips_bom(self, tmp_path): + """Existing context file with UTF-8 BOM must be cleaned up on upsert.""" + integration = get_integration("claude") + ctx_path = tmp_path / integration.context_file + + # Write a file that starts with a UTF-8 BOM (as the old PowerShell script did) + bom = codecs.BOM_UTF8 + ctx_path.write_bytes(bom + b"# CLAUDE.md\n\nSome existing content.\n") + + manifest = IntegrationManifest("claude", tmp_path) + integration.upsert_context_section(tmp_path) + + result = ctx_path.read_bytes() + assert not result.startswith(bom), "BOM must be stripped after upsert" + content = result.decode("utf-8") + assert "" in content + assert "Some existing content." in content + + def test_remove_context_section_strips_bom(self, tmp_path): + """remove_context_section must clean BOM from context file on Windows-authored files.""" + integration = get_integration("claude") + ctx_path = tmp_path / integration.context_file + + marker_content = ( + "# CLAUDE.md\n\n" + "\n" + "For additional context about technologies to be used, project structure,\n" + "shell commands, and other important information, read the current plan\n" + "\n" + ) + ctx_path.write_bytes(codecs.BOM_UTF8 + marker_content.encode("utf-8")) + + result = integration.remove_context_section(tmp_path) + + assert result is True + assert ctx_path.exists(), "File should exist (non-empty content remains)" + remaining = ctx_path.read_bytes() + assert not remaining.startswith(codecs.BOM_UTF8), "BOM must be stripped after remove" + assert b"