basic-memory 0.5.0__py3-none-any.whl → 0.7.0__py3-none-any.whl

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.

Files changed (36) hide show
  1. basic_memory/__init__.py +1 -1
  2. basic_memory/api/app.py +7 -0
  3. basic_memory/api/routers/knowledge_router.py +0 -8
  4. basic_memory/api/routers/memory_router.py +26 -10
  5. basic_memory/api/routers/resource_router.py +14 -8
  6. basic_memory/api/routers/search_router.py +17 -9
  7. basic_memory/cli/app.py +1 -1
  8. basic_memory/cli/commands/db.py +11 -8
  9. basic_memory/cli/commands/import_chatgpt.py +31 -27
  10. basic_memory/cli/commands/import_claude_conversations.py +29 -27
  11. basic_memory/cli/commands/import_claude_projects.py +30 -29
  12. basic_memory/cli/commands/import_memory_json.py +28 -26
  13. basic_memory/cli/commands/status.py +8 -6
  14. basic_memory/cli/commands/sync.py +6 -3
  15. basic_memory/cli/commands/tools.py +157 -0
  16. basic_memory/cli/main.py +1 -4
  17. basic_memory/config.py +5 -0
  18. basic_memory/db.py +1 -0
  19. basic_memory/deps.py +5 -1
  20. basic_memory/mcp/tools/knowledge.py +26 -14
  21. basic_memory/mcp/tools/memory.py +48 -29
  22. basic_memory/mcp/tools/notes.py +66 -72
  23. basic_memory/mcp/tools/search.py +13 -4
  24. basic_memory/repository/search_repository.py +3 -0
  25. basic_memory/schemas/memory.py +3 -0
  26. basic_memory/schemas/request.py +1 -1
  27. basic_memory/schemas/search.py +2 -0
  28. basic_memory/services/context_service.py +14 -6
  29. basic_memory/services/search_service.py +3 -1
  30. basic_memory/sync/sync_service.py +98 -89
  31. basic_memory/utils.py +32 -4
  32. {basic_memory-0.5.0.dist-info → basic_memory-0.7.0.dist-info}/METADATA +2 -1
  33. {basic_memory-0.5.0.dist-info → basic_memory-0.7.0.dist-info}/RECORD +36 -35
  34. {basic_memory-0.5.0.dist-info → basic_memory-0.7.0.dist-info}/WHEEL +0 -0
  35. {basic_memory-0.5.0.dist-info → basic_memory-0.7.0.dist-info}/entry_points.txt +0 -0
  36. {basic_memory-0.5.0.dist-info → basic_memory-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,6 @@
1
1
  """Search tools for Basic Memory MCP server."""
2
2
 
3
+ import logfire
3
4
  from loguru import logger
4
5
 
5
6
  from basic_memory.mcp.server import mcp
@@ -11,7 +12,7 @@ from basic_memory.mcp.async_client import client
11
12
  @mcp.tool(
12
13
  description="Search across all content in basic-memory, including documents and entities",
13
14
  )
14
- async def search(query: SearchQuery) -> SearchResponse:
15
+ async def search(query: SearchQuery, page: int = 1, page_size: int = 10) -> SearchResponse:
15
16
  """Search across all content in basic-memory.
16
17
 
17
18
  Args:
@@ -20,10 +21,18 @@ async def search(query: SearchQuery) -> SearchResponse:
20
21
  - types: Optional list of content types to search ("document" or "entity")
21
22
  - entity_types: Optional list of entity types to filter by
22
23
  - after_date: Optional date filter for recent content
24
+ page: the page number of results to return (default 1)
25
+ page_size: the number of results to return per page (default 10)
23
26
 
24
27
  Returns:
25
28
  SearchResponse with search results and metadata
26
29
  """
27
- logger.info(f"Searching for {query.text}")
28
- response = await call_post(client, "/search/", json=query.model_dump())
29
- return SearchResponse.model_validate(response.json())
30
+ with logfire.span("Searching for {query}", query=query): # pyright: ignore [reportGeneralTypeIssues]
31
+ logger.info(f"Searching for {query}")
32
+ response = await call_post(
33
+ client,
34
+ "/search/",
35
+ json=query.model_dump(),
36
+ params={"page": page, "page_size": page_size},
37
+ )
38
+ return SearchResponse.model_validate(response.json())
@@ -114,6 +114,7 @@ class SearchRepository:
114
114
  after_date: Optional[datetime] = None,
115
115
  entity_types: Optional[List[str]] = None,
116
116
  limit: int = 10,
117
+ offset: int = 0,
117
118
  ) -> List[SearchIndexRow]:
118
119
  """Search across all indexed content with fuzzy matching."""
119
120
  conditions = []
@@ -169,6 +170,7 @@ class SearchRepository:
169
170
 
170
171
  # set limit on search query
171
172
  params["limit"] = limit
173
+ params["offset"] = offset
172
174
 
173
175
  # Build WHERE clause
