diff --git a/src/basic_memory/services/entity_service.py b/src/basic_memory/services/entity_service.py index 8082d610..eb86545f 100644 --- a/src/basic_memory/services/entity_service.py +++ b/src/basic_memory/services/entity_service.py @@ -141,10 +141,23 @@ class EntityService(BaseService[EntityModel]): # Convert file path string to Path file_path = Path(entity.file_path) + # Read existing frontmatter from the file if it exists + existing_markdown = await self.entity_parser.parse_file(file_path) + + # Create post with new content from schema post = await schema_to_markdown(schema) + + # Merge new metadata with existing metadata + existing_markdown.frontmatter.metadata.update(post.metadata) + + # Create a new post with merged metadata + merged_post = frontmatter.Post( + post.content, + **existing_markdown.frontmatter.metadata + ) # write file - final_content = frontmatter.dumps(post, sort_keys=False) + final_content = frontmatter.dumps(merged_post, sort_keys=False) checksum = await self.file_service.write_file(file_path, final_content) # parse entity from file @@ -309,4 +322,4 @@ class EntityService(BaseService[EntityModel]): ) continue - return await self.repository.get_by_file_path(path) + return await self.repository.get_by_file_path(path) \ No newline at end of file diff --git a/tests/mcp/test_tool_notes.py b/tests/mcp/test_tool_notes.py index 903dd2a9..a2f96a91 100644 --- a/tests/mcp/test_tool_notes.py +++ b/tests/mcp/test_tool_notes.py @@ -252,3 +252,72 @@ async def test_write_note_verbose(app): """).strip() in result ) + + +@pytest.mark.asyncio +async def test_write_note_preserves_custom_metadata(app, test_config): + """Test that updating a note preserves custom metadata fields. + + Reproduces issue #36 where custom frontmatter fields like Status + were being lost when updating notes with the write_note tool. + + Should: + - Create a note with custom frontmatter + - Update the note with new content + - Verify custom frontmatter is preserved + """ + # First, create a note with custom metadata using write_note + await write_note( + title="Custom Metadata Note", + folder="test", + content="# Initial content", + tags=["test"], + ) + + # Read the note to get its permalink + content = await read_note("test/custom-metadata-note") + + # Now directly update the file with custom frontmatter + # We need to use a direct file update to add custom frontmatter + from pathlib import Path + import frontmatter + + file_path = test_config.home / "test" / "Custom Metadata Note.md" + post = frontmatter.load(file_path) + + # Add custom frontmatter + post["Status"] = "In Progress" + post["Priority"] = "High" + post["Version"] = "1.0" + + # Write the file back + with open(file_path, "w") as f: + f.write(frontmatter.dumps(post)) + + # Now update the note using write_note + result = await write_note( + title="Custom Metadata Note", + folder="test", + content="# Updated content", + tags=["test", "updated"], + ) + + # Verify the update was successful + assert "Updated test/Custom Metadata Note.md" in result + + # Read the note back and check if custom frontmatter is preserved + content = await read_note("test/custom-metadata-note") + + # Custom frontmatter should be preserved + assert "Status: In Progress" in content + assert "Priority: High" in content + # Version might be quoted as '1.0' due to YAML serialization + assert "Version:" in content # Just check that the field exists + assert "1.0" in content # And that the value exists somewhere + + # And new content should be there + assert "# Updated content" in content + + # And tags should be updated + assert "'#test'" in content + assert "'#updated'" in content \ No newline at end of file