mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
158 lines
5.2 KiB
Python
158 lines
5.2 KiB
Python
"""Database management commands."""
|
|
|
|
import asyncio
|
|
from typing import Optional, List, Annotated
|
|
|
|
import typer
|
|
from rich import print as rprint
|
|
|
|
from basic_memory.cli.app import app
|
|
from basic_memory.mcp.tools import build_context as mcp_build_context
|
|
from basic_memory.mcp.tools import get_entity as mcp_get_entity
|
|
from basic_memory.mcp.tools import read_note as mcp_read_note
|
|
from basic_memory.mcp.tools import recent_activity as mcp_recent_activity
|
|
from basic_memory.mcp.tools import search as mcp_search
|
|
from basic_memory.mcp.tools import write_note as mcp_write_note
|
|
from basic_memory.schemas.base import TimeFrame
|
|
from basic_memory.schemas.memory import MemoryUrl
|
|
from basic_memory.schemas.search import SearchQuery
|
|
|
|
tool_app = typer.Typer()
|
|
app.add_typer(tool_app, name="tools", help="cli versions mcp tools")
|
|
|
|
|
|
@tool_app.command()
|
|
def write_note(
|
|
title: Annotated[str, typer.Option(help="The title of the note")],
|
|
content: Annotated[str, typer.Option(help="The content of the note")],
|
|
folder: Annotated[str, typer.Option(help="The folder to create the note in")],
|
|
tags: Annotated[
|
|
Optional[List[str]], typer.Option(help="A list of tags to apply to the note")
|
|
] = None,
|
|
):
|
|
try:
|
|
note = asyncio.run(mcp_write_note(title, content, folder, tags))
|
|
rprint(note)
|
|
except Exception as e: # pragma: no cover
|
|
if not isinstance(e, typer.Exit):
|
|
typer.echo(f"Error during write_note: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
raise
|
|
|
|
|
|
@tool_app.command()
|
|
def read_note(identifier: str, page: int = 1, page_size: int = 10):
|
|
try:
|
|
note = asyncio.run(mcp_read_note(identifier, page, page_size))
|
|
rprint(note)
|
|
except Exception as e: # pragma: no cover
|
|
if not isinstance(e, typer.Exit):
|
|
typer.echo(f"Error during read_note: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
raise
|
|
|
|
|
|
@tool_app.command()
|
|
def build_context(
|
|
url: MemoryUrl,
|
|
depth: Optional[int] = 1,
|
|
timeframe: Optional[TimeFrame] = "7d",
|
|
page: int = 1,
|
|
page_size: int = 10,
|
|
max_related: int = 10,
|
|
):
|
|
try:
|
|
context = asyncio.run(
|
|
mcp_build_context(
|
|
url=url,
|
|
depth=depth,
|
|
timeframe=timeframe,
|
|
page=page,
|
|
page_size=page_size,
|
|
max_related=max_related,
|
|
)
|
|
)
|
|
rprint(context.model_dump_json(indent=2))
|
|
except Exception as e: # pragma: no cover
|
|
if not isinstance(e, typer.Exit):
|
|
typer.echo(f"Error during build_context: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
raise
|
|
|
|
|
|
@tool_app.command()
|
|
def recent_activity(
|
|
type: Annotated[Optional[List[str]], typer.Option()] = ["entity", "observation", "relation"],
|
|
depth: Optional[int] = 1,
|
|
timeframe: Optional[TimeFrame] = "7d",
|
|
page: int = 1,
|
|
page_size: int = 10,
|
|
max_related: int = 10,
|
|
):
|
|
assert type is not None, "type is required"
|
|
if any(t not in ["entity", "observation", "relation"] for t in type): # pragma: no cover
|
|
print("type must be one of ['entity', 'observation', 'relation']")
|
|
raise typer.Abort()
|
|
|
|
try:
|
|
context = asyncio.run(
|
|
mcp_recent_activity(
|
|
type=type, # pyright: ignore [reportArgumentType]
|
|
depth=depth,
|
|
timeframe=timeframe,
|
|
page=page,
|
|
page_size=page_size,
|
|
max_related=max_related,
|
|
)
|
|
)
|
|
rprint(context.model_dump_json(indent=2))
|
|
except Exception as e: # pragma: no cover
|
|
if not isinstance(e, typer.Exit):
|
|
typer.echo(f"Error during build_context: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
raise
|
|
|
|
|
|
@tool_app.command()
|
|
def search(
|
|
query: str,
|
|
permalink: Annotated[bool, typer.Option("--permalink", help="Search permalink values")] = False,
|
|
title: Annotated[bool, typer.Option("--title", help="Search title values")] = False,
|
|
after_date: Annotated[
|
|
Optional[str],
|
|
typer.Option("--after_date", help="Search results after date, eg. '2d', '1 week'"),
|
|
] = None,
|
|
page: int = 1,
|
|
page_size: int = 10,
|
|
):
|
|
if permalink and title: # pragma: no cover
|
|
print("Cannot search both permalink and title")
|
|
raise typer.Abort()
|
|
|
|
try:
|
|
search_query = SearchQuery(
|
|
permalink_match=query if permalink else None,
|
|
text=query if query else None,
|
|
title=query if title else None,
|
|
after_date=after_date,
|
|
)
|
|
results = asyncio.run(mcp_search(query=search_query, page=page, page_size=page_size))
|
|
rprint(results.model_dump_json(indent=2))
|
|
except Exception as e: # pragma: no cover
|
|
if not isinstance(e, typer.Exit):
|
|
typer.echo(f"Error during search: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
raise
|
|
|
|
|
|
@tool_app.command()
|
|
def get_entity(identifier: str):
|
|
try:
|
|
entity = asyncio.run(mcp_get_entity(identifier=identifier))
|
|
rprint(entity.model_dump_json(indent=2))
|
|
except Exception as e: # pragma: no cover
|
|
if not isinstance(e, typer.Exit):
|
|
typer.echo(f"Error during get_entity: {e}", err=True)
|
|
raise typer.Exit(1)
|
|
raise
|