174
176
  where_clause = " AND ".join(conditions) if conditions else "1=1"
@@ -194,6 +196,7 @@ class SearchRepository:
194
196
  WHERE {where_clause}
195
197
  ORDER BY score ASC {order_by_clause}
196
198
  LIMIT :limit
199
+ OFFSET :offset
197
200
  """
198
201
 
199
202
  logger.debug(f"Search {sql} params: {params}")
@@ -111,3 +111,6 @@ class GraphContext(BaseModel):
111
111
 
112
112
  # Context metadata
113
113
  metadata: MemoryMetadata
114
+
115
+ page: int = 1
116
+ page_size: int = 1
@@ -51,7 +51,7 @@ class GetEntitiesRequest(BaseModel):
51
51
  discovered through search.
52
52
  """
53
53
 
54
- permalinks: Annotated[List[Permalink], MinLen(1)]
54
+ permalinks: Annotated[List[Permalink], MinLen(1), MaxLen(10)]
55
55
 
56
56
 
57
57
  class CreateRelationsRequest(BaseModel):
@@ -102,6 +102,8 @@ class SearchResponse(BaseModel):
102
102
  """Wrapper for search results."""
103
103
 
104
104
  results: List[SearchResult]
105
+ current_page: int
106
+ page_size: int
105
107
 
106
108
 
107
109
  # Schema for future advanced search endpoint
@@ -54,11 +54,13 @@ class ContextService:
54
54
  types: Optional[List[SearchItemType]] = None,
55
55
  depth: int = 1,
56
56
  since: Optional[datetime] = None,
57
- max_results: int = 10,
57
+ limit=10,
58
+ offset=0,
59
+ max_related: int = 10,
58
60
  ):
59
61
  """Build rich context from a memory:// URI."""
60
62
  logger.debug(
61
- f"Building context for URI: '{memory_url}' depth: '{depth}' since: '{since}' max_results: '{max_results}'"
63
+ f"Building context for URI: '{memory_url}' depth: '{depth}' since: '{since}' limit: '{limit}' offset: '{offset}' max_related: '{max_related}'"
62
64
  )
63
65
 
64
66
  if memory_url:
@@ -66,15 +68,21 @@ class ContextService:
66
68
  # Pattern matching - use search
67
69
  if "*" in path:
68
70
  logger.debug(f"Pattern search for '{path}'")
69
- primary = await self.search_repository.search(permalink_match=path)
71
+ primary = await self.search_repository.search(
72
+ permalink_match=path, limit=limit, offset=offset
73
+ )
70
74
 
71
75
  # Direct lookup for exact path
72
76
  else:
73
77
  logger.debug(f"Direct lookup for '{path}'")
74
- primary = await self.search_repository.search(permalink=path)
78
+ primary = await self.search_repository.search(
79
+ permalink=path, limit=limit, offset=offset
80
+ )
75
81
  else:
76
82
  logger.debug(f"Build context for '{types}'")
77
- primary = await self.search_repository.search(types=types, after_date=since)
83
+ primary = await self.search_repository.search(
84
+ types=types, after_date=since, limit=limit, offset=offset
85
+ )
78
86
 
79
87
  # Get type_id pairs for traversal
80
88
 
@@ -83,7 +91,7 @@ class ContextService:
83
91
 
84
92
  # Find related content
85
93
  related = await self.find_related(
86
- type_id_pairs, max_depth=depth, since=since, max_results=max_results
94
+ type_id_pairs, max_depth=depth, since=since, max_results=max_related
87
95
  )
88
96
  logger.debug(f"Found {len(related)} related results")
89
97
  for r in related:
@@ -51,7 +51,7 @@ class SearchService:
51
51
 
52
52
  logger.info("Reindex complete")
53
53
 
