basic-memory 0.7.0__py3-none-any.whl → 0.17.4__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 (195) hide show
  1. basic_memory/__init__.py +5 -1
  2. basic_memory/alembic/alembic.ini +119 -0
  3. basic_memory/alembic/env.py +130 -20
  4. basic_memory/alembic/migrations.py +4 -9
  5. basic_memory/alembic/versions/314f1ea54dc4_add_postgres_full_text_search_support_.py +131 -0
  6. basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
  7. basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +120 -0
  8. basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +112 -0
  9. basic_memory/alembic/versions/6830751f5fb6_merge_multiple_heads.py +24 -0
  10. basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +49 -0
  11. basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +49 -0
  12. basic_memory/alembic/versions/a2b3c4d5e6f7_add_search_index_entity_cascade.py +56 -0
  13. basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
  14. basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +113 -0
  15. basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
  16. basic_memory/alembic/versions/f8a9b2c3d4e5_add_pg_trgm_for_fuzzy_link_resolution.py +239 -0
  17. basic_memory/alembic/versions/g9a0b3c4d5e6_add_external_id_to_project_and_entity.py +173 -0
  18. basic_memory/api/app.py +87 -20
  19. basic_memory/api/container.py +133 -0
  20. basic_memory/api/routers/__init__.py +4 -1
  21. basic_memory/api/routers/directory_router.py +84 -0
  22. basic_memory/api/routers/importer_router.py +152 -0
  23. basic_memory/api/routers/knowledge_router.py +180 -23
  24. basic_memory/api/routers/management_router.py +80 -0
  25. basic_memory/api/routers/memory_router.py +9 -64
  26. basic_memory/api/routers/project_router.py +460 -0
  27. basic_memory/api/routers/prompt_router.py +260 -0
  28. basic_memory/api/routers/resource_router.py +136 -11
  29. basic_memory/api/routers/search_router.py +5 -5
  30. basic_memory/api/routers/utils.py +169 -0
  31. basic_memory/api/template_loader.py +292 -0
  32. basic_memory/api/v2/__init__.py +35 -0
  33. basic_memory/api/v2/routers/__init__.py +21 -0
  34. basic_memory/api/v2/routers/directory_router.py +93 -0
  35. basic_memory/api/v2/routers/importer_router.py +181 -0
  36. basic_memory/api/v2/routers/knowledge_router.py +427 -0
  37. basic_memory/api/v2/routers/memory_router.py +130 -0
  38. basic_memory/api/v2/routers/project_router.py +359 -0
  39. basic_memory/api/v2/routers/prompt_router.py +269 -0
  40. basic_memory/api/v2/routers/resource_router.py +286 -0
  41. basic_memory/api/v2/routers/search_router.py +73 -0
  42. basic_memory/cli/app.py +80 -10
  43. basic_memory/cli/auth.py +300 -0
  44. basic_memory/cli/commands/__init__.py +15 -2
  45. basic_memory/cli/commands/cloud/__init__.py +6 -0
  46. basic_memory/cli/commands/cloud/api_client.py +127 -0
  47. basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
  48. basic_memory/cli/commands/cloud/cloud_utils.py +108 -0
  49. basic_memory/cli/commands/cloud/core_commands.py +195 -0
  50. basic_memory/cli/commands/cloud/rclone_commands.py +397 -0
  51. basic_memory/cli/commands/cloud/rclone_config.py +110 -0
  52. basic_memory/cli/commands/cloud/rclone_installer.py +263 -0
  53. basic_memory/cli/commands/cloud/upload.py +240 -0
  54. basic_memory/cli/commands/cloud/upload_command.py +124 -0
  55. basic_memory/cli/commands/command_utils.py +99 -0
  56. basic_memory/cli/commands/db.py +87 -12
  57. basic_memory/cli/commands/format.py +198 -0
  58. basic_memory/cli/commands/import_chatgpt.py +47 -223
  59. basic_memory/cli/commands/import_claude_conversations.py +48 -171
  60. basic_memory/cli/commands/import_claude_projects.py +53 -160
  61. basic_memory/cli/commands/import_memory_json.py +55 -111
  62. basic_memory/cli/commands/mcp.py +67 -11
  63. basic_memory/cli/commands/project.py +889 -0
  64. basic_memory/cli/commands/status.py +52 -34
  65. basic_memory/cli/commands/telemetry.py +81 -0
  66. basic_memory/cli/commands/tool.py +341 -0
  67. basic_memory/cli/container.py +84 -0
  68. basic_memory/cli/main.py +14 -6
  69. basic_memory/config.py +580 -26
  70. basic_memory/db.py +285 -28
  71. basic_memory/deps/__init__.py +293 -0
  72. basic_memory/deps/config.py +26 -0
  73. basic_memory/deps/db.py +56 -0
  74. basic_memory/deps/importers.py +200 -0
  75. basic_memory/deps/projects.py +238 -0
  76. basic_memory/deps/repositories.py +179 -0
  77. basic_memory/deps/services.py +480 -0
  78. basic_memory/deps.py +16 -185
  79. basic_memory/file_utils.py +318 -54
  80. basic_memory/ignore_utils.py +297 -0
  81. basic_memory/importers/__init__.py +27 -0
  82. basic_memory/importers/base.py +100 -0
  83. basic_memory/importers/chatgpt_importer.py +245 -0
  84. basic_memory/importers/claude_conversations_importer.py +192 -0
  85. basic_memory/importers/claude_projects_importer.py +184 -0
  86. basic_memory/importers/memory_json_importer.py +128 -0
  87. basic_memory/importers/utils.py +61 -0
  88. basic_memory/markdown/entity_parser.py +182 -23
  89. basic_memory/markdown/markdown_processor.py +70 -7
  90. basic_memory/markdown/plugins.py +43 -23
  91. basic_memory/markdown/schemas.py +1 -1
  92. basic_memory/markdown/utils.py +38 -14
  93. basic_memory/mcp/async_client.py +135 -4
  94. basic_memory/mcp/clients/__init__.py +28 -0
  95. basic_memory/mcp/clients/directory.py +70 -0
  96. basic_memory/mcp/clients/knowledge.py +176 -0
  97. basic_memory/mcp/clients/memory.py +120 -0
  98. basic_memory/mcp/clients/project.py +89 -0
  99. basic_memory/mcp/clients/resource.py +71 -0
  100. basic_memory/mcp/clients/search.py +65 -0
  101. basic_memory/mcp/container.py +110 -0
  102. basic_memory/mcp/project_context.py +155 -0
  103. basic_memory/mcp/prompts/__init__.py +19 -0
  104. basic_memory/mcp/prompts/ai_assistant_guide.py +70 -0
  105. basic_memory/mcp/prompts/continue_conversation.py +62 -0
  106. basic_memory/mcp/prompts/recent_activity.py +188 -0
  107. basic_memory/mcp/prompts/search.py +57 -0
  108. basic_memory/mcp/prompts/utils.py +162 -0
  109. basic_memory/mcp/resources/ai_assistant_guide.md +283 -0
  110. basic_memory/mcp/resources/project_info.py +71 -0
  111. basic_memory/mcp/server.py +61 -9
  112. basic_memory/mcp/tools/__init__.py +33 -21
  113. basic_memory/mcp/tools/build_context.py +120 -0
  114. basic_memory/mcp/tools/canvas.py +152 -0
  115. basic_memory/mcp/tools/chatgpt_tools.py +190 -0
  116. basic_memory/mcp/tools/delete_note.py +249 -0
  117. basic_memory/mcp/tools/edit_note.py +325 -0
  118. basic_memory/mcp/tools/list_directory.py +157 -0
  119. basic_memory/mcp/tools/move_note.py +549 -0
  120. basic_memory/mcp/tools/project_management.py +204 -0
  121. basic_memory/mcp/tools/read_content.py +281 -0
  122. basic_memory/mcp/tools/read_note.py +265 -0
  123. basic_memory/mcp/tools/recent_activity.py +528 -0
  124. basic_memory/mcp/tools/search.py +377 -24
  125. basic_memory/mcp/tools/utils.py +402 -16
  126. basic_memory/mcp/tools/view_note.py +78 -0
  127. basic_memory/mcp/tools/write_note.py +230 -0
  128. basic_memory/models/__init__.py +3 -2
  129. basic_memory/models/knowledge.py +82 -17
  130. basic_memory/models/project.py +93 -0
  131. basic_memory/models/search.py +68 -8
  132. basic_memory/project_resolver.py +222 -0
  133. basic_memory/repository/__init__.py +2 -0
  134. basic_memory/repository/entity_repository.py +437 -8
  135. basic_memory/repository/observation_repository.py +36 -3
  136. basic_memory/repository/postgres_search_repository.py +451 -0
  137. basic_memory/repository/project_info_repository.py +10 -0
  138. basic_memory/repository/project_repository.py +140 -0
  139. basic_memory/repository/relation_repository.py +79 -4
  140. basic_memory/repository/repository.py +148 -29
  141. basic_memory/repository/search_index_row.py +95 -0
  142. basic_memory/repository/search_repository.py +79 -268
  143. basic_memory/repository/search_repository_base.py +241 -0
  144. basic_memory/repository/sqlite_search_repository.py +437 -0
  145. basic_memory/runtime.py +61 -0
  146. basic_memory/schemas/__init__.py +22 -9
  147. basic_memory/schemas/base.py +131 -12
  148. basic_memory/schemas/cloud.py +50 -0
  149. basic_memory/schemas/directory.py +31 -0
  150. basic_memory/schemas/importer.py +35 -0
  151. basic_memory/schemas/memory.py +194 -25
  152. basic_memory/schemas/project_info.py +213 -0
  153. basic_memory/schemas/prompt.py +90 -0
  154. basic_memory/schemas/request.py +56 -2
  155. basic_memory/schemas/response.py +85 -28
  156. basic_memory/schemas/search.py +36 -35
  157. basic_memory/schemas/sync_report.py +72 -0
  158. basic_memory/schemas/v2/__init__.py +27 -0
  159. basic_memory/schemas/v2/entity.py +133 -0
  160. basic_memory/schemas/v2/resource.py +47 -0
  161. basic_memory/services/__init__.py +2 -1
  162. basic_memory/services/context_service.py +451 -138
  163. basic_memory/services/directory_service.py +310 -0
  164. basic_memory/services/entity_service.py +636 -71
  165. basic_memory/services/exceptions.py +21 -0
  166. basic_memory/services/file_service.py +402 -33
  167. basic_memory/services/initialization.py +216 -0
  168. basic_memory/services/link_resolver.py +50 -56
  169. basic_memory/services/project_service.py +888 -0
  170. basic_memory/services/search_service.py +232 -37
  171. basic_memory/sync/__init__.py +4 -2
  172. basic_memory/sync/background_sync.py +26 -0
  173. basic_memory/sync/coordinator.py +160 -0
  174. basic_memory/sync/sync_service.py +1200 -109
  175. basic_memory/sync/watch_service.py +432 -135
  176. basic_memory/telemetry.py +249 -0
  177. basic_memory/templates/prompts/continue_conversation.hbs +110 -0
  178. basic_memory/templates/prompts/search.hbs +101 -0
  179. basic_memory/utils.py +407 -54
  180. basic_memory-0.17.4.dist-info/METADATA +617 -0
  181. basic_memory-0.17.4.dist-info/RECORD +193 -0
  182. {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/WHEEL +1 -1
  183. {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/entry_points.txt +1 -0
  184. basic_memory/alembic/README +0 -1
  185. basic_memory/cli/commands/sync.py +0 -206
  186. basic_memory/cli/commands/tools.py +0 -157
  187. basic_memory/mcp/tools/knowledge.py +0 -68
  188. basic_memory/mcp/tools/memory.py +0 -170
  189. basic_memory/mcp/tools/notes.py +0 -202
  190. basic_memory/schemas/discovery.py +0 -28
  191. basic_memory/sync/file_change_scanner.py +0 -158
  192. basic_memory/sync/utils.py +0 -31
  193. basic_memory-0.7.0.dist-info/METADATA +0 -378
  194. basic_memory-0.7.0.dist-info/RECORD +0 -82
  195. {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,222 @@
1
+ """Unified project resolution across MCP, API, and CLI.
2
+
3
+ This module provides a single canonical implementation of project resolution
4
+ logic, eliminating duplicated decision trees across the codebase.
5
+
6
+ The resolution follows a three-tier hierarchy:
7
+ 1. Constrained mode: BASIC_MEMORY_MCP_PROJECT env var (highest priority)
8
+ 2. Explicit parameter: Project passed directly to operation
9
+ 3. Default project: Used when default_project_mode=true (lowest priority)
10
+
11
+ In cloud mode, project is required unless discovery mode is explicitly allowed.
12
+ """
13
+
14
+ import os
15
+ from dataclasses import dataclass
16
+ from enum import Enum, auto
17
+ from typing import Optional
18
+
19
+ from loguru import logger
20
+
21
+
22
+ class ResolutionMode(Enum):
23
+ """How the project was resolved."""
24
+
25
+ CLOUD_EXPLICIT = auto() # Explicit project in cloud mode
26
+ CLOUD_DISCOVERY = auto() # Discovery mode allowed in cloud (no project)
27
+ ENV_CONSTRAINT = auto() # BASIC_MEMORY_MCP_PROJECT env var
28
+ EXPLICIT = auto() # Explicit project parameter
29
+ DEFAULT = auto() # default_project with default_project_mode=true
30
+ NONE = auto() # No resolution possible
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ResolvedProject:
35
+ """Result of project resolution.
36
+
37
+ Attributes:
38
+ project: The resolved project name, or None if not resolved
39
+ mode: How the project was resolved
40
+ reason: Human-readable explanation of resolution
41
+ """
42
+
43
+ project: Optional[str]
44
+ mode: ResolutionMode
45
+ reason: str
46
+
47
+ @property
48
+ def is_resolved(self) -> bool:
49
+ """Whether a project was successfully resolved."""
50
+ return self.project is not None
51
+
52
+ @property
53
+ def is_discovery_mode(self) -> bool:
54
+ """Whether we're in discovery mode (no specific project)."""
55
+ return self.mode == ResolutionMode.CLOUD_DISCOVERY or (
56
+ self.mode == ResolutionMode.NONE and self.project is None
57
+ )
58
+
59
+
60
+ @dataclass
61
+ class ProjectResolver:
62
+ """Unified project resolution logic.
63
+
64
+ Resolves the effective project given requested project, environment
65
+ constraints, and configuration settings.
66
+
67
+ This is the single canonical implementation of project resolution,
68
+ used by MCP tools, API routes, and CLI commands.
69
+
70
+ Args:
71
+ cloud_mode: Whether running in cloud mode (project required)
72
+ default_project_mode: Whether to use default project when not specified
73
+ default_project: The default project name
74
+ constrained_project: Optional env-constrained project override
75
+ (typically from BASIC_MEMORY_MCP_PROJECT)
76
+ """
77
+
78
+ cloud_mode: bool = False
79
+ default_project_mode: bool = False
80
+ default_project: Optional[str] = None
81
+ constrained_project: Optional[str] = None
82
+
83
+ @classmethod
84
+ def from_env(
85
+ cls,
86
+ cloud_mode: bool = False,
87
+ default_project_mode: bool = False,
88
+ default_project: Optional[str] = None,
89
+ ) -> "ProjectResolver":
90
+ """Create resolver with constrained_project from environment.
91
+
92
+ Args:
93
+ cloud_mode: Whether running in cloud mode
94
+ default_project_mode: Whether to use default project when not specified
95
+ default_project: The default project name
96
+
97
+ Returns:
98
+ ProjectResolver configured with current environment
99
+ """
100
+ constrained = os.environ.get("BASIC_MEMORY_MCP_PROJECT")
101
+ return cls(
102
+ cloud_mode=cloud_mode,
103
+ default_project_mode=default_project_mode,
104
+ default_project=default_project,
105
+ constrained_project=constrained,
106
+ )
107
+
108
+ def resolve(
109
+ self,
110
+ project: Optional[str] = None,
111
+ allow_discovery: bool = False,
112
+ ) -> ResolvedProject:
113
+ """Resolve project using the three-tier hierarchy.
114
+
115
+ Resolution order:
116
+ 1. Cloud mode check (project required unless discovery allowed)
117
+ 2. Constrained project from env var (highest priority in local mode)
118
+ 3. Explicit project parameter
119
+ 4. Default project if default_project_mode=true
120
+
121
+ Args:
122
+ project: Optional explicit project parameter
123
+ allow_discovery: If True, allows returning None in cloud mode
124
+ for discovery operations (e.g., recent_activity across projects)
125
+
126
+ Returns:
127
+ ResolvedProject with project name, resolution mode, and reason
128
+
129
+ Raises:
130
+ ValueError: If in cloud mode and no project specified (unless discovery allowed)
131
+ """
132
+ # --- Cloud Mode Handling ---
133
+ # In cloud mode, project is required unless discovery is explicitly allowed
134
+ if self.cloud_mode:
135
+ if project:
136
+ logger.debug(f"Cloud mode: using explicit project '{project}'")
137
+ return ResolvedProject(
138
+ project=project,
139
+ mode=ResolutionMode.CLOUD_EXPLICIT,
140
+ reason=f"Explicit project in cloud mode: {project}",
141
+ )
142
+ elif allow_discovery:
143
+ logger.debug("Cloud mode: discovery mode allowed, no project required")
144
+ return ResolvedProject(
145
+ project=None,
146
+ mode=ResolutionMode.CLOUD_DISCOVERY,
147
+ reason="Discovery mode enabled in cloud",
148
+ )
149
+ else:
150
+ raise ValueError("No project specified. Project is required for cloud mode.")
151
+
152
+ # --- Local Mode: Three-Tier Hierarchy ---
153
+
154
+ # Priority 1: CLI constraint overrides everything
155
+ if self.constrained_project:
156
+ logger.debug(f"Using CLI constrained project: {self.constrained_project}")
157
+ return ResolvedProject(
158
+ project=self.constrained_project,
159
+ mode=ResolutionMode.ENV_CONSTRAINT,
160
+ reason=f"Environment constraint: BASIC_MEMORY_MCP_PROJECT={self.constrained_project}",
161
+ )
162
+
163
+ # Priority 2: Explicit project parameter
164
+ if project:
165
+ logger.debug(f"Using explicit project parameter: {project}")
166
+ return ResolvedProject(
167
+ project=project,
168
+ mode=ResolutionMode.EXPLICIT,
169
+ reason=f"Explicit parameter: {project}",
170
+ )
171
+
172
+ # Priority 3: Default project mode
173
+ if self.default_project_mode and self.default_project:
174
+ logger.debug(f"Using default project from config: {self.default_project}")
175
+ return ResolvedProject(
176
+ project=self.default_project,
177
+ mode=ResolutionMode.DEFAULT,
178
+ reason=f"Default project mode: {self.default_project}",
179
+ )
180
+
181
+ # No resolution possible
182
+ logger.debug("No project resolution possible")
183
+ return ResolvedProject(
184
+ project=None,
185
+ mode=ResolutionMode.NONE,
186
+ reason="No project specified and default_project_mode is disabled",
187
+ )
188
+
189
+ def require_project(
190
+ self,
191
+ project: Optional[str] = None,
192
+ error_message: Optional[str] = None,
193
+ ) -> ResolvedProject:
194
+ """Resolve project, raising an error if not resolved.
195
+
196
+ Convenience method for operations that require a project.
197
+
198
+ Args:
199
+ project: Optional explicit project parameter
200
+ error_message: Custom error message if project not resolved
201
+
202
+ Returns:
203
+ ResolvedProject (always with a non-None project)
204
+
205
+ Raises:
206
+ ValueError: If project could not be resolved
207
+ """
208
+ result = self.resolve(project, allow_discovery=False)
209
+ if not result.is_resolved:
210
+ msg = error_message or (
211
+ "No project specified. Either set 'default_project_mode=true' in config, "
212
+ "or provide a 'project' argument."
213
+ )
214
+ raise ValueError(msg)
215
+ return result
216
+
217
+
218
+ __all__ = [
219
+ "ProjectResolver",
220
+ "ResolvedProject",
221
+ "ResolutionMode",
222
+ ]
@@ -1,9 +1,11 @@
1
1
  from .entity_repository import EntityRepository
2
2
  from .observation_repository import ObservationRepository
3
+ from .project_repository import ProjectRepository
3
4
  from .relation_repository import RelationRepository
4
5
 
5
6
  __all__ = [
6
7
  "EntityRepository",
7
8
  "ObservationRepository",
9
+ "ProjectRepository",
8
10
  "RelationRepository",
9
11
  ]