Skip to content

Commit 32b4146

Browse files
authored
Merge branch 'main' into fix-issue-70-20250406011950
2 parents 8caeb7e + 9bff1f7 commit 32b4146

6 files changed

Lines changed: 151 additions & 9 deletions

File tree

README.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ Basic Memory lets you build persistent knowledge through natural conversations w
1313
Claude, while keeping everything in simple Markdown files on your computer. It uses the Model Context Protocol (MCP) to
1414
enable any compatible LLM to read and write to your local knowledge base.
1515

16-
- Website: http://basicmachines.co
17-
- Documentation: http://memory.basicmachines.co
16+
- Website: https://basicmachines.co
17+
- Documentation: https://memory.basicmachines.co
1818

1919
## Pick up your conversation right where you left off
2020

@@ -262,6 +262,43 @@ Examples of relations:
262262
- documented_in [[Coffee Journal]]
263263
```
264264

265+
## Using with VS Code
266+
For one-click installation, click one of the install buttons below...
267+
268+
[![Install with UV in VS Code](https://img.shields.io/badge/VS_Code-UV-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D) [![Install with UV in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-UV-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D&quality=insiders)
269+
270+
You can use Basic Memory with VS Code to easily retrieve and store information while coding. Click the installation buttons above for one-click setup, or follow the manual installation instructions below.
271+
272+
### Manual Installation
273+
274+
Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`.
275+
276+
```json
277+
{
278+
"mcp": {
279+
"servers": {
280+
"basic-memory": {
281+
"command": "uvx",
282+
"args": ["basic-memory", "mcp"]
283+
}
284+
}
285+
}
286+
}
287+
```
288+
289+
Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace. This will allow you to share the configuration with others.
290+
291+
```json
292+
{
293+
"servers": {
294+
"basic-memory": {
295+
"command": "uvx",
296+
"args": ["basic-memory", "mcp"]
297+
}
298+
}
299+
}
300+
```
301+
265302
## Using with Claude Desktop
266303