54
- async def search(self, query: SearchQuery) -> List[SearchIndexRow]:
54
+ async def search(self, query: SearchQuery, limit=10, offset=0) -> List[SearchIndexRow]:
55
55
  """Search across all indexed content.
56
56
 
57
57
  Supports three modes:
@@ -84,6 +84,8 @@ class SearchService:
84
84
  types=query.types,
85
85
  entity_types=query.entity_types,
86
86
  after_date=after_date,
87
+ limit=limit,
88
+ offset=offset,
87
89
  )
88
90
 
89
91
  return results
@@ -3,6 +3,7 @@
3
3
  from pathlib import Path
4
4
  from typing import Dict
5
5
 
6
+ import logfire
6
7
  from loguru import logger
7
8
  from sqlalchemy.exc import IntegrityError
8
9
 
@@ -61,105 +62,113 @@ class SyncService:
61
62
 
62
63
  async def sync(self, directory: Path) -> SyncReport:
63
64
  """Sync knowledge files with database."""
64
- changes = await self.scanner.find_knowledge_changes(directory)
65
- logger.info(f"Found {changes.total_changes} knowledge changes")
66
-
67
- # Handle moves first
68
- for old_path, new_path in changes.moves.items():
69
- logger.debug(f"Moving entity: {old_path} -> {new_path}")
70
- entity = await self.entity_repository.get_by_file_path(old_path)
71
- if entity:
72
- # Update file_path but keep the same permalink for link stability
73
- updated = await self.entity_repository.update(
74
- entity.id, {"file_path": new_path, "checksum": changes.checksums[new_path]}
65
+
66
+ with logfire.span("sync", directory=directory): # pyright: ignore [reportGeneralTypeIssues]
67
+ changes = await self.scanner.find_knowledge_changes(directory)
68
+ logger.info(f"Found {changes.total_changes} knowledge changes")
69
+
70
+ # Handle moves first
71
+ for old_path, new_path in changes.moves.items():
72
+ logger.debug(f"Moving entity: {old_path} -> {new_path}")
73
+ entity = await self.entity_repository.get_by_file_path(old_path)
74
+ if entity:
75
+ # Update file_path but keep the same permalink for link stability
76
+ updated = await self.entity_repository.update(
77
+ entity.id, {"file_path": new_path, "checksum": changes.checksums[new_path]}
78
+ )
79
+ # update search index
80
+ if updated:
81
+ await self.search_service.index_entity(updated)
82
+
83
+ # Handle deletions next
84
+ # remove rows from db for files no longer present
85
+ for path in changes.deleted:
86
+ await self.handle_entity_deletion(path)
87
+
88
+ # Parse files that need updating
89
+ parsed_entities: Dict[str, EntityMarkdown] = {}
90
+
91
+ for path in [*changes.new, *changes.modified]:
92
+ entity_markdown = await self.entity_parser.parse_file(directory / path)
93
+ parsed_entities[path] = entity_markdown
94
+
95
+ # First pass: Create/update entities
96
+ # entities will have a null checksum to indicate they are not complete
97
+ for path, entity_markdown in parsed_entities.items():
98
+ # Get unique permalink and update markdown if needed
99
+ permalink = await self.entity_service.resolve_permalink(
100
+ Path(path), markdown=entity_markdown
75
101
  )
76
- # update search index
77
- if updated:
78
- await self.search_service.index_entity(updated)
79
-
80
- # Handle deletions next
81
- # remove rows from db for files no longer present
82
- for path in changes.deleted:
83
- await self.handle_entity_deletion(path)
84
-
85
- # Parse files that need updating
86
- parsed_entities: Dict[str, EntityMarkdown] = {}
87
-
88
- for path in [*changes.new, *changes.modified]:
89
- entity_markdown = await self.entity_parser.parse_file(directory / path)
90
- parsed_entities[path] = entity_markdown
91
-
92
- # First pass: Create/update entities
93
- # entities will have a null checksum to indicate they are not complete
94
- for path, entity_markdown in parsed_entities.items():
95
- # Get unique permalink and update markdown if needed
96
- permalink = await self.entity_service.resolve_permalink(
97
- Path(path), markdown=entity_markdown
98
- )
99
102
 
100
- if permalink != entity_markdown.frontmatter.permalink:
101
- # Add/update permalink in frontmatter
102
- logger.info(f"Adding permalink '{permalink}' to file: {path}")
103
+ if permalink != entity_markdown.frontmatter.permalink:
104
+ # Add/update permalink in frontmatter
105
+ logger.info(f"Adding permalink '{permalink}' to file: {path}")
103
106
 
104
- # update markdown
105
- entity_markdown.frontmatter.metadata["permalink"] = permalink
107
+ # update markdown
108
+ entity_markdown.frontmatter.metadata["permalink"] = permalink
106
109
 
107
- # update file frontmatter
108
- updated_checksum = await file_utils.update_frontmatter(
109
- directory / path, {"permalink": permalink}
110
- )
110
+ # update file frontmatter
111
+ updated_checksum = await file_utils.update_frontmatter(
112
+ directory / path, {"permalink": permalink}
113
+ )
111
114
 
112
- # Update checksum in changes report since file was modified
113
- changes.checksums[path] = updated_checksum
114
-
115
- # if the file is new, create an entity
116
- if path in changes.new:
117
- # Create entity with final permalink
118
- logger.debug(f"Creating new entity_markdown: {path}")
119
- await self.entity_service.create_entity_from_markdown(Path(path), entity_markdown)
120
- # otherwise we need to update the entity and observations
121
- else:
122
- logger.debug(f"Updating entity_markdown: {path}")
123
- await self.entity_service.update_entity_and_observations(
124
- Path(path), entity_markdown
125
- )
115
+ # Update checksum in changes report since file was modified
116
+ changes.checksums[path] = updated_checksum
126
117
 
127
- # Second pass
128
- for path, entity_markdown in parsed_entities.items():
129
- logger.debug(f"Updating relations for: {path}")
118
+ # if the file is new, create an entity
119
+ if path in changes.new:
120
+ # Create entity with final permalink
121
+ logger.debug(f"Creating new entity_markdown: {path}")
122
+ await self.entity_service.create_entity_from_markdown(
123
+ Path(path), entity_markdown
124
+ )
125
+ # otherwise we need to update the entity and observations
126
+ else:
127
+ logger.debug(f"Updating entity_markdown: {path}")
128
+ await self.entity_service.update_entity_and_observations(
129
+ Path(path), entity_markdown
130
+ )
130
131
 
131
- # Process relations
132
- checksum = changes.checksums[path]
133
- entity = await self.entity_service.update_entity_relations(Path(path), entity_markdown)
132
+ # Second pass
133
+ for path, entity_markdown in parsed_entities.items():
134
+ logger.debug(f"Updating relations for: {path}")
134
135
 
135
- # add to search index
136
- await self.search_service.index_entity(entity)
136
+ # Process relations
137
+ checksum = changes.checksums[path]
138
+ entity = await self.entity_service.update_entity_relations(
139
+ Path(path), entity_markdown
140
+ )
137
141
 
138
- # Set final checksum to mark sync complete
139
- await self.entity_repository.update(entity.id, {"checksum": checksum})
142
+ # add to search index
143
+ await self.search_service.index_entity(entity)
140
144
 
141
- # Third pass: Try to resolve any forward references
142
- logger.debug("Attempting to resolve forward references")
143
- for relation in await self.relation_repository.find_unresolved_relations():
144
- target_entity = await self.entity_service.link_resolver.resolve_link(relation.to_name)
145
- # check we found a link that is not the source
146
- if target_entity and target_entity.id != relation.from_id:
147
- logger.debug(
148
- f"Resolved forward reference: {relation.to_name} -> {target_entity.permalink}"
149
- )
145
+ # Set final checksum to mark sync complete
146
+ await self.entity_repository.update(entity.id, {"checksum": checksum})
150
147
 
151
- try:
152
- await self.relation_repository.update(
153
- relation.id,
154
- {
155
- "to_id": target_entity.id,
156
- "to_name": target_entity.title, # Update to actual title
157
- },
148
+ # Third pass: Try to resolve any forward references
149
+ logger.debug("Attempting to resolve forward references")
150
+ for relation in await self.relation_repository.find_unresolved_relations():
151
+ target_entity = await self.entity_service.link_resolver.resolve_link(
152
+ relation.to_name
153
+ )
154
+ # check we found a link that is not the source
155
+ if target_entity and target_entity.id != relation.from_id:
156
+ logger.debug(
157
+ f"Resolved forward reference: {relation.to_name} -> {target_entity.permalink}"
158
158
  )
159
- except IntegrityError:
160
- logger.debug(f"Ignoring duplicate relation {relation}")
161
-
162
- # update search index
163
- await self.search_service.index_entity(target_entity)
164
159
 
165
- return changes
160
+ try:
161
+ await self.relation_repository.update(
162
+ relation.id,
163
+ {
164
+ "to_id": target_entity.id,
165
+ "to_name": target_entity.title, # Update to actual title
166
+ },
167
+ )
168
+ except IntegrityError:
169
+ logger.debug(f"Ignoring duplicate relation {relation}")
170
+
171
+ # update search index
172
+ await self.search_service.index_entity(target_entity)
173
+
174
+ return changes
basic_memory/utils.py CHANGED
@@ -9,8 +9,11 @@ from typing import Optional, Union
9
9
  from loguru import logger
10
10
  from unidecode import unidecode
11
11
 
12
+ import basic_memory
12
13
  from basic_memory.config import config
13
14
 
15
+ import logfire
16
+
14
17
 
15
18
  def generate_permalink(file_path: Union[Path, str]) -> str:
16
19
  """Generate a stable permalink from a file path.
