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,72 @@
1
+ """Pydantic schemas for sync report responses."""
2
+
3
+ from datetime import datetime
4
+ from typing import TYPE_CHECKING, Dict, List, Set
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+ # avoid cirular imports
9
+ if TYPE_CHECKING: # pragma: no cover
10
+ from basic_memory.sync.sync_service import SyncReport
11
+
12
+
13
+ class SkippedFileResponse(BaseModel):
14
+ """Information about a file that was skipped due to repeated failures."""
15
+
16
+ path: str = Field(description="File path relative to project root")
17
+ reason: str = Field(description="Error message from last failure")
18
+ failure_count: int = Field(description="Number of consecutive failures")
19
+ first_failed: datetime = Field(description="Timestamp of first failure")
20
+
21
+ model_config = {"from_attributes": True}
22
+
23
+
24
+ class SyncReportResponse(BaseModel):
25
+ """Report of file changes found compared to database state.
26
+
27
+ Used for API responses when scanning or syncing files.
28
+ """
29
+
30
+ new: Set[str] = Field(default_factory=set, description="Files on disk but not in database")
31
+ modified: Set[str] = Field(default_factory=set, description="Files with different checksums")
32
+ deleted: Set[str] = Field(default_factory=set, description="Files in database but not on disk")
33
+ moves: Dict[str, str] = Field(
34
+ default_factory=dict, description="Files moved (old_path -> new_path)"
35
+ )
36
+ checksums: Dict[str, str] = Field(
37
+ default_factory=dict, description="Current file checksums (path -> checksum)"
38
+ )
39
+ skipped_files: List[SkippedFileResponse] = Field(
40
+ default_factory=list, description="Files skipped due to repeated failures"
41
+ )
42
+ total: int = Field(description="Total number of changes")
43
+
44
+ @classmethod
45
+ def from_sync_report(cls, report: "SyncReport") -> "SyncReportResponse":
46
+ """Convert SyncReport dataclass to Pydantic model.
47
+
48
+ Args:
49
+ report: SyncReport dataclass from sync service
50
+
51
+ Returns:
52
+ SyncReportResponse with same data
53
+ """
54
+ return cls(
55
+ new=report.new,
56
+ modified=report.modified,
57
+ deleted=report.deleted,
58
+ moves=report.moves,
59
+ checksums=report.checksums,
60
+ skipped_files=[
61
+ SkippedFileResponse(
62
+ path=skipped.path,
63
+ reason=skipped.reason,
64
+ failure_count=skipped.failure_count,
65
+ first_failed=skipped.first_failed,
66
+ )
67
+ for skipped in report.skipped_files
68
+ ],
69
+ total=report.total,
70
+ )
71
+
72
+ model_config = {"from_attributes": True}
@@ -0,0 +1,27 @@
1
+ """V2 API schemas - ID-based entity and project references."""
2
+
3
+ from basic_memory.schemas.v2.entity import (
4
+ EntityResolveRequest,
5
+ EntityResolveResponse,
6
+ EntityResponseV2,
7
+ MoveEntityRequestV2,
8
+ ProjectResolveRequest,
9
+ ProjectResolveResponse,
10
+ )
11
+ from basic_memory.schemas.v2.resource import (
12
+ CreateResourceRequest,
13
+ UpdateResourceRequest,
14
+ ResourceResponse,
15
+ )
16
+
17
+ __all__ = [
18
+ "EntityResolveRequest",
19
+ "EntityResolveResponse",
20
+ "EntityResponseV2",
21
+ "MoveEntityRequestV2",
22
+ "ProjectResolveRequest",
23
+ "ProjectResolveResponse",
24
+ "CreateResourceRequest",
25
+ "UpdateResourceRequest",
26
+ "ResourceResponse",
27
+ ]
@@ -0,0 +1,133 @@
1
+ """V2 entity and project schemas with ID-first design."""
2
+
3
+ from datetime import datetime
4
+ from typing import Dict, List, Literal, Optional
5
+
6
+ from pydantic import BaseModel, Field, ConfigDict
7
+
8
+ from basic_memory.schemas.response import ObservationResponse, RelationResponse
9
+
10
+
11
+ class EntityResolveRequest(BaseModel):
12
+ """Request to resolve a string identifier to an entity ID.
13
+
14
+ Supports resolution of:
15
+ - Permalinks (e.g., "specs/search")
16
+ - Titles (e.g., "Search Specification")
17
+ - File paths (e.g., "specs/search.md")
18
+ """
19
+
20
+ identifier: str = Field(
21
+ ...,
22
+ description="Entity identifier to resolve (permalink, title, or file path)",
23
+ min_length=1,
24
+ max_length=500,
25
+ )
26
+
27
+
28
+ class EntityResolveResponse(BaseModel):
29
+ """Response from identifier resolution.
30
+
31
+ Returns the entity ID and associated metadata for the resolved entity.
32
+ """
33
+
34
+ external_id: str = Field(..., description="External UUID (primary API identifier)")
35
+ entity_id: int = Field(..., description="Numeric entity ID (internal identifier)")
36
+ permalink: Optional[str] = Field(None, description="Entity permalink")
37
+ file_path: str = Field(..., description="Relative file path")
38
+ title: str = Field(..., description="Entity title")
39
+ resolution_method: Literal["external_id", "permalink", "title", "path", "search"] = Field(
40
+ ..., description="How the identifier was resolved"
41
+ )
42
+
43
+
44
+ class MoveEntityRequestV2(BaseModel):
45
+ """V2 request schema for moving an entity to a new file location.
46
+
47
+ In V2 API, the entity ID is provided in the URL path, so this request
48
+ only needs the destination path.
49
+ """
50
+
51
+ destination_path: str = Field(
52
+ ...,
53
+ description="New file path for the entity (relative to project root)",
54
+ min_length=1,
55
+ max_length=500,
56
+ )
57
+
58
+
59
+ class EntityResponseV2(BaseModel):
60
+ """V2 entity response with external_id as the primary API identifier.
61
+
62
+ This response format emphasizes the external_id (UUID) as the primary API identifier,
63
+ with the numeric id maintained for internal reference.
64
+ """
65
+
66
+ # External UUID first - this is the primary API identifier in v2
67
+ external_id: str = Field(..., description="External UUID (primary API identifier)")
68
+ # Internal numeric ID
69
+ id: int = Field(..., description="Numeric entity ID (internal identifier)")
70
+
71
+ # Core entity fields
72
+ title: str = Field(..., description="Entity title")
73
+ entity_type: str = Field(..., description="Entity type")
74
+ content_type: str = Field(default="text/markdown", description="Content MIME type")
75
+
76
+ # Secondary identifiers (for compatibility and convenience)
77
+ permalink: Optional[str] = Field(None, description="Entity permalink (may change)")
78
+ file_path: str = Field(..., description="Relative file path (may change)")
79
+
80
+ # Content and metadata
81
+ content: Optional[str] = Field(None, description="Entity content")
82
+ entity_metadata: Optional[Dict] = Field(None, description="Entity metadata")
83
+
84
+ # Relationships
85
+ observations: List[ObservationResponse] = Field(
86
+ default_factory=list, description="Entity observations"
87
+ )
88
+ relations: List[RelationResponse] = Field(default_factory=list, description="Entity relations")
89
+
90
+ # Timestamps
91
+ created_at: datetime = Field(..., description="Creation timestamp")
92
+ updated_at: datetime = Field(..., description="Last update timestamp")
93
+
94
+ # V2-specific metadata
95
+ api_version: Literal["v2"] = Field(
96
+ default="v2", description="API version (always 'v2' for this response)"
97
+ )
98
+
99
+ model_config = ConfigDict(from_attributes=True)
100
+
101
+
102
+ class ProjectResolveRequest(BaseModel):
103
+ """Request to resolve a project identifier to a project ID.
104
+
105
+ Supports resolution of:
106
+ - Project names (e.g., "my-project")
107
+ - Permalinks (e.g., "my-project")
108
+ """
109
+
110
+ identifier: str = Field(
111
+ ...,
112
+ description="Project identifier to resolve (name or permalink)",
113
+ min_length=1,
114
+ max_length=255,
115
+ )
116
+
117
+
118
+ class ProjectResolveResponse(BaseModel):
119
+ """Response from project identifier resolution.
120
+
121
+ Returns the project ID and associated metadata for the resolved project.
122
+ """
123
+
124
+ external_id: str = Field(..., description="External UUID (primary API identifier)")
125
+ project_id: int = Field(..., description="Numeric project ID (internal identifier)")
126
+ name: str = Field(..., description="Project name")
127
+ permalink: str = Field(..., description="Project permalink")
128
+ path: str = Field(..., description="Project file path")
129
+ is_active: bool = Field(..., description="Whether the project is active")
130
+ is_default: bool = Field(..., description="Whether the project is the default")
131
+ resolution_method: Literal["external_id", "name", "permalink"] = Field(
132
+ ..., description="How the identifier was resolved"
133
+ )
@@ -0,0 +1,47 @@
1
+ """V2 resource schemas for file content operations."""
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class CreateResourceRequest(BaseModel):
7
+ """Request to create a new resource file.
8
+
9
+ File path is required for new resources since we need to know where
10
+ to create the file.
11
+ """
12
+
13
+ file_path: str = Field(
14
+ ...,
15
+ description="Path to create the file, relative to project root",
16
+ min_length=1,
17
+ max_length=500,
18
+ )
19
+ content: str = Field(..., description="File content to write")
20
+
21
+
22
+ class UpdateResourceRequest(BaseModel):
23
+ """Request to update an existing resource by entity ID.
24
+
25
+ Only content is required - the file path is already known from the entity.
26
+ Optionally can update the file_path to move the file.
27
+ """
28
+
29
+ content: str = Field(..., description="File content to write")
30
+ file_path: str | None = Field(
31
+ None,
32
+ description="Optional new file path to move the resource",
33
+ min_length=1,
34
+ max_length=500,
35
+ )
36
+
37
+
38
+ class ResourceResponse(BaseModel):
39
+ """Response from resource operations."""
40
+
41
+ entity_id: int = Field(..., description="Internal entity ID of the resource")
42
+ external_id: str = Field(..., description="External UUID of the resource for API references")
43
+ file_path: str = Field(..., description="File path of the resource")
44
+ checksum: str = Field(..., description="File content checksum")
45
+ size: int = Field(..., description="File size in bytes")
46
+ created_at: float = Field(..., description="Creation timestamp")
47
+ modified_at: float = Field(..., description="Modification timestamp")
@@ -3,5 +3,6 @@
3
3
  from .service import BaseService
4
4
  from .file_service import FileService
5
5
  from .entity_service import EntityService
6
+ from .project_service import ProjectService
6
7
 
7
- __all__ = ["BaseService", "FileService", "EntityService"]
8
+ __all__ = ["BaseService", "FileService", "EntityService", "ProjectService"]