basic-memory 0.4.3__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- {basic_memory-0.4.3 → basic_memory-0.5.0}/CHANGELOG.md +11 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/PKG-INFO +1 -1
- {basic_memory-0.4.3 → basic_memory-0.5.0}/pyproject.toml +1 -1
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/__init__.py +1 -1
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/api/routers/resource_router.py +1 -1
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/tools/notes.py +41 -23
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/api/test_resource_router.py +0 -1
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/mcp/test_tool_knowledge.py +11 -10
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/mcp/test_tool_notes.py +129 -66
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/mcp/test_tool_search.py +3 -2
- {basic_memory-0.4.3 → basic_memory-0.5.0}/uv.lock +1 -1
- basic_memory-0.4.3/tests/mcp/test_tool_get_entity.py +0 -45
- {basic_memory-0.4.3 → basic_memory-0.5.0}/.github/workflows/pr-title.yml +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/.github/workflows/release.yml +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/.github/workflows/test.yml +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/.gitignore +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/.python-version +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/CITATION.cff +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/CODE_OF_CONDUCT.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/CONTRIBUTING.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/LICENSE +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/Makefile +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/README.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/basic-memory.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/installer/Basic.icns +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/installer/README.md +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/installer/icon.svg +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/installer/installer.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/installer/make_icons.sh +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/installer/setup.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/memory.json +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/scripts/install.sh +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/alembic/README +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/alembic/env.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/alembic/migrations.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/alembic/script.py.mako +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/api/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/api/app.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/api/routers/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/api/routers/knowledge_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/api/routers/memory_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/api/routers/search_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/app.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/db.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/import_chatgpt.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/import_claude_conversations.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/import_claude_projects.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/import_memory_json.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/mcp.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/status.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/sync.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/main.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/config.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/db.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/deps.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/file_utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/markdown/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/markdown/entity_parser.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/markdown/markdown_processor.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/markdown/plugins.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/markdown/schemas.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/markdown/utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/async_client.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/server.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/tools/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/tools/knowledge.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/tools/memory.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/tools/search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/mcp/tools/utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/models/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/models/base.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/models/knowledge.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/models/search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/entity_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/observation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/relation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/search_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/base.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/delete.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/discovery.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/memory.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/request.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/response.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/schemas/search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/context_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/entity_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/exceptions.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/file_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/link_resolver.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/search_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/services/service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/sync/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/sync/file_change_scanner.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/sync/sync_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/sync/utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/sync/watch_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/api/conftest.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/api/test_knowledge_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/api/test_memory_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/api/test_search_router.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/cli/test_import_chatgpt.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/cli/test_import_claude_conversations.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/cli/test_import_claude_projects.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/cli/test_import_memory_json.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/cli/test_status.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/cli/test_sync.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/conftest.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/edit_file_test.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/__init__.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/test_entity_parser.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/test_markdown_plugins.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/test_markdown_processor.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/test_observation_edge_cases.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/test_parser_edge_cases.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/test_relation_edge_cases.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/markdown/test_task_detection.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/mcp/conftest.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/mcp/test_tool_memory.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/mcp/test_tool_utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/repository/test_entity_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/repository/test_observation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/repository/test_relation_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/repository/test_repository.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/schemas/test_memory_url.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/schemas/test_schemas.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/schemas/test_search.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/services/test_context_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/services/test_entity_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/services/test_file_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/services/test_link_resolver.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/services/test_search_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/sync/test_file_change_scanner.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/sync/test_sync_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/sync/test_watch_service.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/test_basic_memory.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/utils/test_file_utils.py +0 -0
- {basic_memory-0.4.3 → basic_memory-0.5.0}/tests/utils/test_permalink_formatting.py +0 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v0.5.0 (2025-02-18)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
- Return semantic info in markdown after write_note
|
|
9
|
+
([#11](https://github.com/basicmachines-co/basic-memory/pull/11),
|
|
10
|
+
[`0689e7a`](https://github.com/basicmachines-co/basic-memory/commit/0689e7a730497827bf4e16156ae402ddc5949077))
|
|
11
|
+
|
|
12
|
+
Co-authored-by: phernandez <phernandez@basicmachines.co>
|
|
13
|
+
|
|
14
|
+
|
|
4
15
|
## v0.4.3 (2025-02-18)
|
|
5
16
|
|
|
6
17
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-memory
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Local-first knowledge management combining Zettelkasten with knowledge graphs
|
|
5
5
|
Project-URL: Homepage, https://github.com/basicmachines-co/basic-memory
|
|
6
6
|
Project-URL: Repository, https://github.com/basicmachines-co/basic-memory
|
|
@@ -31,7 +31,7 @@ def get_entity_ids(item: SearchIndexRow) -> list[int]:
|
|
|
31
31
|
from_entity = item.from_id
|
|
32
32
|
to_entity = item.to_id # pyright: ignore [reportReturnType]
|
|
33
33
|
return [from_entity, to_entity] if to_entity else [from_entity] # pyright: ignore [reportReturnType]
|
|
34
|
-
case _:
|
|
34
|
+
case _: # pragma: no cover
|
|
35
35
|
raise ValueError(f"Unexpected type: {item.type}")
|
|
36
36
|
|
|
37
37
|
|
|
@@ -17,15 +17,14 @@ from basic_memory.schemas.memory import memory_url_path
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@mcp.tool(
|
|
20
|
-
description="Create or update a markdown note. Returns the
|
|
20
|
+
description="Create or update a markdown note. Returns a markdown formatted summary of the semantic content.",
|
|
21
21
|
)
|
|
22
22
|
async def write_note(
|
|
23
23
|
title: str,
|
|
24
24
|
content: str,
|
|
25
25
|
folder: str,
|
|
26
26
|
tags: Optional[List[str]] = None,
|
|
27
|
-
|
|
28
|
-
) -> EntityResponse | str:
|
|
27
|
+
) -> str:
|
|
29
28
|
"""Write a markdown note to the knowledge base.
|
|
30
29
|
|
|
31
30
|
The content can include semantic observations and relations using markdown syntax.
|
|
@@ -53,14 +52,16 @@ async def write_note(
|
|
|
53
52
|
content: Markdown content for the note, can include observations and relations
|
|
54
53
|
folder: the folder where the file should be saved
|
|
55
54
|
tags: Optional list of tags to categorize the note
|
|
56
|
-
verbose: If True, returns full EntityResponse with semantic info
|
|
57
55
|
|
|
58
56
|
Returns:
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
A markdown formatted summary of the semantic content, including:
|
|
58
|
+
- Creation/update status
|
|
59
|
+
- File path and checksum
|
|
60
|
+
- Observation counts by category
|
|
61
|
+
- Relation counts (resolved/unresolved)
|
|
62
|
+
- Tags if present
|
|
61
63
|
|
|
62
64
|
Examples:
|
|
63
|
-
# Note with both explicit and inline relations
|
|
64
65
|
write_note(
|
|
65
66
|
title="Search Implementation",
|
|
66
67
|
content="# Search Component\\n\\n"
|
|
@@ -73,20 +74,6 @@ async def write_note(
|
|
|
73
74
|
"- depends_on [[Database Schema]]",
|
|
74
75
|
folder="docs/components"
|
|
75
76
|
)
|
|
76
|
-
|
|
77
|
-
# Note with tags
|
|
78
|
-
write_note(
|
|
79
|
-
title="Error Handling Design",
|
|
80
|
-
content="# Error Handling\\n\\n"
|
|
81
|
-
"This design builds on [[Reliability Design]].\\n\\n"
|
|
82
|
-
"## Approach\\n"
|
|
83
|
-
"- [design] Use error codes #architecture\\n"
|
|
84
|
-
"- [tech] Implement retry logic #implementation\\n\\n"
|
|
85
|
-
"## Relations\\n"
|
|
86
|
-
"- extends [[Base Error Handling]]",
|
|
87
|
-
folder="docs/design",
|
|
88
|
-
tags=["architecture", "reliability"]
|
|
89
|
-
)
|
|
90
77
|
"""
|
|
91
78
|
logger.info(f"Writing note folder:'{folder}' title: '{title}'")
|
|
92
79
|
|
|
@@ -101,12 +88,43 @@ async def write_note(
|
|
|
101
88
|
entity_metadata=metadata,
|
|
102
89
|
)
|
|
103
90
|
|
|
104
|
-
#
|
|
91
|
+
# Create or update via knowledge API
|
|
105
92
|
logger.info(f"Creating {entity.permalink}")
|
|
106
93
|
url = f"/knowledge/entities/{entity.permalink}"
|
|
107
94
|
response = await call_put(client, url, json=entity.model_dump())
|
|
108
95
|
result = EntityResponse.model_validate(response.json())
|
|
109
|
-
|
|
96
|
+
|
|
97
|
+
# Format semantic summary based on status code
|
|
98
|
+
action = "Created" if response.status_code == 201 else "Updated"
|
|
99
|
+
assert result.checksum is not None
|
|
100
|
+
summary = [
|
|
101
|
+
f"# {action} {result.file_path} ({result.checksum[:8]})",
|
|
102
|
+
f"permalink: {result.permalink}",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
if result.observations:
|
|
106
|
+
categories = {}
|
|
107
|
+
for obs in result.observations:
|
|
108
|
+
categories[obs.category] = categories.get(obs.category, 0) + 1
|
|
109
|
+
|
|
110
|
+
summary.append("\n## Observations")
|
|
111
|
+
for category, count in sorted(categories.items()):
|
|
112
|
+
summary.append(f"- {category}: {count}")
|
|
113
|
+
|
|
114
|
+
if result.relations:
|
|
115
|
+
unresolved = sum(1 for r in result.relations if not r.to_id)
|
|
116
|
+
resolved = len(result.relations) - unresolved
|
|
117
|
+
|
|
118
|
+
summary.append("\n## Relations")
|
|
119
|
+
summary.append(f"- Resolved: {resolved}")
|
|
120
|
+
if unresolved:
|
|
121
|
+
summary.append(f"- Unresolved: {unresolved}")
|
|
122
|
+
summary.append("\nUnresolved relations will be retried on next sync.")
|
|
123
|
+
|
|
124
|
+
if tags:
|
|
125
|
+
summary.append(f"\n## Tags\n- {', '.join(tags)}")
|
|
126
|
+
|
|
127
|
+
return "\n".join(summary)
|
|
110
128
|
|
|
111
129
|
|
|
112
130
|
@mcp.tool(description="Read note content by title, permalink, relation, or pattern")
|
|
@@ -158,7 +158,6 @@ async def test_get_resource_entities(client, test_config, entity_repository):
|
|
|
158
158
|
entity2 = EntityResponse(**entity_response)
|
|
159
159
|
|
|
160
160
|
assert len(entity2.relations) == 1
|
|
161
|
-
relation = entity2.relations[0]
|
|
162
161
|
|
|
163
162
|
# Test getting the content via the relation
|
|
164
163
|
response = await client.get("/resource/test/*")
|
|
@@ -13,7 +13,7 @@ from basic_memory.schemas.delete import DeleteEntitiesRequest
|
|
|
13
13
|
async def test_get_single_entity(client):
|
|
14
14
|
"""Test retrieving a single entity."""
|
|
15
15
|
# First create an entity
|
|
16
|
-
|
|
16
|
+
result = await notes.write_note(
|
|
17
17
|
title="Test Note",
|
|
18
18
|
folder="test",
|
|
19
19
|
content="""
|
|
@@ -22,9 +22,10 @@ async def test_get_single_entity(client):
|
|
|
22
22
|
""",
|
|
23
23
|
tags=["test", "documentation"],
|
|
24
24
|
)
|
|
25
|
+
assert result
|
|
25
26
|
|
|
26
27
|
# Get the entity
|
|
27
|
-
entity = await get_entity(
|
|
28
|
+
entity = await get_entity("test/test-note")
|
|
28
29
|
|
|
29
30
|
# Verify entity details
|
|
30
31
|
assert entity.title == "Test Note"
|
|
@@ -36,40 +37,40 @@ async def test_get_single_entity(client):
|
|
|
36
37
|
async def test_get_multiple_entities(client):
|
|
37
38
|
"""Test retrieving multiple entities."""
|
|
38
39
|
# Create two test entities
|
|
39
|
-
|
|
40
|
+
await notes.write_note(
|
|
40
41
|
title="Test Note 1",
|
|
41
42
|
folder="test",
|
|
42
43
|
content="# Test 1",
|
|
43
44
|
)
|
|
44
|
-
|
|
45
|
+
await notes.write_note(
|
|
45
46
|
title="Test Note 2",
|
|
46
47
|
folder="test",
|
|
47
48
|
content="# Test 2",
|
|
48
49
|
)
|
|
49
50
|
|
|
50
51
|
# Get both entities
|
|
51
|
-
request = GetEntitiesRequest(permalinks=[
|
|
52
|
+
request = GetEntitiesRequest(permalinks=["test/test-note-1", "test/test-note-2"])
|
|
52
53
|
response = await get_entities(request)
|
|
53
54
|
|
|
54
55
|
# Verify we got both entities
|
|
55
56
|
assert len(response.entities) == 2
|
|
56
57
|
permalinks = {e.permalink for e in response.entities}
|
|
57
|
-
assert
|
|
58
|
-
assert
|
|
58
|
+
assert "test/test-note-1" in permalinks
|
|
59
|
+
assert "test/test-note-2" in permalinks
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
@pytest.mark.asyncio
|
|
62
63
|
async def test_delete_entities(client):
|
|
63
64
|
"""Test deleting entities."""
|
|
64
65
|
# Create a test entity
|
|
65
|
-
|
|
66
|
+
await notes.write_note(
|
|
66
67
|
title="Test Note",
|
|
67
68
|
folder="test",
|
|
68
69
|
content="# Test Note to Delete",
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
# Delete the entity
|
|
72
|
-
request = DeleteEntitiesRequest(permalinks=[
|
|
73
|
+
request = DeleteEntitiesRequest(permalinks=["test/test-note"])
|
|
73
74
|
response = await delete_entities(request)
|
|
74
75
|
|
|
75
76
|
# Verify deletion
|
|
@@ -77,7 +78,7 @@ async def test_delete_entities(client):
|
|
|
77
78
|
|
|
78
79
|
# Verify entity no longer exists
|
|
79
80
|
with pytest.raises(ToolError):
|
|
80
|
-
await get_entity(
|
|
81
|
+
await get_entity("test/test-note")
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
@pytest.mark.asyncio
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""Tests for note tools that exercise the full stack with SQLite."""
|
|
2
2
|
|
|
3
|
+
from textwrap import dedent
|
|
4
|
+
|
|
3
5
|
import pytest
|
|
4
6
|
from mcp.server.fastmcp.exceptions import ToolError
|
|
5
7
|
|
|
6
8
|
from basic_memory.mcp.tools import notes
|
|
7
|
-
from basic_memory.schemas import EntityResponse
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@pytest.mark.asyncio
|
|
@@ -17,31 +18,41 @@ async def test_write_note(app):
|
|
|
17
18
|
- Handle tags correctly
|
|
18
19
|
- Return valid permalink
|
|
19
20
|
"""
|
|
20
|
-
|
|
21
|
+
result = await notes.write_note(
|
|
21
22
|
title="Test Note",
|
|
22
23
|
folder="test",
|
|
23
24
|
content="# Test\nThis is a test note",
|
|
24
25
|
tags=["test", "documentation"],
|
|
25
26
|
)
|
|
26
27
|
|
|
27
|
-
assert
|
|
28
|
+
assert result
|
|
29
|
+
assert (
|
|
30
|
+
dedent("""
|
|
31
|
+
# Created test/Test Note.md (159f2168)
|
|
32
|
+
permalink: test/test-note
|
|
33
|
+
|
|
34
|
+
## Tags
|
|
35
|
+
- test, documentation
|
|
36
|
+
""").strip()
|
|
37
|
+
in result
|
|
38
|
+
)
|
|
28
39
|
|
|
29
40
|
# Try reading it back via permalink
|
|
30
|
-
content = await notes.read_note(
|
|
41
|
+
content = await notes.read_note("test/test-note")
|
|
31
42
|
assert (
|
|
32
|
-
"""
|
|
33
|
-
---
|
|
34
|
-
title: Test Note
|
|
35
|
-
type: note
|
|
36
|
-
permalink: test/test-note
|
|
37
|
-
tags:
|
|
38
|
-
- '#test'
|
|
39
|
-
- '#documentation'
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
# Test
|
|
43
|
-
This is a test note
|
|
44
|
-
""".strip()
|
|
43
|
+
dedent("""
|
|
44
|
+
---
|
|
45
|
+
title: Test Note
|
|
46
|
+
type: note
|
|
47
|
+
permalink: test/test-note
|
|
48
|
+
tags:
|
|
49
|
+
- '#test'
|
|
50
|
+
- '#documentation'
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# Test
|
|
54
|
+
This is a test note
|
|
55
|
+
""").strip()
|
|
45
56
|
in content
|
|
46
57
|
)
|
|
47
58
|
|
|
@@ -49,20 +60,28 @@ This is a test note
|
|
|
49
60
|
@pytest.mark.asyncio
|
|
50
61
|
async def test_write_note_no_tags(app):
|
|
51
62
|
"""Test creating a note without tags."""
|
|
52
|
-
|
|
63
|
+
result = await notes.write_note(title="Simple Note", folder="test", content="Just some text")
|
|
53
64
|
|
|
65
|
+
assert result
|
|
66
|
+
assert (
|
|
67
|
+
dedent("""
|
|
68
|
+
# Created test/Simple Note.md (9a1ff079)
|
|
69
|
+
permalink: test/simple-note
|
|
70
|
+
""").strip()
|
|
71
|
+
in result
|
|
72
|
+
)
|
|
54
73
|
# Should be able to read it back
|
|
55
|
-
content = await notes.read_note(
|
|
74
|
+
content = await notes.read_note("test/simple-note")
|
|
56
75
|
assert (
|
|
57
|
-
"""
|
|
58
|
-
--
|
|
59
|
-
title: Simple Note
|
|
60
|
-
type: note
|
|
61
|
-
permalink: test/simple-note
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
Just some text
|
|
65
|
-
""".strip()
|
|
76
|
+
dedent("""
|
|
77
|
+
--
|
|
78
|
+
title: Simple Note
|
|
79
|
+
type: note
|
|
80
|
+
permalink: test/simple-note
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
Just some text
|
|
84
|
+
""").strip()
|
|
66
85
|
in content
|
|
67
86
|
)
|
|
68
87
|
|
|
@@ -84,24 +103,44 @@ async def test_write_note_update_existing(app):
|
|
|
84
103
|
- Handle tags correctly
|
|
85
104
|
- Return valid permalink
|
|
86
105
|
"""
|
|
87
|
-
|
|
106
|
+
result = await notes.write_note(
|
|
88
107
|
title="Test Note",
|
|
89
108
|
folder="test",
|
|
90
109
|
content="# Test\nThis is a test note",
|
|
91
110
|
tags=["test", "documentation"],
|
|
92
111
|
)
|
|
93
112
|
|
|
94
|
-
assert
|
|
113
|
+
assert result # Got a valid permalink
|
|
114
|
+
assert (
|
|
115
|
+
dedent("""
|
|
116
|
+
# Created test/Test Note.md (159f2168)
|
|
117
|
+
permalink: test/test-note
|
|
118
|
+
|
|
119
|
+
## Tags
|
|
120
|
+
- test, documentation
|
|
121
|
+
""").strip()
|
|
122
|
+
in result
|
|
123
|
+
)
|
|
95
124
|
|
|
96
|
-
|
|
125
|
+
result = await notes.write_note(
|
|
97
126
|
title="Test Note",
|
|
98
127
|
folder="test",
|
|
99
128
|
content="# Test\nThis is an updated note",
|
|
100
129
|
tags=["test", "documentation"],
|
|
101
130
|
)
|
|
131
|
+
assert (
|
|
132
|
+
dedent("""
|
|
133
|
+
# Updated test/Test Note.md (131b5662)
|
|
134
|
+
permalink: test/test-note
|
|
135
|
+
|
|
136
|
+
## Tags
|
|
137
|
+
- test, documentation
|
|
138
|
+
""").strip()
|
|
139
|
+
in result
|
|
140
|
+
)
|
|
102
141
|
|
|
103
142
|
# Try reading it back
|
|
104
|
-
content = await notes.read_note(
|
|
143
|
+
content = await notes.read_note("test/test-note")
|
|
105
144
|
assert (
|
|
106
145
|
"""
|
|
107
146
|
---
|
|
@@ -135,10 +174,18 @@ async def test_read_note_by_title(app):
|
|
|
135
174
|
async def test_note_unicode_content(app):
|
|
136
175
|
"""Test handling of unicode content in notes."""
|
|
137
176
|
content = "# Test 🚀\nThis note has emoji 🎉 and unicode ♠♣♥♦"
|
|
138
|
-
|
|
177
|
+
result = await notes.write_note(title="Unicode Test", folder="test", content=content)
|
|
178
|
+
|
|
179
|
+
assert (
|
|
180
|
+
dedent("""
|
|
181
|
+
# Created test/Unicode Test.md (272389cd)
|
|
182
|
+
permalink: test/unicode-test
|
|
183
|
+
""").strip()
|
|
184
|
+
in result
|
|
185
|
+
)
|
|
139
186
|
|
|
140
187
|
# Read back should preserve unicode
|
|
141
|
-
result = await notes.read_note(
|
|
188
|
+
result = await notes.read_note("test/unicode-test")
|
|
142
189
|
assert content in result
|
|
143
190
|
|
|
144
191
|
|
|
@@ -147,20 +194,32 @@ async def test_multiple_notes(app):
|
|
|
147
194
|
"""Test creating and managing multiple notes."""
|
|
148
195
|
# Create several notes
|
|
149
196
|
notes_data = [
|
|
150
|
-
("Note 1", "test", "Content 1", ["tag1"]),
|
|
151
|
-
("Note 2", "test", "Content 2", ["tag1", "tag2"]),
|
|
152
|
-
("Note 3", "test", "Content 3", []),
|
|
197
|
+
("test/note-1", "Note 1", "test", "Content 1", ["tag1"]),
|
|
198
|
+
("test/note-2", "Note 2", "test", "Content 2", ["tag1", "tag2"]),
|
|
199
|
+
("test/note-3", "Note 3", "test", "Content 3", []),
|
|
153
200
|
]
|
|
154
201
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
permalink = await notes.write_note(title=title, folder=folder, content=content, tags=tags)
|
|
158
|
-
permalinks.append(permalink)
|
|
202
|
+
for _, title, folder, content, tags in notes_data:
|
|
203
|
+
await notes.write_note(title=title, folder=folder, content=content, tags=tags)
|
|
159
204
|
|
|
160
205
|
# Should be able to read each one
|
|
161
|
-
for
|
|
162
|
-
|
|
163
|
-
assert
|
|
206
|
+
for permalink, title, folder, content, _ in notes_data:
|
|
207
|
+
note = await notes.read_note(permalink)
|
|
208
|
+
assert content in note
|
|
209
|
+
|
|
210
|
+
# read multiple notes at once
|
|
211
|
+
|
|
212
|
+
result = await notes.read_note("test/*")
|
|
213
|
+
|
|
214
|
+
# note we can't compare times
|
|
215
|
+
assert "--- memory://test/note-1" in result
|
|
216
|
+
assert "Content 1" in result
|
|
217
|
+
|
|
218
|
+
assert "--- memory://test/note-2" in result
|
|
219
|
+
assert "Content 2" in result
|
|
220
|
+
|
|
221
|
+
assert "--- memory://test/note-3" in result
|
|
222
|
+
assert "Content 3" in result
|
|
164
223
|
|
|
165
224
|
|
|
166
225
|
@pytest.mark.asyncio
|
|
@@ -172,16 +231,16 @@ async def test_delete_note_existing(app):
|
|
|
172
231
|
- Return valid permalink
|
|
173
232
|
- Delete the note
|
|
174
233
|
"""
|
|
175
|
-
|
|
234
|
+
result = await notes.write_note(
|
|
176
235
|
title="Test Note",
|
|
177
236
|
folder="test",
|
|
178
237
|
content="# Test\nThis is a test note",
|
|
179
238
|
tags=["test", "documentation"],
|
|
180
239
|
)
|
|
181
240
|
|
|
182
|
-
assert
|
|
241
|
+
assert result
|
|
183
242
|
|
|
184
|
-
deleted = await notes.delete_note(
|
|
243
|
+
deleted = await notes.delete_note("test/test-note")
|
|
185
244
|
assert deleted is True
|
|
186
245
|
|
|
187
246
|
|
|
@@ -207,7 +266,7 @@ async def test_write_note_verbose(app):
|
|
|
207
266
|
- Handle tags correctly
|
|
208
267
|
- Return valid permalink
|
|
209
268
|
"""
|
|
210
|
-
|
|
269
|
+
result = await notes.write_note(
|
|
211
270
|
title="Test Note",
|
|
212
271
|
folder="test",
|
|
213
272
|
content="""
|
|
@@ -218,24 +277,27 @@ async def test_write_note_verbose(app):
|
|
|
218
277
|
|
|
219
278
|
""",
|
|
220
279
|
tags=["test", "documentation"],
|
|
221
|
-
verbose=True,
|
|
222
280
|
)
|
|
223
281
|
|
|
224
|
-
assert
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
282
|
+
assert (
|
|
283
|
+
dedent("""
|
|
284
|
+
# Created test/Test Note.md (06873a7a)
|
|
285
|
+
permalink: test/test-note
|
|
286
|
+
|
|
287
|
+
## Observations
|
|
288
|
+
- note: 1
|
|
289
|
+
|
|
290
|
+
## Relations
|
|
291
|
+
- Resolved: 0
|
|
292
|
+
- Unresolved: 1
|
|
293
|
+
|
|
294
|
+
Unresolved relations will be retried on next sync.
|
|
295
|
+
|
|
296
|
+
## Tags
|
|
297
|
+
- test, documentation
|
|
298
|
+
""").strip()
|
|
299
|
+
in result
|
|
300
|
+
)
|
|
239
301
|
|
|
240
302
|
|
|
241
303
|
@pytest.mark.asyncio
|
|
@@ -248,13 +310,14 @@ async def test_read_note_memory_url(app):
|
|
|
248
310
|
- Return the note content
|
|
249
311
|
"""
|
|
250
312
|
# First create a note
|
|
251
|
-
|
|
313
|
+
result = await notes.write_note(
|
|
252
314
|
title="Memory URL Test",
|
|
253
315
|
folder="test",
|
|
254
316
|
content="Testing memory:// URL handling",
|
|
255
317
|
)
|
|
318
|
+
assert result
|
|
256
319
|
|
|
257
320
|
# Should be able to read it with a memory:// URL
|
|
258
|
-
memory_url =
|
|
321
|
+
memory_url = "memory://test/memory-url-test"
|
|
259
322
|
content = await notes.read_note(memory_url)
|
|
260
323
|
assert "Testing memory:// URL handling" in content
|
|
@@ -12,12 +12,13 @@ from basic_memory.schemas.search import SearchQuery, SearchItemType
|
|
|
12
12
|
async def test_search_basic(client):
|
|
13
13
|
"""Test basic search functionality."""
|
|
14
14
|
# Create a test note
|
|
15
|
-
|
|
15
|
+
result = await notes.write_note(
|
|
16
16
|
title="Test Search Note",
|
|
17
17
|
folder="test",
|
|
18
18
|
content="# Test\nThis is a searchable test note",
|
|
19
19
|
tags=["test", "search"],
|
|
20
20
|
)
|
|
21
|
+
assert result
|
|
21
22
|
|
|
22
23
|
# Search for it
|
|
23
24
|
query = SearchQuery(text="searchable")
|
|
@@ -25,7 +26,7 @@ async def test_search_basic(client):
|
|
|
25
26
|
|
|
26
27
|
# Verify results
|
|
27
28
|
assert len(response.results) > 0
|
|
28
|
-
assert any(r.permalink ==
|
|
29
|
+
assert any(r.permalink == "test/test-search-note" for r in response.results)
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
@pytest.mark.asyncio
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"""Tests for get_entity MCP tool."""
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
from mcp.server.fastmcp.exceptions import ToolError
|
|
5
|
-
|
|
6
|
-
from basic_memory.mcp.tools import notes
|
|
7
|
-
from basic_memory.mcp.tools.knowledge import get_entity
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@pytest.mark.asyncio
|
|
11
|
-
async def test_get_basic_entity(client):
|
|
12
|
-
"""Test retrieving a basic entity."""
|
|
13
|
-
# First create an entity
|
|
14
|
-
permalink = await notes.write_note(
|
|
15
|
-
title="Test Note",
|
|
16
|
-
folder="test",
|
|
17
|
-
content="""
|
|
18
|
-
# Test\nThis is a test note
|
|
19
|
-
- [note] First observation
|
|
20
|
-
""",
|
|
21
|
-
tags=["test", "documentation"],
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
assert permalink # Got a valid permalink
|
|
25
|
-
|
|
26
|
-
# Get the entity without content
|
|
27
|
-
entity = await get_entity(permalink)
|
|
28
|
-
|
|
29
|
-
# Verify entity details
|
|
30
|
-
assert entity.file_path == "test/Test Note.md"
|
|
31
|
-
assert entity.entity_type == "note"
|
|
32
|
-
assert entity.permalink == "test/test-note"
|
|
33
|
-
|
|
34
|
-
# Check observations
|
|
35
|
-
assert len(entity.observations) == 1
|
|
36
|
-
obs = entity.observations[0]
|
|
37
|
-
assert obs.content == "First observation"
|
|
38
|
-
assert obs.category == "note"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@pytest.mark.asyncio
|
|
42
|
-
async def test_get_nonexistent_entity(client):
|
|
43
|
-
"""Test attempting to get a non-existent entity."""
|
|
44
|
-
with pytest.raises(ToolError):
|
|
45
|
-
await get_entity("test/nonexistent")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/import_claude_projects.py
RENAMED
|
File without changes
|
{basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/cli/commands/import_memory_json.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/observation_repository.py
RENAMED
|
File without changes
|
{basic_memory-0.4.3 → basic_memory-0.5.0}/src/basic_memory/repository/relation_repository.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|