@@ -61,19 +64,42 @@ def generate_permalink(file_path: Union[Path, str]) -> str:
61
64
  return "/".join(clean_segments)
62
65
 
63
66
 
64
- def setup_logging(home_dir: Path = config.home, log_file: Optional[str] = None) -> None:
67
+ def setup_logging(
68
+ home_dir: Path = config.home, log_file: Optional[str] = None, console: bool = True
69
+ ) -> None: # pragma: no cover
65
70
  """
66
71
  Configure logging for the application.
72
+ :param home_dir: the root directory for the application
73
+ :param log_file: the name of the log file to write to
74
+ :param app: the fastapi application instance
75
+ :param console: whether to log to the console
67
76
  """
68
77
 
69
78
  # Remove default handler and any existing handlers
70
79
  logger.remove()
71
80
 
72
- # Add file handler
73
- if log_file:
81
+ # Add file handler if we are not running tests
82
+ if log_file and config.env != "test":
83
+ # enable pydantic logfire
84
+ logfire.configure(
85
+ code_source=logfire.CodeSource(
86
+ repository="https://github.com/basicmachines-co/basic-memory",
87
+ revision=basic_memory.__version__,
88
+ root_path="/src/basic_memory",
89
+ ),
90
+ environment=config.env,
91
+ console=False,
92
+ )
93
+ logger.configure(handlers=[logfire.loguru_handler()])
94
+
95
+ # instrument code spans
96
+ logfire.instrument_sqlite3()
97
+ logfire.instrument_httpx()
98
+
99
+ # setup logger
74
100
  log_path = home_dir / log_file
