basic-memory 0.2.12__py3-none-any.whl → 0.16.1__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.
- basic_memory/__init__.py +5 -1
- basic_memory/alembic/alembic.ini +119 -0
- basic_memory/alembic/env.py +27 -3
- basic_memory/alembic/migrations.py +4 -9
- basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
- basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +108 -0
- basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +104 -0
- basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +49 -0
- basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +49 -0
- basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
- basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +100 -0
- basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
- basic_memory/api/app.py +63 -31
- basic_memory/api/routers/__init__.py +4 -1
- basic_memory/api/routers/directory_router.py +84 -0
- basic_memory/api/routers/importer_router.py +152 -0
- basic_memory/api/routers/knowledge_router.py +165 -28
- basic_memory/api/routers/management_router.py +80 -0
- basic_memory/api/routers/memory_router.py +28 -67
- basic_memory/api/routers/project_router.py +406 -0
- basic_memory/api/routers/prompt_router.py +260 -0
- basic_memory/api/routers/resource_router.py +219 -14
- basic_memory/api/routers/search_router.py +21 -13
- basic_memory/api/routers/utils.py +130 -0
- basic_memory/api/template_loader.py +292 -0
- basic_memory/cli/app.py +52 -1
- basic_memory/cli/auth.py +277 -0
- basic_memory/cli/commands/__init__.py +13 -2
- basic_memory/cli/commands/cloud/__init__.py +6 -0
- basic_memory/cli/commands/cloud/api_client.py +112 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
- basic_memory/cli/commands/cloud/cloud_utils.py +101 -0
- basic_memory/cli/commands/cloud/core_commands.py +195 -0
- basic_memory/cli/commands/cloud/rclone_commands.py +301 -0
- basic_memory/cli/commands/cloud/rclone_config.py +110 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +249 -0
- basic_memory/cli/commands/cloud/upload.py +233 -0
- basic_memory/cli/commands/cloud/upload_command.py +124 -0
- basic_memory/cli/commands/command_utils.py +51 -0
- basic_memory/cli/commands/db.py +26 -7
- basic_memory/cli/commands/import_chatgpt.py +83 -0
- basic_memory/cli/commands/import_claude_conversations.py +86 -0
- basic_memory/cli/commands/import_claude_projects.py +85 -0
- basic_memory/cli/commands/import_memory_json.py +35 -92
- basic_memory/cli/commands/mcp.py +84 -10
- basic_memory/cli/commands/project.py +876 -0
- basic_memory/cli/commands/status.py +47 -30
- basic_memory/cli/commands/tool.py +341 -0
- basic_memory/cli/main.py +13 -6
- basic_memory/config.py +481 -22
- basic_memory/db.py +192 -32
- basic_memory/deps.py +252 -22
- basic_memory/file_utils.py +113 -58
- basic_memory/ignore_utils.py +297 -0
- basic_memory/importers/__init__.py +27 -0
- basic_memory/importers/base.py +79 -0
- basic_memory/importers/chatgpt_importer.py +232 -0
- basic_memory/importers/claude_conversations_importer.py +177 -0
- basic_memory/importers/claude_projects_importer.py +148 -0
- basic_memory/importers/memory_json_importer.py +108 -0
- basic_memory/importers/utils.py +58 -0
- basic_memory/markdown/entity_parser.py +143 -23
- basic_memory/markdown/markdown_processor.py +3 -3
- basic_memory/markdown/plugins.py +39 -21
- basic_memory/markdown/schemas.py +1 -1
- basic_memory/markdown/utils.py +28 -13
- basic_memory/mcp/async_client.py +134 -4
- basic_memory/mcp/project_context.py +141 -0
- basic_memory/mcp/prompts/__init__.py +19 -0
- basic_memory/mcp/prompts/ai_assistant_guide.py +70 -0
- basic_memory/mcp/prompts/continue_conversation.py +62 -0
- basic_memory/mcp/prompts/recent_activity.py +188 -0
- basic_memory/mcp/prompts/search.py +57 -0
- basic_memory/mcp/prompts/utils.py +162 -0
- basic_memory/mcp/resources/ai_assistant_guide.md +283 -0
- basic_memory/mcp/resources/project_info.py +71 -0
- basic_memory/mcp/server.py +7 -13
- basic_memory/mcp/tools/__init__.py +33 -21
- basic_memory/mcp/tools/build_context.py +120 -0
- basic_memory/mcp/tools/canvas.py +130 -0
- basic_memory/mcp/tools/chatgpt_tools.py +187 -0
- basic_memory/mcp/tools/delete_note.py +225 -0
- basic_memory/mcp/tools/edit_note.py +320 -0
- basic_memory/mcp/tools/list_directory.py +167 -0
- basic_memory/mcp/tools/move_note.py +545 -0
- basic_memory/mcp/tools/project_management.py +200 -0
- basic_memory/mcp/tools/read_content.py +271 -0
- basic_memory/mcp/tools/read_note.py +255 -0
- basic_memory/mcp/tools/recent_activity.py +534 -0
- basic_memory/mcp/tools/search.py +369 -14
- basic_memory/mcp/tools/utils.py +374 -16
- basic_memory/mcp/tools/view_note.py +77 -0
- basic_memory/mcp/tools/write_note.py +207 -0
- basic_memory/models/__init__.py +3 -2
- basic_memory/models/knowledge.py +67 -15
- basic_memory/models/project.py +87 -0
- basic_memory/models/search.py +10 -6
- basic_memory/repository/__init__.py +2 -0
- basic_memory/repository/entity_repository.py +229 -7
- basic_memory/repository/observation_repository.py +35 -3
- basic_memory/repository/project_info_repository.py +10 -0
- basic_memory/repository/project_repository.py +103 -0
- basic_memory/repository/relation_repository.py +21 -2
- basic_memory/repository/repository.py +147 -29
- basic_memory/repository/search_repository.py +437 -59
- basic_memory/schemas/__init__.py +22 -9
- basic_memory/schemas/base.py +97 -8
- basic_memory/schemas/cloud.py +50 -0
- basic_memory/schemas/directory.py +30 -0
- basic_memory/schemas/importer.py +35 -0
- basic_memory/schemas/memory.py +188 -23
- basic_memory/schemas/project_info.py +211 -0
- basic_memory/schemas/prompt.py +90 -0
- basic_memory/schemas/request.py +57 -3
- basic_memory/schemas/response.py +9 -1
- basic_memory/schemas/search.py +33 -35
- basic_memory/schemas/sync_report.py +72 -0
- basic_memory/services/__init__.py +2 -1
- basic_memory/services/context_service.py +251 -106
- basic_memory/services/directory_service.py +295 -0
- basic_memory/services/entity_service.py +595 -60
- basic_memory/services/exceptions.py +21 -0
- basic_memory/services/file_service.py +284 -30
- basic_memory/services/initialization.py +191 -0
- basic_memory/services/link_resolver.py +50 -56
- basic_memory/services/project_service.py +863 -0
- basic_memory/services/search_service.py +172 -34
- basic_memory/sync/__init__.py +3 -2
- basic_memory/sync/background_sync.py +26 -0
- basic_memory/sync/sync_service.py +1176 -96
- basic_memory/sync/watch_service.py +412 -135
- basic_memory/templates/prompts/continue_conversation.hbs +110 -0
- basic_memory/templates/prompts/search.hbs +101 -0
- basic_memory/utils.py +388 -28
- basic_memory-0.16.1.dist-info/METADATA +493 -0
- basic_memory-0.16.1.dist-info/RECORD +148 -0
- {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/entry_points.txt +1 -0
- basic_memory/alembic/README +0 -1
- basic_memory/cli/commands/sync.py +0 -203
- basic_memory/mcp/tools/knowledge.py +0 -56
- basic_memory/mcp/tools/memory.py +0 -151
- basic_memory/mcp/tools/notes.py +0 -122
- basic_memory/schemas/discovery.py +0 -28
- basic_memory/sync/file_change_scanner.py +0 -158
- basic_memory/sync/utils.py +0 -34
- basic_memory-0.2.12.dist-info/METADATA +0 -291
- basic_memory-0.2.12.dist-info/RECORD +0 -78
- {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/WHEEL +0 -0
- {basic_memory-0.2.12.dist-info → basic_memory-0.16.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"""Service for detecting changes between filesystem and database."""
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Dict, Sequence
|
|
6
|
-
|
|
7
|
-
from loguru import logger
|
|
8
|
-
|
|
9
|
-
from basic_memory.file_utils import compute_checksum
|
|
10
|
-
from basic_memory.models import Entity
|
|
11
|
-
from basic_memory.repository.entity_repository import EntityRepository
|
|
12
|
-
from basic_memory.sync.utils import SyncReport
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass
|
|
16
|
-
class FileState:
|
|
17
|
-
"""State of a file including file path, permalink and checksum info."""
|
|
18
|
-
|
|
19
|
-
file_path: str
|
|
20
|
-
permalink: str
|
|
21
|
-
checksum: str
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@dataclass
|
|
25
|
-
class ScanResult:
|
|
26
|
-
"""Result of scanning a directory."""
|
|
27
|
-
|
|
28
|
-
# file_path -> checksum
|
|
29
|
-
files: Dict[str, str] = field(default_factory=dict)
|
|
30
|
-
# file_path -> error message
|
|
31
|
-
errors: Dict[str, str] = field(default_factory=dict)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class FileChangeScanner:
|
|
35
|
-
"""
|
|
36
|
-
Service for detecting changes between filesystem and database.
|
|
37
|
-
The filesystem is treated as the source of truth.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
def __init__(self, entity_repository: EntityRepository):
|
|
41
|
-
self.entity_repository = entity_repository
|
|
42
|
-
|
|
43
|
-
async def scan_directory(self, directory: Path) -> ScanResult:
|
|
44
|
-
"""
|
|
45
|
-
Scan directory for markdown files and their checksums.
|
|
46
|
-
Only processes .md files, logs and skips others.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
directory: Directory to scan
|
|
50
|
-
|
|
51
|
-
Returns:
|
|
52
|
-
ScanResult containing found files and any errors
|
|
53
|
-
"""
|
|
54
|
-
logger.debug(f"Scanning directory: {directory}")
|
|
55
|
-
result = ScanResult()
|
|
56
|
-
|
|
57
|
-
if not directory.exists():
|
|
58
|
-
logger.debug(f"Directory does not exist: {directory}")
|
|
59
|
-
return result
|
|
60
|
-
|
|
61
|
-
for path in directory.rglob("*"):
|
|
62
|
-
if not path.is_file() or not path.name.endswith(".md"):
|
|
63
|
-
if path.is_file():
|
|
64
|
-
logger.debug(f"Skipping non-markdown file: {path}")
|
|
65
|
-
continue
|
|
66
|
-
|
|
67
|
-
try:
|
|
68
|
-
# Get relative path first - used in error reporting if needed
|
|
69
|
-
rel_path = str(path.relative_to(directory))
|
|
70
|
-
content = path.read_text()
|
|
71
|
-
checksum = await compute_checksum(content)
|
|
72
|
-
result.files[rel_path] = checksum
|
|
73
|
-
|
|
74
|
-
except Exception as e:
|
|
75
|
-
rel_path = str(path.relative_to(directory))
|
|
76
|
-
result.errors[rel_path] = str(e)
|
|
77
|
-
logger.error(f"Failed to read {rel_path}: {e}")
|
|
78
|
-
|
|
79
|
-
logger.debug(f"Found {len(result.files)} markdown files")
|
|
80
|
-
if result.errors:
|
|
81
|
-
logger.warning(f"Encountered {len(result.errors)} errors while scanning")
|
|
82
|
-
|
|
83
|
-
return result
|
|
84
|
-
|
|
85
|
-
async def find_changes(
|
|
86
|
-
self, directory: Path, db_file_state: Dict[str, FileState]
|
|
87
|
-
) -> SyncReport:
|
|
88
|
-
"""Find changes between filesystem and database."""
|
|
89
|
-
# Get current files and checksums
|
|
90
|
-
scan_result = await self.scan_directory(directory)
|
|
91
|
-
current_files = scan_result.files
|
|
92
|
-
|
|
93
|
-
# Build report
|
|
94
|
-
report = SyncReport(total=len(current_files))
|
|
95
|
-
|
|
96
|
-
# Track potentially moved files by checksum
|
|
97
|
-
files_by_checksum = {} # checksum -> file_path
|
|
98
|
-
|
|
99
|
-
# First find potential new files and record checksums
|
|
100
|
-
for file_path, checksum in current_files.items():
|
|
101
|
-
logger.debug(f"{file_path} ({checksum[:8]})")
|
|
102
|
-
|
|
103
|
-
if file_path not in db_file_state:
|
|
104
|
-
# Could be new or could be the destination of a move
|
|
105
|
-
report.new.add(file_path)
|
|
106
|
-
files_by_checksum[checksum] = file_path
|
|
107
|
-
elif checksum != db_file_state[file_path].checksum:
|
|
108
|
-
report.modified.add(file_path)
|
|
109
|
-
|
|
110
|
-
report.checksums[file_path] = checksum
|
|
111
|
-
|
|
112
|
-
# Now detect moves and deletions
|
|
113
|
-
for db_file_path, db_state in db_file_state.items():
|
|
114
|
-
if db_file_path not in current_files:
|
|
115
|
-
if db_state.checksum in files_by_checksum:
|
|
116
|
-
# Found a move - file exists at new path with same checksum
|
|
117
|
-
new_path = files_by_checksum[db_state.checksum]
|
|
118
|
-
report.moves[db_file_path] = new_path
|
|
119
|
-
# Remove from new files since it's a move
|
|
120
|
-
report.new.remove(new_path)
|
|
121
|
-
else:
|
|
122
|
-
# Actually deleted
|
|
123
|
-
report.deleted.add(db_file_path)
|
|
124
|
-
|
|
125
|
-
# Log summary
|
|
126
|
-
logger.debug(f"Total files: {report.total}")
|
|
127
|
-
logger.debug(f"Changes found: {report.total_changes}")
|
|
128
|
-
logger.debug(f" New: {len(report.new)}")
|
|
129
|
-
logger.debug(f" Modified: {len(report.modified)}")
|
|
130
|
-
logger.debug(f" Moved: {len(report.moves)}")
|
|
131
|
-
logger.debug(f" Deleted: {len(report.deleted)}")
|
|
132
|
-
|
|
133
|
-
if scan_result.errors: # pragma: no cover
|
|
134
|
-
logger.warning("Files skipped due to errors:")
|
|
135
|
-
for file_path, error in scan_result.errors.items():
|
|
136
|
-
logger.warning(f" {file_path}: {error}")
|
|
137
|
-
|
|
138
|
-
return report
|
|
139
|
-
|
|
140
|
-
async def get_db_file_state(self, db_records: Sequence[Entity]) -> Dict[str, FileState]:
|
|
141
|
-
"""Get file_path and checksums from database.
|
|
142
|
-
Args:
|
|
143
|
-
db_records: database records
|
|
144
|
-
Returns:
|
|
145
|
-
Dict mapping file paths to FileState
|
|
146
|
-
:param db_records: the data from the db
|
|
147
|
-
"""
|
|
148
|
-
return {
|
|
149
|
-
r.file_path: FileState(
|
|
150
|
-
file_path=r.file_path, permalink=r.permalink, checksum=r.checksum or ""
|
|
151
|
-
)
|
|
152
|
-
for r in db_records
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async def find_knowledge_changes(self, directory: Path) -> SyncReport:
|
|
156
|
-
"""Find changes in knowledge directory."""
|
|
157
|
-
db_file_state = await self.get_db_file_state(await self.entity_repository.find_all())
|
|
158
|
-
return await self.find_changes(directory=directory, db_file_state=db_file_state)
|
basic_memory/sync/utils.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"""Types and utilities for file sync."""
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from typing import Set, Dict, Optional
|
|
5
|
-
|
|
6
|
-
from watchfiles import Change
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class SyncReport:
|
|
12
|
-
"""Report of file changes found compared to database state.
|
|
13
|
-
|
|
14
|
-
Attributes:
|
|
15
|
-
total: Total number of files in directory being synced
|
|
16
|
-
new: Files that exist on disk but not in database
|
|
17
|
-
modified: Files that exist in both but have different checksums
|
|
18
|
-
deleted: Files that exist in database but not on disk
|
|
19
|
-
moves: Files that have been moved from one location to another
|
|
20
|
-
checksums: Current checksums for files on disk
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
total: int = 0
|
|
24
|
-
# We keep paths as strings in sets/dicts for easier serialization
|
|
25
|
-
new: Set[str] = field(default_factory=set)
|
|
26
|
-
modified: Set[str] = field(default_factory=set)
|
|
27
|
-
deleted: Set[str] = field(default_factory=set)
|
|
28
|
-
moves: Dict[str, str] = field(default_factory=dict) # old_path -> new_path
|
|
29
|
-
checksums: Dict[str, str] = field(default_factory=dict) # path -> checksum
|
|
30
|
-
|
|
31
|
-
@property
|
|
32
|
-
def total_changes(self) -> int:
|
|
33
|
-
"""Total number of changes."""
|
|
34
|
-
return len(self.new) + len(self.modified) + len(self.deleted) + len(self.moves)
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: basic-memory
|
|
3
|
-
Version: 0.2.12
|
|
4
|
-
Summary: Local-first knowledge management combining Zettelkasten with knowledge graphs
|
|
5
|
-
Project-URL: Homepage, https://github.com/basicmachines-co/basic-memory
|
|
6
|
-
Project-URL: Repository, https://github.com/basicmachines-co/basic-memory
|
|
7
|
-
Project-URL: Documentation, https://github.com/basicmachines-co/basic-memory#readme
|
|
8
|
-
Author-email: Basic Machines <hello@basic-machines.co>
|
|
9
|
-
License: AGPL-3.0-or-later
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Python: >=3.12.1
|
|
12
|
-
Requires-Dist: aiosqlite>=0.20.0
|
|
13
|
-
Requires-Dist: alembic>=1.14.1
|
|
14
|
-
Requires-Dist: dateparser>=1.2.0
|
|
15
|
-
Requires-Dist: fastapi[standard]>=0.115.8
|
|
16
|
-
Requires-Dist: greenlet>=3.1.1
|
|
17
|
-
Requires-Dist: icecream>=2.1.3
|
|
18
|
-
Requires-Dist: loguru>=0.7.3
|
|
19
|
-
Requires-Dist: markdown-it-py>=3.0.0
|
|
20
|
-
Requires-Dist: mcp>=1.2.0
|
|
21
|
-
Requires-Dist: pydantic-settings>=2.6.1
|
|
22
|
-
Requires-Dist: pydantic[email,timezone]>=2.10.3
|
|
23
|
-
Requires-Dist: pyright>=1.1.390
|
|
24
|
-
Requires-Dist: python-frontmatter>=1.1.0
|
|
25
|
-
Requires-Dist: pyyaml>=6.0.1
|
|
26
|
-
Requires-Dist: qasync>=0.27.1
|
|
27
|
-
Requires-Dist: rich>=13.9.4
|
|
28
|
-
Requires-Dist: sqlalchemy>=2.0.0
|
|
29
|
-
Requires-Dist: typer>=0.9.0
|
|
30
|
-
Requires-Dist: unidecode>=1.3.8
|
|
31
|
-
Requires-Dist: watchfiles>=1.0.4
|
|
32
|
-
Description-Content-Type: text/markdown
|
|
33
|
-
|
|
34
|
-
# Basic Memory
|
|
35
|
-
|
|
36
|
-
Basic Memory lets you build persistent knowledge through natural conversations with Large Language Models (LLMs) like
|
|
37
|
-
Claude, while keeping everything in simple markdown files on your computer. It uses the Model Context Protocol (MCP) to
|
|
38
|
-
enable any compatible LLM to read and write to your local knowledge base.
|
|
39
|
-
|
|
40
|
-
## What is Basic Memory?
|
|
41
|
-
|
|
42
|
-
Most people use LLMs like calculators - paste in some text, expect to get an answer back, repeat. Each conversation
|
|
43
|
-
starts fresh,
|
|
44
|
-
and any knowledge or context is lost. Some try to work around this by:
|
|
45
|
-
|
|
46
|
-
- Saving chat histories (but they're hard to reference)
|
|
47
|
-
- Copying and pasting previous conversations (messy and repetitive)
|
|
48
|
-
- Using RAG systems to query documents (complex and often cloud-based)
|
|
49
|
-
|
|
50
|
-
Basic Memory takes a different approach by letting both humans and LLMs read and write knowledge naturally using
|
|
51
|
-
standard markdown files. This means:
|
|
52
|
-
|
|
53
|
-
- Your knowledge stays in files you control
|
|
54
|
-
- Both you and the LLM can read and write notes
|
|
55
|
-
- Context persists across conversations
|
|
56
|
-
- Context stays local and user controlled
|
|
57
|
-
|
|
58
|
-
## How It Works in Practice
|
|
59
|
-
|
|
60
|
-
Let's say you're working on a new project and want to capture design decisions. Here's how it works:
|
|
61
|
-
|
|
62
|
-
1. Start by chatting normally:
|
|
63
|
-
|
|
64
|
-
```markdown
|
|
65
|
-
We need to design a new auth system, some key features:
|
|
66
|
-
|
|
67
|
-
- local first, don't delegate users to third party system
|
|
68
|
-
- support multiple platforms via jwt
|
|
69
|
-
- want to keep it simple but secure
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
... continue conversation.
|
|
73
|
-
|
|
74
|
-
2. Ask Claude to help structure this knowledge:
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
"Lets write a note about the auth system design."
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Claude creates a new markdown file on your system (which you can see instantly in Obsidian or your editor):
|
|
81
|
-
|
|
82
|
-
```markdown
|
|
83
|
-
---
|
|
84
|
-
title: Auth System Design
|
|
85
|
-
permalink: auth-system-design
|
|
86
|
-
tags
|
|
87
|
-
- design
|
|
88
|
-
- auth
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
# Auth System Design
|
|
92
|
-
|
|
93
|
-
## Observations
|
|
94
|
-
|
|
95
|
-
- [requirement] Local-first authentication without third party delegation
|
|
96
|
-
- [tech] JWT-based auth for cross-platform support
|
|
97
|
-
- [principle] Balance simplicity with security
|
|
98
|
-
|
|
99
|
-
## Relations
|
|
100
|
-
|
|
101
|
-
- implements [[Security Requirements]]
|
|
102
|
-
- relates_to [[Platform Support]]
|
|
103
|
-
- referenced_by [[JWT Implementation]]
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
The note embeds semantic content (Observations) and links to other topics (Relations) via simple markdown formatting.
|
|
107
|
-
|
|
108
|
-
3. You can edit this file directly in your editor in real time:
|
|
109
|
-
|
|
110
|
-
```markdown
|
|
111
|
-
# Auth System Design
|
|
112
|
-
|
|
113
|
-
## Observations
|
|
114
|
-
|
|
115
|
-
- [requirement] Local-first authentication without third party delegation
|
|
116
|
-
- [tech] JWT-based auth for cross-platform support
|
|
117
|
-
- [principle] Balance simplicity with security
|
|
118
|
-
- [decision] Will use bcrypt for password hashing # Added by you
|
|
119
|
-
|
|
120
|
-
## Relations
|
|
121
|
-
|
|
122
|
-
- implements [[Security Requirements]]
|
|
123
|
-
- relates_to [[Platform Support]]
|
|
124
|
-
- referenced_by [[JWT Implementation]]
|
|
125
|
-
- blocks [[User Service]] # Added by you
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
4. In a new chat with Claude, you can reference this knowledge:
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
"Claude, look at memory://auth-system-design for context about our auth system"
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Claude can now build rich context from the knowledge graph. For example:
|
|
135
|
-
|
|
136
|
-
```
|
|
137
|
-
Following relation 'implements [[Security Requirements]]':
|
|
138
|
-
- Found authentication best practices
|
|
139
|
-
- OWASP guidelines for JWT
|
|
140
|
-
- Rate limiting requirements
|
|
141
|
-
|
|
142
|
-
Following relation 'relates_to [[Platform Support]]':
|
|
143
|
-
- Mobile auth requirements
|
|
144
|
-
- Browser security considerations
|
|
145
|
-
- JWT storage strategies
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Each related document can lead to more context, building a rich semantic understanding of your knowledge base. All of
|
|
149
|
-
this context comes from standard markdown files that both humans and LLMs can read and write.
|
|
150
|
-
|
|
151
|
-
Everything stays in local markdown files that you can:
|
|
152
|
-
|
|
153
|
-
- Edit in any text editor
|
|
154
|
-
- Version via git
|
|
155
|
-
- Back up normally
|
|
156
|
-
- Share when you want to
|
|
157
|
-
|
|
158
|
-
## Technical Implementation
|
|
159
|
-
|
|
160
|
-
Under the hood, Basic Memory:
|
|
161
|
-
|
|
162
|
-
1. Stores everything in markdown files
|
|
163
|
-
2. Uses a SQLite database just for searching and indexing
|
|
164
|
-
3. Extracts semantic meaning from simple markdown patterns
|
|
165
|
-
4. Maintains a local knowledge graph from file content
|
|
166
|
-
|
|
167
|
-
The file format is just markdown with some simple markup:
|
|
168
|
-
|
|
169
|
-
Frontmatter
|
|
170
|
-
|
|
171
|
-
- title
|
|
172
|
-
- type
|
|
173
|
-
- permalink
|
|
174
|
-
- optional metadata
|
|
175
|
-
|
|
176
|
-
Observations
|
|
177
|
-
|
|
178
|
-
- facts about a topic
|
|
179
|
-
|
|
180
|
-
```markdown
|
|
181
|
-
- [category] content #tag (optional context)
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Relations
|
|
185
|
-
|
|
186
|
-
- links to other topics
|
|
187
|
-
|
|
188
|
-
```markdown
|
|
189
|
-
- relation_type [[WikiLink]] (optional context)
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
Example:
|
|
193
|
-
|
|
194
|
-
```markdown
|
|
195
|
-
---
|
|
196
|
-
title: Note tile
|
|
197
|
-
type: note
|
|
198
|
-
permalink: unique/stable/id # Added automatically
|
|
199
|
-
tags
|
|
200
|
-
- tag1
|
|
201
|
-
- tag2
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
# Note Title
|
|
205
|
-
|
|
206
|
-
Regular markdown content...
|
|
207
|
-
|
|
208
|
-
## Observations
|
|
209
|
-
|
|
210
|
-
- [category] Structured knowledge #tag (optional context)
|
|
211
|
-
- [idea] Another observation
|
|
212
|
-
|
|
213
|
-
## Relations
|
|
214
|
-
|
|
215
|
-
- links_to [[Other Note]]
|
|
216
|
-
- implements [[Some Spec]]
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
Basic Memory will parse the markdown and derive the semantic relationships in the content. When you run
|
|
220
|
-
`basic-memory sync`:
|
|
221
|
-
|
|
222
|
-
1. New and changed files are detected
|
|
223
|
-
2. Markdown patterns become semantic knowledge:
|
|
224
|
-
|
|
225
|
-
- `[tech]` becomes a categorized observation
|
|
226
|
-
- `[[WikiLink]]` creates a relation in the knowledge graph
|
|
227
|
-
- Tags and metadata are indexed for search
|
|
228
|
-
|
|
229
|
-
3. A SQLite database maintains these relationships for fast querying
|
|
230
|
-
4. Claude and other MCP-compatible LLMs can access this knowledge via memory:// URLs
|
|
231
|
-
|
|
232
|
-
This creates a two-way flow where:
|
|
233
|
-
|
|
234
|
-
- Humans write and edit markdown files
|
|
235
|
-
- LLMs read and write through the MCP protocol
|
|
236
|
-
- Sync keeps everything consistent
|
|
237
|
-
- All knowledge stays in local files.
|
|
238
|
-
|
|
239
|
-
## Using with Claude
|
|
240
|
-
|
|
241
|
-
Basic Memory works with the Claude desktop app (https://claude.ai/):
|
|
242
|
-
|
|
243
|
-
1. Install Basic Memory locally:
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
{
|
|
247
|
-
"mcpServers": {
|
|
248
|
-
"basic-memory": {
|
|
249
|
-
"command": "uvx",
|
|
250
|
-
"args": [
|
|
251
|
-
"basic-memory"
|
|
252
|
-
]
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
2. Add to Claude Desktop:
|
|
258
|
-
|
|
259
|
-
```
|
|
260
|
-
Basic Memory is available with these tools:
|
|
261
|
-
- write_note() for creating/updating notes
|
|
262
|
-
- read_note() for loading notes
|
|
263
|
-
- build_context() to load notes via memory:// URLs
|
|
264
|
-
- recent_activity() to find recently updated information
|
|
265
|
-
- search() to search infomation in the knowledge base
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
3. Install via uv
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
uv add basic-memory
|
|
272
|
-
|
|
273
|
-
# sync local knowledge updates
|
|
274
|
-
basic-memory sync
|
|
275
|
-
|
|
276
|
-
# run realtime sync process
|
|
277
|
-
basic-memory sync --watch
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Design Philosophy
|
|
281
|
-
|
|
282
|
-
Basic Memory is built on some key ideas:
|
|
283
|
-
|
|
284
|
-
- Your knowledge should stay in files you control
|
|
285
|
-
- Both humans and AI should use natural formats
|
|
286
|
-
- Simple text patterns can capture rich meaning
|
|
287
|
-
- Local-first doesn't mean feature-poor
|
|
288
|
-
|
|
289
|
-
## License
|
|
290
|
-
|
|
291
|
-
AGPL-3.0
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
basic_memory/__init__.py,sha256=_ij75bUYM3LqRQYHrJ1kLnDuUyauuHilEBF96OFw9hA,122
|
|
2
|
-
basic_memory/config.py,sha256=PZA2qgwKACvKfRcM3H-BPB_8FYVhgZAwTmlKJ3ROfhU,1643
|
|
3
|
-
basic_memory/db.py,sha256=BFZCp4aJ7Xj9_ZCMz0rnSBuCy5xIMvvWjSImmuKzdWg,4605
|
|
4
|
-
basic_memory/deps.py,sha256=UzivBw6e6iYcU_8SQ8LNCmSsmFyHfjdzfWvnfNzqbRc,5375
|
|
5
|
-
basic_memory/file_utils.py,sha256=gp7RCFWaddFnELIyTc1E19Rk8jJsrKshG2n8ZZR-kKA,5751
|
|
6
|
-
basic_memory/utils.py,sha256=HiLorP5_YCQeNeTcDqvnkrwY7OBaFRS3i_hdV9iWKLs,2374
|
|
7
|
-
basic_memory/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
|
|
8
|
-
basic_memory/alembic/env.py,sha256=XqJVQhS41ba7NCPmmaSZ09_tbSLnwsY2bcpJpqx_ZTc,2107
|
|
9
|
-
basic_memory/alembic/migrations.py,sha256=CIbkMHEKZ60aDUhFGSQjv8kDNM7sazfvEYHGGcy1DBk,858
|
|
10
|
-
basic_memory/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
|
11
|
-
basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py,sha256=lTbWlAnd1es7xU99DoJgfaRe1_Kte8TL98riqeKGV80,4363
|
|
12
|
-
basic_memory/api/__init__.py,sha256=wCpj-21j1D0KzKl9Ql6unLBVFY0K1uGp_FeSZRKtqpk,72
|
|
13
|
-
basic_memory/api/app.py,sha256=AEHcslN4SBq5Ni7q7wkG4jDH0-SwMWV2DeTdaUSQKns,2083
|
|
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=_Gp5HSJr-L-GUkQKbEP2bAZvCY8Smd-sBNWpGyqXS4c,1056
|
|
18
|
-
basic_memory/api/routers/search_router.py,sha256=dCRnBbp3r966U8UYwgAaxZBbg7yX7pC8QJqagdACUi0,1086
|
|
19
|
-
basic_memory/cli/__init__.py,sha256=arcKLAWRDhPD7x5t80MlviZeYzwHZ0GZigyy3NKVoGk,33
|
|
20
|
-
basic_memory/cli/app.py,sha256=hF4MgYCgFql4J6qi3lguqc6HQdP2gm6PpvtSxKBSjZc,34
|
|
21
|
-
basic_memory/cli/main.py,sha256=Vvpmh33MSZJftCENEjzJH3yBbxD4B40Pl6IBIumiVX4,505
|
|
22
|
-
basic_memory/cli/commands/__init__.py,sha256=OQGLaKTsOdPsp2INM_pHzmOlbVfdL0sytBNgvqTqCDY,159
|
|
23
|
-
basic_memory/cli/commands/db.py,sha256=I92CRufPskvHl9c90f5Eg7U7D0uIzLBiwngQuAh5cLk,772
|
|
24
|
-
basic_memory/cli/commands/import_memory_json.py,sha256=ZXSRHH_3GgJzmMLvDulakKIpzsKxrZIUmEuWgJmwMOE,5138
|
|
25
|
-
basic_memory/cli/commands/mcp.py,sha256=a0v54iFL01_eykODHuWIupTHCn-COm-WZGdSO5iinc0,563
|
|
26
|
-
basic_memory/cli/commands/status.py,sha256=aNpP8u-ECoVTiL5MIb-D2cXXLJtv6z2z8CMCh5nt2KY,5782
|
|
27
|
-
basic_memory/cli/commands/sync.py,sha256=sb6OGl9IVZLmGfHUm0-aexD365BRTaHJhpwqt0O5yxk,7035
|
|
28
|
-
basic_memory/markdown/__init__.py,sha256=DdzioCWtDnKaq05BHYLgL_78FawEHLpLXnp-kPSVfIc,501
|
|
29
|
-
basic_memory/markdown/entity_parser.py,sha256=sJk8TRUd9cAaIjATiJn7dBQRorrYngRbd7MRVfc0Oc4,3781
|
|
30
|
-
basic_memory/markdown/markdown_processor.py,sha256=mV3pYoDTaQMEl1tA5n_XztBvNlYyH2SzKs4vnKdAet4,4952
|
|
31
|
-
basic_memory/markdown/plugins.py,sha256=gtIzKRjoZsyvBqLpVNnrmzl_cbTZ5ZGn8kcuXxQjRko,6639
|
|
32
|
-
basic_memory/markdown/schemas.py,sha256=mzVEDUhH98kwETMknjkKw5H697vg_zUapsJkJVi17ho,1894
|
|
33
|
-
basic_memory/markdown/utils.py,sha256=ZtHa-dG--ZwFEUC3jfl04KZGhM_ZWo5b-8d8KpJ90gY,2758
|
|
34
|
-
basic_memory/mcp/__init__.py,sha256=dsDOhKqjYeIbCULbHIxfcItTbqudEuEg1Np86eq0GEQ,35
|
|
35
|
-
basic_memory/mcp/async_client.py,sha256=Eo345wANiBRSM4u3j_Vd6Ax4YtMg7qbWd9PIoFfj61I,236
|
|
36
|
-
basic_memory/mcp/server.py,sha256=L92Vit7llaKT9NlPZfxdp67C33niObmRH2QFyUhmnD0,355
|
|
37
|
-
basic_memory/mcp/tools/__init__.py,sha256=MHZmWw016N0qbtC3f186Jg1tPzh2g88_ZsCKJ0oyrrs,873
|
|
38
|
-
basic_memory/mcp/tools/knowledge.py,sha256=2U8YUKCizsAETHCC1mBVKMfCEef6tlc_pa2wOmA9mD4,2016
|
|
39
|
-
basic_memory/mcp/tools/memory.py,sha256=gl4MBm9l2lMOfu_xmUqjoZacWSIHOAYZiAm8z7oDuY8,5203
|
|
40
|
-
basic_memory/mcp/tools/notes.py,sha256=4GKnhDK53UkeZtpZENQ9id9XdemKxLzGwMQJeuX-Kok,3772
|
|
41
|
-
basic_memory/mcp/tools/search.py,sha256=tx6aIuB2FWmmrvzu3RHSQvszlk-zHcwrWhkLLHWjuZc,1105
|
|
42
|
-
basic_memory/mcp/tools/utils.py,sha256=icm-Xyqw3GxooGYkXqjEjoZvIGy_Z3CPw-uUYBxR_YQ,4831
|
|
43
|
-
basic_memory/models/__init__.py,sha256=Bf0xXV_ryndogvZDiVM_Wb6iV2fHUxYNGMZNWNcZi0s,307
|
|
44
|
-
basic_memory/models/base.py,sha256=4hAXJ8CE1RnjKhb23lPd-QM7G_FXIdTowMJ9bRixspU,225
|
|
45
|
-
basic_memory/models/knowledge.py,sha256=R05mLr2GXDfUcmPe2ja20wvzP818b4npnxL1PvQooEY,5921
|
|
46
|
-
basic_memory/models/search.py,sha256=IB-ySJUqlQq9FqLGfWnraIFcB_brWa9eBwsQP1rVTeI,1164
|
|
47
|
-
basic_memory/repository/__init__.py,sha256=TnscLXARq2iOgQZFvQoT9X1Bn9SB_7s1xw2fOqRs3Jg,252
|
|
48
|
-
basic_memory/repository/entity_repository.py,sha256=VFLymzJ1W6AZru_s1S3U6nlqSprBrVV5Toy0-qysIfw,3524
|
|
49
|
-
basic_memory/repository/observation_repository.py,sha256=BOcy4wARqCXu-thYyt7mPxt2A2C8TW0le3s_X9wrK6I,1701
|
|
50
|
-
basic_memory/repository/relation_repository.py,sha256=DwpTcn9z_1sZQcyMOUABz1k1VSwo_AU63x2zR7aerTk,2933
|
|
51
|
-
basic_memory/repository/repository.py,sha256=jUScHWOfcB2FajwVZ2Sbjtg-gSI2Y2rhiIaTULjvmn8,11321
|
|
52
|
-
basic_memory/repository/search_repository.py,sha256=OfocJZ7EWum33klFFvsLE7BEUnZPda1BNSwrbkRiXko,9233
|
|
53
|
-
basic_memory/schemas/__init__.py,sha256=eVxrtuPT7-9JIQ7UDx2J8t8xlS3u0iUkV_VLNbzvxo4,1575
|
|
54
|
-
basic_memory/schemas/base.py,sha256=epSauNNVZ2lRLATf-HIzqeberq4ZBTgxliNmjitAsWc,5538
|
|
55
|
-
basic_memory/schemas/delete.py,sha256=UAR2JK99WMj3gP-yoGWlHD3eZEkvlTSRf8QoYIE-Wfw,1180
|
|
56
|
-
basic_memory/schemas/discovery.py,sha256=6Y2tUiv9f06rFTsa8_wTH2haS2bhCfuQh0uW33hwdd8,876
|
|
57
|
-
basic_memory/schemas/memory.py,sha256=mqslazV0lQswtbNgYv_y2-KxmifIvRlg5I3IuTTMnO4,2882
|
|
58
|
-
basic_memory/schemas/request.py,sha256=rt_guNWrUMePJvDmsh1g1dc7IqEY6K6mGXMKx8tBCj8,1614
|
|
59
|
-
basic_memory/schemas/response.py,sha256=2su3YP-gkbw4MvgGtgZLHEuTp6RuVlK736KakaV7fP4,6273
|
|
60
|
-
basic_memory/schemas/search.py,sha256=pWBA1-xEQ3rH8vLIgrQT4oygq9MMwr0B7VCbFafVVOw,3278
|
|
61
|
-
basic_memory/services/__init__.py,sha256=oop6SKmzV4_NAYt9otGnupLGVCCKIVgxEcdRQWwh25I,197
|
|
62
|
-
basic_memory/services/context_service.py,sha256=Bu1wVl9q3FDGbGChrLqgFGQW95-W1OfjNqq6SGljqWg,9388
|
|
63
|
-
basic_memory/services/entity_service.py,sha256=bm_Z63_AJmXiRQkVYWwoB3PYLMW1t1xS3Nh0Nm9SwiI,11538
|
|
64
|
-
basic_memory/services/exceptions.py,sha256=VGlCLd4UD2w5NWKqC7QpG4jOM_hA7jKRRM-MqvEVMNk,288
|
|
65
|
-
basic_memory/services/file_service.py,sha256=r4JfPY1wyenAH0Y-iq7vGHPwT616ayUWoLnvA1NuzpA,5695
|
|
66
|
-
basic_memory/services/link_resolver.py,sha256=VdhoPAVa65T6LW7kSTLWts55zbnnN481fr7VLz3HaXE,4513
|
|
67
|
-
basic_memory/services/search_service.py,sha256=iB-BgFwInrJxTfYBerj68QORlMv46wYy2-ceQx61Dd8,7839
|
|
68
|
-
basic_memory/services/service.py,sha256=V-d_8gOV07zGIQDpL-Ksqs3ZN9l3qf3HZOK1f_YNTag,336
|
|
69
|
-
basic_memory/sync/__init__.py,sha256=ko0xLQv1S5U7sAOmIP2XKl03akVPzoY-a9m3TFPcMh4,193
|
|
70
|
-
basic_memory/sync/file_change_scanner.py,sha256=4whJej6t9sxwUp1ox93efJ0bBHSnAr6STpk_PsKU6to,5784
|
|
71
|
-
basic_memory/sync/sync_service.py,sha256=nAOX4N90lbpRJeq5tRR_7PYptIoWwhXMUljE7yrneF4,7087
|
|
72
|
-
basic_memory/sync/utils.py,sha256=uc7VLK34HufKyKavGwTPGU-ARfoQr_jYbjs4fsmUvuo,1233
|
|
73
|
-
basic_memory/sync/watch_service.py,sha256=CtKBrP1imI3ZSEgJl7Ffi-JZ_oDGKrhiyGgs41h5QYI,7563
|
|
74
|
-
basic_memory-0.2.12.dist-info/METADATA,sha256=cDO0_phlsla6a99ue5_lnioXPUimFKfuC-V7itW3-0E,7540
|
|
75
|
-
basic_memory-0.2.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
76
|
-
basic_memory-0.2.12.dist-info/entry_points.txt,sha256=IDQa_VmVTzmvMrpnjhEfM0S3F--XsVGEj3MpdJfuo-Q,59
|
|
77
|
-
basic_memory-0.2.12.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
78
|
-
basic_memory-0.2.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|