basic-memory 0.10.0__py3-none-any.whl → 0.11.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.
- basic_memory/__init__.py +1 -1
- basic_memory/cli/commands/__init__.py +1 -2
- basic_memory/cli/commands/db.py +15 -2
- basic_memory/cli/commands/project.py +168 -2
- basic_memory/cli/commands/tool.py +8 -4
- basic_memory/config.py +45 -10
- basic_memory/markdown/entity_parser.py +13 -10
- basic_memory/markdown/schemas.py +1 -1
- basic_memory/mcp/prompts/ai_assistant_guide.py +1 -3
- basic_memory/mcp/prompts/continue_conversation.py +3 -3
- basic_memory/mcp/prompts/search.py +4 -4
- basic_memory/mcp/resources/ai_assistant_guide.md +413 -0
- basic_memory/mcp/tools/__init__.py +2 -2
- basic_memory/mcp/tools/read_note.py +3 -3
- basic_memory/mcp/tools/search.py +11 -11
- basic_memory/mcp/tools/write_note.py +17 -7
- basic_memory/services/entity_service.py +11 -1
- basic_memory/sync/watch_service.py +1 -1
- basic_memory/utils.py +27 -1
- {basic_memory-0.10.0.dist-info → basic_memory-0.11.0.dist-info}/METADATA +17 -4
- {basic_memory-0.10.0.dist-info → basic_memory-0.11.0.dist-info}/RECORD +24 -24
- {basic_memory-0.10.0.dist-info → basic_memory-0.11.0.dist-info}/entry_points.txt +1 -0
- basic_memory/cli/commands/project_info.py +0 -167
- {basic_memory-0.10.0.dist-info → basic_memory-0.11.0.dist-info}/WHEEL +0 -0
- {basic_memory-0.10.0.dist-info → basic_memory-0.11.0.dist-info}/licenses/LICENSE +0 -0
basic_memory/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""CLI commands for basic-memory."""
|
|
2
2
|
|
|
3
3
|
from . import status, sync, db, import_memory_json, mcp, import_claude_conversations
|
|
4
|
-
from . import import_claude_projects, import_chatgpt, tool, project
|
|
4
|
+
from . import import_claude_projects, import_chatgpt, tool, project
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
7
|
"status",
|
|
@@ -14,5 +14,4 @@ __all__ = [
|
|
|
14
14
|
"import_chatgpt",
|
|
15
15
|
"tool",
|
|
16
16
|
"project",
|
|
17
|
-
"project_info",
|
|
18
17
|
]
|
basic_memory/cli/commands/db.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Database management commands."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
3
5
|
import typer
|
|
4
6
|
from loguru import logger
|
|
5
7
|
|
|
6
|
-
from basic_memory
|
|
8
|
+
from basic_memory import db
|
|
7
9
|
from basic_memory.cli.app import app
|
|
10
|
+
from basic_memory.config import config
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
@app.command()
|
|
@@ -14,7 +17,17 @@ def reset(
|
|
|
14
17
|
"""Reset database (drop all tables and recreate)."""
|
|
15
18
|
if typer.confirm("This will delete all data in your db. Are you sure?"):
|
|
16
19
|
logger.info("Resetting database...")
|
|
17
|
-
|
|
20
|
+
# Get database path
|
|
21
|
+
db_path = config.database_path
|
|
22
|
+
|
|
23
|
+
# Delete the database file if it exists
|
|
24
|
+
if db_path.exists():
|
|
25
|
+
db_path.unlink()
|
|
26
|
+
logger.info(f"Database file deleted: {db_path}")
|
|
27
|
+
|
|
28
|
+
# Create a new empty database
|
|
29
|
+
asyncio.run(db.run_migrations(config))
|
|
30
|
+
logger.info("Database reset complete")
|
|
18
31
|
|
|
19
32
|
if reindex:
|
|
20
33
|
# Import and run sync
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Command module for basic-memory project management."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import os
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
@@ -9,6 +10,12 @@ from rich.table import Table
|
|
|
9
10
|
|
|
10
11
|
from basic_memory.cli.app import app
|
|
11
12
|
from basic_memory.config import ConfigManager, config
|
|
13
|
+
from basic_memory.mcp.tools.project_info import project_info
|
|
14
|
+
import json
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
from rich.tree import Tree
|
|
12
19
|
|
|
13
20
|
console = Console()
|
|
14
21
|
|
|
@@ -92,12 +99,22 @@ def remove_project(
|
|
|
92
99
|
def set_default_project(
|
|
93
100
|
name: str = typer.Argument(..., help="Name of the project to set as default"),
|
|
94
101
|
) -> None:
|
|
95
|
-
"""Set the default project."""
|
|
102
|
+
"""Set the default project and activate it for the current session."""
|
|
96
103
|
config_manager = ConfigManager()
|
|
97
104
|
|
|
98
105
|
try:
|
|
106
|
+
# Set the default project
|
|
99
107
|
config_manager.set_default_project(name)
|
|
100
|
-
|
|
108
|
+
|
|
109
|
+
# Also activate it for the current session by setting the environment variable
|
|
110
|
+
os.environ["BASIC_MEMORY_PROJECT"] = name
|
|
111
|
+
|
|
112
|
+
# Reload configuration to apply the change
|
|
113
|
+
from importlib import reload
|
|
114
|
+
from basic_memory import config as config_module
|
|
115
|
+
|
|
116
|
+
reload(config_module)
|
|
117
|
+
console.print(f"[green]Project '{name}' set as default and activated[/green]")
|
|
101
118
|
except ValueError as e: # pragma: no cover
|
|
102
119
|
console.print(f"[red]Error: {e}[/red]")
|
|
103
120
|
raise typer.Exit(1)
|
|
@@ -117,3 +134,152 @@ def show_current_project() -> None:
|
|
|
117
134
|
except ValueError: # pragma: no cover
|
|
118
135
|
console.print(f"[yellow]Warning: Project '{current}' not found in configuration[/yellow]")
|
|
119
136
|
console.print(f"Using default project: [cyan]{config_manager.default_project}[/cyan]")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@project_app.command("info")
|
|
140
|
+
def display_project_info(
|
|
141
|
+
json_output: bool = typer.Option(False, "--json", help="Output in JSON format"),
|
|
142
|
+
):
|
|
143
|
+
"""Display detailed information and statistics about the current project."""
|
|
144
|
+
try:
|
|
145
|
+
# Get project info
|
|
146
|
+
info = asyncio.run(project_info())
|
|
147
|
+
|
|
148
|
+
if json_output:
|
|
149
|
+
# Convert to JSON and print
|
|
150
|
+
print(json.dumps(info.model_dump(), indent=2, default=str))
|
|
151
|
+
else:
|
|
152
|
+
# Create rich display
|
|
153
|
+
console = Console()
|
|
154
|
+
|
|
155
|
+
# Project configuration section
|
|
156
|
+
console.print(
|
|
157
|
+
Panel(
|
|
158
|
+
f"[bold]Project:[/bold] {info.project_name}\n"
|
|
159
|
+
f"[bold]Path:[/bold] {info.project_path}\n"
|
|
160
|
+
f"[bold]Default Project:[/bold] {info.default_project}\n",
|
|
161
|
+
title="📊 Basic Memory Project Info",
|
|
162
|
+
expand=False,
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Statistics section
|
|
167
|
+
stats_table = Table(title="📈 Statistics")
|
|
168
|
+
stats_table.add_column("Metric", style="cyan")
|
|
169
|
+
stats_table.add_column("Count", style="green")
|
|
170
|
+
|
|
171
|
+
stats_table.add_row("Entities", str(info.statistics.total_entities))
|
|
172
|
+
stats_table.add_row("Observations", str(info.statistics.total_observations))
|
|
173
|
+
stats_table.add_row("Relations", str(info.statistics.total_relations))
|
|
174
|
+
stats_table.add_row(
|
|
175
|
+
"Unresolved Relations", str(info.statistics.total_unresolved_relations)
|
|
176
|
+
)
|
|
177
|
+
stats_table.add_row("Isolated Entities", str(info.statistics.isolated_entities))
|
|
178
|
+
|
|
179
|
+
console.print(stats_table)
|
|
180
|
+
|
|
181
|
+
# Entity types
|
|
182
|
+
if info.statistics.entity_types:
|
|
183
|
+
entity_types_table = Table(title="📑 Entity Types")
|
|
184
|
+
entity_types_table.add_column("Type", style="blue")
|
|
185
|
+
entity_types_table.add_column("Count", style="green")
|
|
186
|
+
|
|
187
|
+
for entity_type, count in info.statistics.entity_types.items():
|
|
188
|
+
entity_types_table.add_row(entity_type, str(count))
|
|
189
|
+
|
|
190
|
+
console.print(entity_types_table)
|
|
191
|
+
|
|
192
|
+
# Most connected entities
|
|
193
|
+
if info.statistics.most_connected_entities:
|
|
194
|
+
connected_table = Table(title="🔗 Most Connected Entities")
|
|
195
|
+
connected_table.add_column("Title", style="blue")
|
|
196
|
+
connected_table.add_column("Permalink", style="cyan")
|
|
197
|
+
connected_table.add_column("Relations", style="green")
|
|
198
|
+
|
|
199
|
+
for entity in info.statistics.most_connected_entities:
|
|
200
|
+
connected_table.add_row(
|
|
201
|
+
entity["title"], entity["permalink"], str(entity["relation_count"])
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
console.print(connected_table)
|
|
205
|
+
|
|
206
|
+
# Recent activity
|
|
207
|
+
if info.activity.recently_updated:
|
|
208
|
+
recent_table = Table(title="🕒 Recent Activity")
|
|
209
|
+
recent_table.add_column("Title", style="blue")
|
|
210
|
+
recent_table.add_column("Type", style="cyan")
|
|
211
|
+
recent_table.add_column("Last Updated", style="green")
|
|
212
|
+
|
|
213
|
+
for entity in info.activity.recently_updated[:5]: # Show top 5
|
|
214
|
+
updated_at = (
|
|
215
|
+
datetime.fromisoformat(entity["updated_at"])
|
|
216
|
+
if isinstance(entity["updated_at"], str)
|
|
217
|
+
else entity["updated_at"]
|
|
218
|
+
)
|
|
219
|
+
recent_table.add_row(
|
|
220
|
+
entity["title"],
|
|
221
|
+
entity["entity_type"],
|
|
222
|
+
updated_at.strftime("%Y-%m-%d %H:%M"),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
console.print(recent_table)
|
|
226
|
+
|
|
227
|
+
# System status
|
|
228
|
+
system_tree = Tree("🖥️ System Status")
|
|
229
|
+
system_tree.add(f"Basic Memory version: [bold green]{info.system.version}[/bold green]")
|
|
230
|
+
system_tree.add(
|
|
231
|
+
f"Database: [cyan]{info.system.database_path}[/cyan] ([green]{info.system.database_size}[/green])"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Watch status
|
|
235
|
+
if info.system.watch_status: # pragma: no cover
|
|
236
|
+
watch_branch = system_tree.add("Watch Service")
|
|
237
|
+
running = info.system.watch_status.get("running", False)
|
|
238
|
+
status_color = "green" if running else "red"
|
|
239
|
+
watch_branch.add(
|
|
240
|
+
f"Status: [bold {status_color}]{'Running' if running else 'Stopped'}[/bold {status_color}]"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
if running:
|
|
244
|
+
start_time = (
|
|
245
|
+
datetime.fromisoformat(info.system.watch_status.get("start_time", ""))
|
|
246
|
+
if isinstance(info.system.watch_status.get("start_time"), str)
|
|
247
|
+
else info.system.watch_status.get("start_time")
|
|
248
|
+
)
|
|
249
|
+
watch_branch.add(
|
|
250
|
+
f"Running since: [cyan]{start_time.strftime('%Y-%m-%d %H:%M')}[/cyan]"
|
|
251
|
+
)
|
|
252
|
+
watch_branch.add(
|
|
253
|
+
f"Files synced: [green]{info.system.watch_status.get('synced_files', 0)}[/green]"
|
|
254
|
+
)
|
|
255
|
+
watch_branch.add(
|
|
256
|
+
f"Errors: [{'red' if info.system.watch_status.get('error_count', 0) > 0 else 'green'}]{info.system.watch_status.get('error_count', 0)}[/{'red' if info.system.watch_status.get('error_count', 0) > 0 else 'green'}]"
|
|
257
|
+
)
|
|
258
|
+
else:
|
|
259
|
+
system_tree.add("[yellow]Watch service not running[/yellow]")
|
|
260
|
+
|
|
261
|
+
console.print(system_tree)
|
|
262
|
+
|
|
263
|
+
# Available projects
|
|
264
|
+
projects_table = Table(title="📁 Available Projects")
|
|
265
|
+
projects_table.add_column("Name", style="blue")
|
|
266
|
+
projects_table.add_column("Path", style="cyan")
|
|
267
|
+
projects_table.add_column("Default", style="green")
|
|
268
|
+
|
|
269
|
+
for name, path in info.available_projects.items():
|
|
270
|
+
is_default = name == info.default_project
|
|
271
|
+
projects_table.add_row(name, path, "✓" if is_default else "")
|
|
272
|
+
|
|
273
|
+
console.print(projects_table)
|
|
274
|
+
|
|
275
|
+
# Timestamp
|
|
276
|
+
current_time = (
|
|
277
|
+
datetime.fromisoformat(str(info.system.timestamp))
|
|
278
|
+
if isinstance(info.system.timestamp, str)
|
|
279
|
+
else info.system.timestamp
|
|
280
|
+
)
|
|
281
|
+
console.print(f"\nTimestamp: [cyan]{current_time.strftime('%Y-%m-%d %H:%M:%S')}[/cyan]")
|
|
282
|
+
|
|
283
|
+
except Exception as e: # pragma: no cover
|
|
284
|
+
typer.echo(f"Error getting project info: {e}", err=True)
|
|
285
|
+
raise typer.Exit(1)
|
|
@@ -12,7 +12,7 @@ from basic_memory.cli.app import app
|
|
|
12
12
|
from basic_memory.mcp.tools import build_context as mcp_build_context
|
|
13
13
|
from basic_memory.mcp.tools import read_note as mcp_read_note
|
|
14
14
|
from basic_memory.mcp.tools import recent_activity as mcp_recent_activity
|
|
15
|
-
from basic_memory.mcp.tools import
|
|
15
|
+
from basic_memory.mcp.tools import search_notes as mcp_search
|
|
16
16
|
from basic_memory.mcp.tools import write_note as mcp_write_note
|
|
17
17
|
|
|
18
18
|
# Import prompts
|
|
@@ -29,7 +29,7 @@ from basic_memory.schemas.memory import MemoryUrl
|
|
|
29
29
|
from basic_memory.schemas.search import SearchQuery, SearchItemType
|
|
30
30
|
|
|
31
31
|
tool_app = typer.Typer()
|
|
32
|
-
app.add_typer(tool_app, name="tool", help="
|
|
32
|
+
app.add_typer(tool_app, name="tool", help="Access to MCP tools via CLI")
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
@tool_app.command()
|
|
@@ -103,6 +103,7 @@ def write_note(
|
|
|
103
103
|
|
|
104
104
|
@tool_app.command()
|
|
105
105
|
def read_note(identifier: str, page: int = 1, page_size: int = 10):
|
|
106
|
+
"""Read a markdown note from the knowledge base."""
|
|
106
107
|
try:
|
|
107
108
|
note = asyncio.run(mcp_read_note(identifier, page, page_size))
|
|
108
109
|
rprint(note)
|
|
@@ -122,6 +123,7 @@ def build_context(
|
|
|
122
123
|
page_size: int = 10,
|
|
123
124
|
max_related: int = 10,
|
|
124
125
|
):
|
|
126
|
+
"""Get context needed to continue a discussion."""
|
|
125
127
|
try:
|
|
126
128
|
context = asyncio.run(
|
|
127
129
|
mcp_build_context(
|
|
@@ -154,6 +156,7 @@ def recent_activity(
|
|
|
154
156
|
page_size: int = 10,
|
|
155
157
|
max_related: int = 10,
|
|
156
158
|
):
|
|
159
|
+
"""Get recent activity across the knowledge base."""
|
|
157
160
|
try:
|
|
158
161
|
context = asyncio.run(
|
|
159
162
|
mcp_recent_activity(
|
|
@@ -177,8 +180,8 @@ def recent_activity(
|
|
|
177
180
|
raise
|
|
178
181
|
|
|
179
182
|
|
|
180
|
-
@tool_app.command()
|
|
181
|
-
def
|
|
183
|
+
@tool_app.command("search-notes")
|
|
184
|
+
def search_notes(
|
|
182
185
|
query: str,
|
|
183
186
|
permalink: Annotated[bool, typer.Option("--permalink", help="Search permalink values")] = False,
|
|
184
187
|
title: Annotated[bool, typer.Option("--title", help="Search title values")] = False,
|
|
@@ -189,6 +192,7 @@ def search(
|
|
|
189
192
|
page: int = 1,
|
|
190
193
|
page_size: int = 10,
|
|
191
194
|
):
|
|
195
|
+
"""Search across all content in the knowledge base."""
|
|
192
196
|
if permalink and title: # pragma: no cover
|
|
193
197
|
print("Cannot search both permalink and title")
|
|
194
198
|
raise typer.Abort()
|
basic_memory/config.py
CHANGED
|
@@ -5,12 +5,13 @@ import os
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any, Dict, Literal, Optional
|
|
7
7
|
|
|
8
|
-
import basic_memory
|
|
9
|
-
from basic_memory.utils import setup_logging
|
|
10
8
|
from loguru import logger
|
|
11
9
|
from pydantic import Field, field_validator
|
|
12
10
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
13
11
|
|
|
12
|
+
import basic_memory
|
|
13
|
+
from basic_memory.utils import setup_logging
|
|
14
|
+
|
|
14
15
|
DATABASE_NAME = "memory.db"
|
|
15
16
|
DATA_DIR_NAME = ".basic-memory"
|
|
16
17
|
CONFIG_FILE_NAME = "config.json"
|
|
@@ -213,11 +214,45 @@ user_home = Path.home()
|
|
|
213
214
|
log_dir = user_home / DATA_DIR_NAME
|
|
214
215
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
217
|
+
|
|
218
|
+
def get_process_name(): # pragma: no cover
|
|
219
|
+
"""
|
|
220
|
+
get the type of process for logging
|
|
221
|
+
"""
|
|
222
|
+
import sys
|
|
223
|
+
|
|
224
|
+
if "sync" in sys.argv:
|
|
225
|
+
return "sync"
|
|
226
|
+
elif "mcp" in sys.argv:
|
|
227
|
+
return "mcp"
|
|
228
|
+
else:
|
|
229
|
+
return "cli"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
process_name = get_process_name()
|
|
233
|
+
|
|
234
|
+
# Global flag to track if logging has been set up
|
|
235
|
+
_LOGGING_SETUP = False
|
|
236
|
+
|
|
237
|
+
def setup_basic_memory_logging(): # pragma: no cover
|
|
238
|
+
"""Set up logging for basic-memory, ensuring it only happens once."""
|
|
239
|
+
global _LOGGING_SETUP
|
|
240
|
+
if _LOGGING_SETUP:
|
|
241
|
+
# We can't log before logging is set up
|
|
242
|
+
# print("Skipping duplicate logging setup")
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
setup_logging(
|
|
246
|
+
env=config.env,
|
|
247
|
+
home_dir=user_home, # Use user home for logs
|
|
248
|
+
log_level=config.log_level,
|
|
249
|
+
log_file=f"{DATA_DIR_NAME}/basic-memory-{process_name}.log",
|
|
250
|
+
console=False,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
logger.info(f"Starting Basic Memory {basic_memory.__version__} (Project: {config.project})")
|
|
254
|
+
_LOGGING_SETUP = True
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# Set up logging
|
|
258
|
+
setup_basic_memory_logging()
|
|
@@ -4,21 +4,22 @@ Uses markdown-it with plugins to parse structured data from markdown content.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
|
-
from pathlib import Path
|
|
8
7
|
from datetime import datetime
|
|
8
|
+
from pathlib import Path
|
|
9
9
|
from typing import Any, Optional
|
|
10
|
-
import dateparser
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
import dateparser
|
|
13
12
|
import frontmatter
|
|
13
|
+
from markdown_it import MarkdownIt
|
|
14
14
|
|
|
15
15
|
from basic_memory.markdown.plugins import observation_plugin, relation_plugin
|
|
16
16
|
from basic_memory.markdown.schemas import (
|
|
17
|
-
EntityMarkdown,
|
|
18
17
|
EntityFrontmatter,
|
|
18
|
+
EntityMarkdown,
|
|
19
19
|
Observation,
|
|
20
20
|
Relation,
|
|
21
21
|
)
|
|
22
|
+
from basic_memory.utils import parse_tags
|
|
22
23
|
|
|
23
24
|
md = MarkdownIt().use(observation_plugin).use(relation_plugin)
|
|
24
25
|
|
|
@@ -56,11 +57,11 @@ def parse(content: str) -> EntityContent:
|
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
def parse_tags(tags: Any) -> list[str]:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
# def parse_tags(tags: Any) -> list[str]:
|
|
61
|
+
# """Parse tags into list of strings."""
|
|
62
|
+
# if isinstance(tags, (list, tuple)):
|
|
63
|
+
# return [str(t).strip() for t in tags if str(t).strip()]
|
|
64
|
+
# return [t.strip() for t in tags.split(",") if t.strip()]
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
class EntityParser:
|
|
@@ -101,7 +102,9 @@ class EntityParser:
|
|
|
101
102
|
metadata = post.metadata
|
|
102
103
|
metadata["title"] = post.metadata.get("title", absolute_path.name)
|
|
103
104
|
metadata["type"] = post.metadata.get("type", "note")
|
|
104
|
-
|
|
105
|
+
tags = parse_tags(post.metadata.get("tags", [])) # pyright: ignore
|
|
106
|
+
if tags:
|
|
107
|
+
metadata["tags"] = tags
|
|
105
108
|
|
|
106
109
|
# frontmatter
|
|
107
110
|
entity_frontmatter = EntityFrontmatter(
|
basic_memory/markdown/schemas.py
CHANGED
|
@@ -42,7 +42,7 @@ class EntityFrontmatter(BaseModel):
|
|
|
42
42
|
|
|
43
43
|
@property
|
|
44
44
|
def tags(self) -> List[str]:
|
|
45
|
-
return self.metadata.get("tags") if self.metadata else
|
|
45
|
+
return self.metadata.get("tags") if self.metadata else None # pyright: ignore
|
|
46
46
|
|
|
47
47
|
@property
|
|
48
48
|
def title(self) -> str:
|
|
@@ -19,9 +19,7 @@ def ai_assistant_guide() -> str:
|
|
|
19
19
|
A focused guide on Basic Memory usage.
|
|
20
20
|
"""
|
|
21
21
|
logger.info("Loading AI assistant guide resource")
|
|
22
|
-
guide_doc = (
|
|
23
|
-
Path(__file__).parent.parent.parent.parent.parent / "static" / "ai_assistant_guide.md"
|
|
24
|
-
)
|
|
22
|
+
guide_doc = Path(__file__).parent.parent / "resources" / "ai_assistant_guide.md"
|
|
25
23
|
content = guide_doc.read_text(encoding="utf-8")
|
|
26
24
|
logger.info(f"Loaded AI assistant guide ({len(content)} chars)")
|
|
27
25
|
return content
|
|
@@ -14,7 +14,7 @@ from basic_memory.mcp.prompts.utils import format_prompt_context, PromptContext,
|
|
|
14
14
|
from basic_memory.mcp.server import mcp
|
|
15
15
|
from basic_memory.mcp.tools.build_context import build_context
|
|
16
16
|
from basic_memory.mcp.tools.recent_activity import recent_activity
|
|
17
|
-
from basic_memory.mcp.tools.search import
|
|
17
|
+
from basic_memory.mcp.tools.search import search_notes
|
|
18
18
|
from basic_memory.schemas.base import TimeFrame
|
|
19
19
|
from basic_memory.schemas.memory import GraphContext
|
|
20
20
|
from basic_memory.schemas.search import SearchQuery, SearchItemType
|
|
@@ -47,7 +47,7 @@ async def continue_conversation(
|
|
|
47
47
|
|
|
48
48
|
# If topic provided, search for it
|
|
49
49
|
if topic:
|
|
50
|
-
search_results = await
|
|
50
|
+
search_results = await search_notes(
|
|
51
51
|
SearchQuery(text=topic, after_date=timeframe, types=[SearchItemType.ENTITY])
|
|
52
52
|
)
|
|
53
53
|
|
|
@@ -93,7 +93,7 @@ async def continue_conversation(
|
|
|
93
93
|
## Next Steps
|
|
94
94
|
|
|
95
95
|
You can:
|
|
96
|
-
- Explore more with: `
|
|
96
|
+
- Explore more with: `search_notes({{"text": "{topic}"}})`
|
|
97
97
|
- See what's changed: `recent_activity(timeframe="{timeframe or "7d"}")`
|
|
98
98
|
- **Record new learnings or decisions from this conversation:** `write_note(title="[Create a meaningful title]", content="[Content with observations and relations]")`
|
|
99
99
|
|
|
@@ -10,7 +10,7 @@ from loguru import logger
|
|
|
10
10
|
from pydantic import Field
|
|
11
11
|
|
|
12
12
|
from basic_memory.mcp.server import mcp
|
|
13
|
-
from basic_memory.mcp.tools.search import
|
|
13
|
+
from basic_memory.mcp.tools.search import search_notes as search_tool
|
|
14
14
|
from basic_memory.schemas.base import TimeFrame
|
|
15
15
|
from basic_memory.schemas.search import SearchQuery, SearchResponse
|
|
16
16
|
|
|
@@ -144,9 +144,9 @@ def format_search_results(
|
|
|
144
144
|
## Next Steps
|
|
145
145
|
|
|
146
146
|
You can:
|
|
147
|
-
- Refine your search: `
|
|
148
|
-
- Exclude terms: `
|
|
149
|
-
- View more results: `
|
|
147
|
+
- Refine your search: `search_notes("{query} AND additional_term")`
|
|
148
|
+
- Exclude terms: `search_notes("{query} NOT exclude_term")`
|
|
149
|
+
- View more results: `search_notes("{query}", after_date=None)`
|
|
150
150
|
- Check recent activity: `recent_activity()`
|
|
151
151
|
|
|
152
152
|
## Synthesize and Capture Knowledge
|