75
101
  logger.add(
76
- str(log_path), # loguru expects a string path
102
+ str(log_path),
77
103
  level=config.log_level,
78
104
  rotation="100 MB",
79
105
  retention="10 days",
@@ -85,3 +111,5 @@ def setup_logging(home_dir: Path = config.home, log_file: Optional[str] = None)
85
111
 
86
112
  # Add stderr handler
87
113
  logger.add(sys.stderr, level=config.log_level, backtrace=True, diagnose=True, colorize=True)
114
+
115
+ logger.info(f"ENV: '{config.env}' Log level: '{config.log_level}' Logging to {log_file}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basic-memory
3
- Version: 0.5.0
3
+ Version: 0.7.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
@@ -15,6 +15,7 @@ Requires-Dist: dateparser>=1.2.0
15
15
  Requires-Dist: fastapi[standard]>=0.115.8
16
16
  Requires-Dist: greenlet>=3.1.1
17
17
  Requires-Dist: icecream>=2.1.3
18
+ Requires-Dist: logfire[fastapi,httpx,sqlalchemy,sqlite3]>=3.6.0
18
19
  Requires-Dist: loguru>=0.7.3
19
20
  Requires-Dist: markdown-it-py>=3.0.0
20
21
  Requires-Dist: mcp>=1.2.0
@@ -1,33 +1,34 @@
1
- basic_memory/__init__.py,sha256=oSbUpibWuITY-SoYtwzq1d3ISgJIVK5Ft8pFz6SYZ1A,122
2
- basic_memory/config.py,sha256=PZA2qgwKACvKfRcM3H-BPB_8FYVhgZAwTmlKJ3ROfhU,1643
3
- basic_memory/db.py,sha256=IK_gz8Uiwcgxe8TjarW7kpl8cVVNtEnR0lm1LemgZ8I,5283
4
- basic_memory/deps.py,sha256=UzivBw6e6iYcU_8SQ8LNCmSsmFyHfjdzfWvnfNzqbRc,5375
1
+ basic_memory/__init__.py,sha256=2LnWS_j9nC8P5BSYmW2D9Ccx3xNj4gPQf_GElvfJcgU,122
2
+ basic_memory/config.py,sha256=lgihYA80A5uzSzUc3GE7aEWogndocv7J0uyYK1JCp68,1793
3
+ basic_memory/db.py,sha256=EVX3pgA2rah4tZpxy5wKvZSN8vVcrvo5l-hKjAXBb0U,5284
4
+ basic_memory/deps.py,sha256=8LkcfppQEJpiflYZxLk-SmQuL04qknbsdfzIkM_ctuY,5530
5
5
  basic_memory/file_utils.py,sha256=gp7RCFWaddFnELIyTc1E19Rk8jJsrKshG2n8ZZR-kKA,5751
6
- basic_memory/utils.py,sha256=HiLorP5_YCQeNeTcDqvnkrwY7OBaFRS3i_hdV9iWKLs,2374
6
+ basic_memory/utils.py,sha256=SuAE8rVIGTcj9Yi2fVMhDbToVjfprU_iOiOr0U8aqIU,3352
7
7
  basic_memory/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
8
8
  basic_memory/alembic/env.py,sha256=XqJVQhS41ba7NCPmmaSZ09_tbSLnwsY2bcpJpqx_ZTc,2107
9
9
  basic_memory/alembic/migrations.py,sha256=CIbkMHEKZ60aDUhFGSQjv8kDNM7sazfvEYHGGcy1DBk,858
10
10
  basic_memory/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
11
11
  basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py,sha256=lTbWlAnd1es7xU99DoJgfaRe1_Kte8TL98riqeKGV80,4363
12
12
  basic_memory/api/__init__.py,sha256=wCpj-21j1D0KzKl9Ql6unLBVFY0K1uGp_FeSZRKtqpk,72
13
- basic_memory/api/app.py,sha256=3ddcWTxVjMy_5SUq89kROhMwosZqcr67Q5evOlSR9GE,1389
13
+ basic_memory/api/app.py,sha256=0vmDJDhKkRN1f7XDO3hqcUpXrLRmcHOH79O5x4hPtho,1573
14
14
  basic_memory/api/routers/__init__.py,sha256=iviQ1QVYobC8huUuyRhEjcA0BDjrOUm1lXHXhJkxP9A,239
15
- basic_memory/api/routers/knowledge_router.py,sha256=cMLhRczOfSRnsZdyR0bSS8PENPRTu70dlwaV27O34bs,5705
16
- basic_memory/api/routers/memory_router.py,sha256=pF0GzmWoxmjhtxZM8jCmfLwqjey_fmXER5vYbD8fsQw,4556
17
- basic_memory/api/routers/resource_router.py,sha256=MoW8LEjBfNbJsp6Nt2JnE4LKe3ysiVqwgY5BMCFuPCQ,4360
18
- basic_memory/api/routers/search_router.py,sha256=dCRnBbp3r966U8UYwgAaxZBbg7yX7pC8QJqagdACUi0,1086
15
+ basic_memory/api/routers/knowledge_router.py,sha256=LtZIXZ2cgqLesMMY8_xoyQm2WW4dnn3ZrASjZYeLMhA,5278
16
+ basic_memory/api/routers/memory_router.py,sha256=oBOyR-qpT8FAYvzENeBEWuWeGe6beD3GXUJ2SVhdRCw,5080
17
+ basic_memory/api/routers/resource_router.py,sha256=B3pd_5qJMgYro0CmLV3YIr0eol4eRtb78FYOPJzrPgA,4534
18
+ basic_memory/api/routers/search_router.py,sha256=WCLuAkEZ-DpwHU8RsnZg63_LDiSYbbLLlBb9Avbc2fA,1164
19
19
  basic_memory/cli/__init__.py,sha256=arcKLAWRDhPD7x5t80MlviZeYzwHZ0GZigyy3NKVoGk,33
20
- basic_memory/cli/app.py,sha256=NG6gs_UzyXBiQLHbiZRZlew3nb7G7i_8gwPh1383EnA,450
21
- basic_memory/cli/main.py,sha256=_x9Tvjv5Xl26Bhn6dO2A2-5yu5ckiLiPZr0yFeDYB2w,611
20
+ basic_memory/cli/app.py,sha256=7_HWUOW6oWncbwhuCJ5jC3z2WadSfkuTWiH_w_Rbhnk,465
21
+ basic_memory/cli/main.py,sha256=iVHi23ClA1hTvC5lq8dpGMmWObIjhA1QoWo_moX84os,434
22
22
  basic_memory/cli/commands/__init__.py,sha256=OQGLaKTsOdPsp2INM_pHzmOlbVfdL0sytBNgvqTqCDY,159
23
- basic_memory/cli/commands/db.py,sha256=XW2ujzas5j2Gf01NOPQI89L4NK-21GksO_OIekKxv6c,770
24
- basic_memory/cli/commands/import_chatgpt.py,sha256=Jnqj_kswM9S-qauPCHqLiMIQMvY4PXULHZSiqVJ_veQ,8150
25
- basic_memory/cli/commands/import_claude_conversations.py,sha256=Ba97fH5yfW642yrkxay3YkyDdgIYCeru-MUIZfEGblo,6812
26
- basic_memory/cli/commands/import_claude_projects.py,sha256=euht03ydbI6c5IO_VeArlk9YUYMXNZGXekaa7uG8i7g,6635
27
- basic_memory/cli/commands/import_memory_json.py,sha256=zqpU4eCzQXx04aRsigddJAyhvklmTgSAzeRTuEdNw0c,5194
23
+ basic_memory/cli/commands/db.py,sha256=BW3VmoTKNfzkl85m9lyrx8lkk1IRb6wuAmrPKFQxNE8,906
24
+ basic_memory/cli/commands/import_chatgpt.py,sha256=py3q9iMlB85olQBsBcEpUt0X03hHvAmRy8iQl2dbbuc,8394
25
+ basic_memory/cli/commands/import_claude_conversations.py,sha256=_7n-nn1tbsaarwR25QXjYxSC_K2M0cXBTzthnCZ_4-w,7030
26
+ basic_memory/cli/commands/import_claude_projects.py,sha256=fvXu6wwlmfA2wJCcp8IoJnz3INefkVcFhqAX8KhnCtc,6851
27
+ basic_memory/cli/commands/import_memory_json.py,sha256=cS-1rxGYUC0-QsETIbA0QqbB1Cl74YcnoJRNCkMkM-o,5395
28
28
  basic_memory/cli/commands/mcp.py,sha256=BPdThcufdriIvrDskc87a0oCC1BkZ0PZsgNao_-oNKk,611
29
- basic_memory/cli/commands/status.py,sha256=G7aAdbCuiFe38VFxMTcAfY2DKqol3WIQxYa491ui4yM,5728
30
- basic_memory/cli/commands/sync.py,sha256=LpoEPstcguhRPT2bwsbAI3ypiY0cDoNn_gxUmLpX21Q,6827
29
+ basic_memory/cli/commands/status.py,sha256=mnAnizd2l0OShTJyF9EGEdO7TeKDUgQVlHNXZwDDFuU,5845
30
+ basic_memory/cli/commands/sync.py,sha256=2Uj7Homfr403OCoPCq3rrC-OQfOhvoDqGH0F38pBQVw,7038
31
+ basic_memory/cli/commands/tools.py,sha256=NuLFckIW7tcRc7KUPm5ig6-iSGzTaCQiu8jXrBM8np8,5261
31
32
  basic_memory/markdown/__init__.py,sha256=DdzioCWtDnKaq05BHYLgL_78FawEHLpLXnp-kPSVfIc,501
32
33
  basic_memory/markdown/entity_parser.py,sha256=sJk8TRUd9cAaIjATiJn7dBQRorrYngRbd7MRVfc0Oc4,3781
33
34
  basic_memory/markdown/markdown_processor.py,sha256=mV3pYoDTaQMEl1tA5n_XztBvNlYyH2SzKs4vnKdAet4,4952
@@ -38,10 +39,10 @@ basic_memory/mcp/__init__.py,sha256=dsDOhKqjYeIbCULbHIxfcItTbqudEuEg1Np86eq0GEQ,
38
39
  basic_memory/mcp/async_client.py,sha256=vMN5nApPA428Oz4Siq2mNTiBjTcM5A5OSZTnX7_sDxE,234
39
40
  basic_memory/mcp/server.py,sha256=L92Vit7llaKT9NlPZfxdp67C33niObmRH2QFyUhmnD0,355
40
41
  basic_memory/mcp/tools/__init__.py,sha256=MHZmWw016N0qbtC3f186Jg1tPzh2g88_ZsCKJ0oyrrs,873
41
- basic_memory/mcp/tools/knowledge.py,sha256=2U8YUKCizsAETHCC1mBVKMfCEef6tlc_pa2wOmA9mD4,2016
42
- basic_memory/mcp/tools/memory.py,sha256=gl4MBm9l2lMOfu_xmUqjoZacWSIHOAYZiAm8z7oDuY8,5203
43
- basic_memory/mcp/tools/notes.py,sha256=ZJGMU-14_aIQvrDA-yaLbnDoFdjIgx7SzE7PWTf7c4o,7249
44
- basic_memory/mcp/tools/search.py,sha256=tx6aIuB2FWmmrvzu3RHSQvszlk-zHcwrWhkLLHWjuZc,1105
42
+ basic_memory/mcp/tools/knowledge.py,sha256=JotFMQpMwidx0WLvOG4yWpwWwLmyp-PoO6bVjpQseYQ,2671
43
+ basic_memory/mcp/tools/memory.py,sha256=zRtwN8va9sEv6ao0dIAU63jDb49H_8IcFmZvPzypg2w,6180
44
+ basic_memory/mcp/tools/notes.py,sha256=6m7Xl9IN_8bkXSvV-vR8GqZZufMki5JjjWc6RlntDts,7424
45
+ basic_memory/mcp/tools/search.py,sha256=UFPBDzfZ60SrvAgvISO3Jt6WdNwEQKsvibQdPxC7dOg,1511
45
46
  basic_memory/mcp/tools/utils.py,sha256=icm-Xyqw3GxooGYkXqjEjoZvIGy_Z3CPw-uUYBxR_YQ,4831
46
47
  basic_memory/models/__init__.py,sha256=Bf0xXV_ryndogvZDiVM_Wb6iV2fHUxYNGMZNWNcZi0s,307
47
48
  basic_memory/models/base.py,sha256=4hAXJ8CE1RnjKhb23lPd-QM7G_FXIdTowMJ9bRixspU,225
@@ -52,30 +53,30 @@ basic_memory/repository/entity_repository.py,sha256=VFLymzJ1W6AZru_s1S3U6nlqSprB
52
53
  basic_memory/repository/observation_repository.py,sha256=BOcy4wARqCXu-thYyt7mPxt2A2C8TW0le3s_X9wrK6I,1701
53
54
  basic_memory/repository/relation_repository.py,sha256=DwpTcn9z_1sZQcyMOUABz1k1VSwo_AU63x2zR7aerTk,2933
54
55
  basic_memory/repository/repository.py,sha256=jUScHWOfcB2FajwVZ2Sbjtg-gSI2Y2rhiIaTULjvmn8,11321
55
- basic_memory/repository/search_repository.py,sha256=NcOGMkPvwo3VlbsmeQWDsA2f1eTzX5cyl-7M2b-ADYs,10036
56
+ basic_memory/repository/search_repository.py,sha256=G5qnchUqhq8httnihOU77nA3mq3MNpTxqQAi96uBmRE,10122
56
57
  basic_memory/schemas/__init__.py,sha256=eVxrtuPT7-9JIQ7UDx2J8t8xlS3u0iUkV_VLNbzvxo4,1575
57
58
  basic_memory/schemas/base.py,sha256=epSauNNVZ2lRLATf-HIzqeberq4ZBTgxliNmjitAsWc,5538
58
59
  basic_memory/schemas/delete.py,sha256=UAR2JK99WMj3gP-yoGWlHD3eZEkvlTSRf8QoYIE-Wfw,1180
59
60
  basic_memory/schemas/discovery.py,sha256=6Y2tUiv9f06rFTsa8_wTH2haS2bhCfuQh0uW33hwdd8,876
60
- basic_memory/schemas/memory.py,sha256=mqslazV0lQswtbNgYv_y2-KxmifIvRlg5I3IuTTMnO4,2882
61
- basic_memory/schemas/request.py,sha256=rt_guNWrUMePJvDmsh1g1dc7IqEY6K6mGXMKx8tBCj8,1614
61
+ basic_memory/schemas/memory.py,sha256=BscZQOYw-dXq_qUz4qd3PrUI5618tRwNrYxKmIppU2Q,2924
62
+ basic_memory/schemas/request.py,sha256=58r9mPGc4Am9rR_zGzo-yqXcsrl5I6n3M5LjGK5gFFk,1626
62
63
  basic_memory/schemas/response.py,sha256=lVYR31DTtSeFRddGWX_wQWnQgyiwX0LEpNJ4f4lKpTM,6440
63
- basic_memory/schemas/search.py,sha256=pWBA1-xEQ3rH8vLIgrQT4oygq9MMwr0B7VCbFafVVOw,3278
64
+ basic_memory/schemas/search.py,sha256=Yl7yPUEYADl4tHllTKCjIoe8ck4kVsVglIItnKN2Ei8,3319
64
65
  basic_memory/services/__init__.py,sha256=oop6SKmzV4_NAYt9otGnupLGVCCKIVgxEcdRQWwh25I,197
65
- basic_memory/services/context_service.py,sha256=Bu1wVl9q3FDGbGChrLqgFGQW95-W1OfjNqq6SGljqWg,9388
66
+ basic_memory/services/context_service.py,sha256=y2Kd9YRPdQbJ6uWcY71z2qCZZUt8Sb2Dy52dh2OMJxo,9651
66
67
  basic_memory/services/entity_service.py,sha256=CoN1HVrjlT2JDaG3tfs6NixkZgJ4xbaEjABkv8hyGJ4,11784
67
68
  basic_memory/services/exceptions.py,sha256=VGlCLd4UD2w5NWKqC7QpG4jOM_hA7jKRRM-MqvEVMNk,288
68
69
  basic_memory/services/file_service.py,sha256=r4JfPY1wyenAH0Y-iq7vGHPwT616ayUWoLnvA1NuzpA,5695
69
70
  basic_memory/services/link_resolver.py,sha256=GmUPTViW5JplQo4yJNaX18OGInqMitrxUeR4LQqUABA,4581
70
- basic_memory/services/search_service.py,sha256=VDtRYYCHojzG2-yQbi_8ncs5YdoyHM7k_QHdA-0g6oI,7829
71
+ basic_memory/services/search_service.py,sha256=0ZeK9CND4GQylRTG3LXxgTS-PVfn8dnDmxw75KR6dsk,7901
71
72
  basic_memory/services/service.py,sha256=V-d_8gOV07zGIQDpL-Ksqs3ZN9l3qf3HZOK1f_YNTag,336
72
73
  basic_memory/sync/__init__.py,sha256=ko0xLQv1S5U7sAOmIP2XKl03akVPzoY-a9m3TFPcMh4,193
73
74
  basic_memory/sync/file_change_scanner.py,sha256=4whJej6t9sxwUp1ox93efJ0bBHSnAr6STpk_PsKU6to,5784
74
- basic_memory/sync/sync_service.py,sha256=nAOX4N90lbpRJeq5tRR_7PYptIoWwhXMUljE7yrneF4,7087
75
+ basic_memory/sync/sync_service.py,sha256=hx8aalnnFZB2T_fVRGN_6OlzTCmL0U0aUoPr8CPiB-o,7662
75
76
  basic_memory/sync/utils.py,sha256=wz1Fe7Mb_M5N9vYRQnDKGODiMGcj5MEK16KVJ3eoQ9g,1191
76
77
  basic_memory/sync/watch_service.py,sha256=CtKBrP1imI3ZSEgJl7Ffi-JZ_oDGKrhiyGgs41h5QYI,7563
77
- basic_memory-0.5.0.dist-info/METADATA,sha256=PMFAU3vLghjj7SV2yHnEXBvwatUbkbzIBtBYzUxtX-4,10791
78
- basic_memory-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
79
- basic_memory-0.5.0.dist-info/entry_points.txt,sha256=IDQa_VmVTzmvMrpnjhEfM0S3F--XsVGEj3MpdJfuo-Q,59
80
- basic_memory-0.5.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
81
- basic_memory-0.5.0.dist-info/RECORD,,
78
+ basic_memory-0.7.0.dist-info/METADATA,sha256=-G7gpth5p_edb7vSSMQdmDn5irZ7fJjOfdOV0qS-CDQ,10855
79
+ basic_memory-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
80
+ basic_memory-0.7.0.dist-info/entry_points.txt,sha256=IDQa_VmVTzmvMrpnjhEfM0S3F--XsVGEj3MpdJfuo-Q,59
81
+ basic_memory-0.7.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
82
+ basic_memory-0.7.0.dist-info/RECORD,,