267304
Basic Memory is built using the MCP (Model Context Protocol) and works with the Claude desktop app (https://claude.ai/):

src/basic_memory/markdown/entity_parser.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ def parse_date(self, value: Any) -> Optional[datetime]:
9292
async def parse_file(self, path: Path | str) -> EntityMarkdown:
9393
"""Parse markdown file into EntityMarkdown."""
9494

95-
absolute_path = self.base_path / path
95+
# Check if the path is already absolute
96+
if isinstance(path, Path) and path.is_absolute() or (isinstance(path, str) and Path(path).is_absolute()):
97+
absolute_path = Path(path)
98+
else:
99+
absolute_path = self.base_path / path
100+
96101
# Parse frontmatter and content using python-frontmatter
97102
post = frontmatter.load(str(absolute_path))
98103

src/basic_memory/mcp/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from mcp.server.fastmcp.utilities.logging import configure_logging
55

66
# mcp console logging
7-
configure_logging(level="INFO")
7+
configure_logging(level="ERROR")
88

99

1010
# Create the shared server instance
11-
mcp = FastMCP("Basic Memory")
11+
mcp = FastMCP("Basic Memory", log_level="ERROR")

src/basic_memory/sync/sync_service.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,18 +362,25 @@ async def sync_markdown_file(self, path: str, new: bool = True) -> Tuple[Optiona
362362
# Update relations and search index
363363
entity = await self.entity_service.update_entity_relations(path, entity_markdown)
364364

365+
# After updating relations, we need to compute the checksum again
366+
# This is necessary for files with wikilinks to ensure consistent checksums
367+
# after relation processing is complete
368+
final_checksum = await self.file_service.compute_checksum(path)
369+
365370
# set checksum
366-
await self.entity_repository.update(entity.id, {"checksum": checksum})
367-
371+
await self.entity_repository.update(entity.id, {"checksum": final_checksum})
372+
368373
logger.debug(
369374
"Markdown sync completed",
370375
path=path,
371376
entity_id=entity.id,
372377
observation_count=len(entity.observations),
373378
relation_count=len(entity.relations),
379+
checksum=final_checksum,
374380
)
375-
376-
return entity, checksum
381+
382+
# Return the final checksum to ensure everything is consistent
383+
return entity, final_checksum
377384

378385
async def sync_regular_file(self, path: str, new: bool = True) -> Tuple[Optional[Entity], str]:
379386
"""Sync a non-markdown file with basic tracking.

tests/markdown/test_entity_parser.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,37 @@ def test_parse_empty_content():
217217
assert len(result.relations) == 0
218218

219219

220+
@pytest.mark.asyncio
221+
async def test_parse_file_with_absolute_path(test_config, entity_parser):
222+
"""Test parsing a file with an absolute path."""
223+
content = dedent("""
224+
---
225+
type: component
226+
permalink: absolute_path_test
227+
---
228+
229+
# Absolute Path Test
230+
231+
A file with an absolute path.
232+
""")
233+
234+
# Create a test file in the project directory
235+
test_file = test_config.home / "absolute_path_test.md"
236+
test_file.write_text(content)
237+
238+
# Get the absolute path to the test file
239+
absolute_path = test_file.resolve()
240+
241+
# Parse the file using the absolute path
242+
entity = await entity_parser.parse_file(absolute_path)
243+
244+
# Verify the file was parsed correctly
245+
assert entity.frontmatter.permalink == "absolute_path_test"
246+
assert "Absolute Path Test" in entity.content
247+
assert entity.created is not None
248+
assert entity.modified is not None
249+
250+
220251
# @pytest.mark.asyncio
221252
# async def test_parse_file_invalid_yaml(test_config, entity_parser):
222253
# """Test parsing file with invalid YAML frontmatter."""
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Test for issue #72 - notes with wikilinks staying in modified status."""
2+
3+
import pytest
4+
from pathlib import Path
5+
6+
from basic_memory.sync.sync_service import SyncService
7+
8+
9+
async def create_test_file(path: Path, content: str) -> None:
10+
"""Create a test file with given content."""
11+
path.parent.mkdir(parents=True, exist_ok=True)
12+
path.write_text(content)
13+
14+
15+
@pytest.mark.asyncio
16+
async def test_wikilink_modified_status_issue(sync_service: SyncService, test_config):
17+
"""Test that files with wikilinks don't remain in modified status after sync."""
18+
project_dir = test_config.home
19+
20+
# Create a file with a wikilink
21+
content = """---
22+
title: Test Wikilink
23+
type: note
24+
---
25+
# Test File
26+
27+
This file contains a wikilink to [[another-file]].
28+
"""
29+
test_file_path = project_dir / "test_wikilink.md"
30+
await create_test_file(test_file_path, content)
31+
32+
# Initial sync
33+
report1 = await sync_service.sync(test_config.home, show_progress=False)
34+
assert "test_wikilink.md" in report1.new
35+
assert "test_wikilink.md" not in report1.modified
36+
37+
# Sync again without changing the file - should not be modified
38+
report2 = await sync_service.sync(test_config.home, show_progress=False)
39+
assert "test_wikilink.md" not in report2.new
40+
assert "test_wikilink.md" not in report2.modified
41+
42+
# Create the target file
43+
target_content = """---
44+
title: Another File
45+
type: note
46+
---
47+
# Another File
48+
49+
This is the target file.
50+
"""
51+
target_file_path = project_dir / "another_file.md"
52+
await create_test_file(target_file_path, content)
53+
54+
# Sync again after adding target file
55+
report3 = await sync_service.sync(test_config.home, show_progress=False)
56+
assert "another_file.md" in report3.new
57+
assert "test_wikilink.md" not in report3.modified
58+
59+
# Sync one more time - both files should now be stable
60+
report4 = await sync_service.sync(test_config.home, show_progress=False)
61+
assert "test_wikilink.md" not in report4.modified
62+
assert "another_file.md" not in report4.modified

0 commit comments

Comments
 (0)