basic-memory 0.14.2__py3-none-any.whl → 0.14.3__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/alembic/env.py +3 -1
- basic_memory/api/app.py +4 -1
- basic_memory/api/routers/management_router.py +3 -1
- basic_memory/api/routers/project_router.py +21 -13
- basic_memory/cli/app.py +3 -3
- basic_memory/cli/commands/__init__.py +1 -2
- basic_memory/cli/commands/db.py +5 -5
- basic_memory/cli/commands/import_chatgpt.py +3 -2
- basic_memory/cli/commands/import_claude_conversations.py +3 -1
- basic_memory/cli/commands/import_claude_projects.py +3 -1
- basic_memory/cli/commands/import_memory_json.py +5 -2
- basic_memory/cli/commands/mcp.py +3 -15
- basic_memory/cli/commands/project.py +41 -0
- basic_memory/cli/commands/status.py +4 -1
- basic_memory/cli/commands/sync.py +10 -2
- basic_memory/cli/main.py +0 -1
- basic_memory/config.py +46 -31
- basic_memory/db.py +2 -6
- basic_memory/deps.py +3 -2
- basic_memory/importers/chatgpt_importer.py +19 -9
- basic_memory/importers/memory_json_importer.py +22 -7
- basic_memory/mcp/async_client.py +22 -2
- basic_memory/mcp/project_session.py +6 -4
- basic_memory/mcp/prompts/__init__.py +0 -2
- basic_memory/mcp/server.py +8 -71
- basic_memory/mcp/tools/move_note.py +24 -12
- basic_memory/mcp/tools/read_content.py +16 -0
- basic_memory/mcp/tools/read_note.py +12 -0
- basic_memory/mcp/tools/sync_status.py +3 -2
- basic_memory/mcp/tools/write_note.py +9 -1
- basic_memory/models/project.py +3 -3
- basic_memory/repository/project_repository.py +18 -0
- basic_memory/schemas/importer.py +1 -0
- basic_memory/services/entity_service.py +49 -3
- basic_memory/services/initialization.py +0 -75
- basic_memory/services/project_service.py +85 -28
- basic_memory/sync/background_sync.py +4 -3
- basic_memory/sync/sync_service.py +50 -1
- basic_memory/utils.py +105 -4
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/METADATA +2 -2
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/RECORD +45 -51
- basic_memory/cli/commands/auth.py +0 -136
- basic_memory/mcp/auth_provider.py +0 -270
- basic_memory/mcp/external_auth_provider.py +0 -321
- basic_memory/mcp/prompts/sync_status.py +0 -112
- basic_memory/mcp/supabase_auth_provider.py +0 -463
- basic_memory/services/migration_service.py +0 -168
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/WHEEL +0 -0
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/entry_points.txt +0 -0
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/licenses/LICENSE +0 -0
basic_memory/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
|
|
2
2
|
|
|
3
3
|
# Package version - updated by release automation
|
|
4
|
-
__version__ = "0.14.
|
|
4
|
+
__version__ = "0.14.3"
|
|
5
5
|
|
|
6
6
|
# API version for FastAPI - independent of package version
|
|
7
7
|
__api_version__ = "v0"
|
basic_memory/alembic/env.py
CHANGED
|
@@ -8,17 +8,19 @@ from sqlalchemy import pool
|
|
|
8
8
|
|
|
9
9
|
from alembic import context
|
|
10
10
|
|
|
11
|
+
from basic_memory.config import ConfigManager
|
|
12
|
+
|
|
11
13
|
# set config.env to "test" for pytest to prevent logging to file in utils.setup_logging()
|
|
12
14
|
os.environ["BASIC_MEMORY_ENV"] = "test"
|
|
13
15
|
|
|
14
16
|
# Import after setting environment variable # noqa: E402
|
|
15
|
-
from basic_memory.config import app_config # noqa: E402
|
|
16
17
|
from basic_memory.models import Base # noqa: E402
|
|
17
18
|
|
|
18
19
|
# this is the Alembic Config object, which provides
|
|
19
20
|
# access to the values within the .ini file in use.
|
|
20
21
|
config = context.config
|
|
21
22
|
|
|
23
|
+
app_config = ConfigManager().config
|
|
22
24
|
# Set the SQLAlchemy URL from our app config
|
|
23
25
|
sqlalchemy_url = f"sqlite:///{app_config.database_path}"
|
|
24
26
|
config.set_main_option("sqlalchemy.url", sqlalchemy_url)
|
basic_memory/api/app.py
CHANGED
|
@@ -20,15 +20,18 @@ from basic_memory.api.routers import (
|
|
|
20
20
|
search,
|
|
21
21
|
prompt_router,
|
|
22
22
|
)
|
|
23
|
-
from basic_memory.config import
|
|
23
|
+
from basic_memory.config import ConfigManager
|
|
24
24
|
from basic_memory.services.initialization import initialize_app, initialize_file_sync
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@asynccontextmanager
|
|
28
28
|
async def lifespan(app: FastAPI): # pragma: no cover
|
|
29
29
|
"""Lifecycle manager for the FastAPI app."""
|
|
30
|
+
|
|
31
|
+
app_config = ConfigManager().config
|
|
30
32
|
# Initialize app and database
|
|
31
33
|
logger.info("Starting Basic Memory API")
|
|
34
|
+
print(f"fastapi {app_config.projects}")
|
|
32
35
|
await initialize_app(app_config)
|
|
33
36
|
|
|
34
37
|
logger.info(f"Sync changes enabled: {app_config.sync_changes}")
|
|
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Request
|
|
|
6
6
|
from loguru import logger
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
|
|
9
|
-
from basic_memory.config import
|
|
9
|
+
from basic_memory.config import ConfigManager
|
|
10
10
|
from basic_memory.deps import SyncServiceDep, ProjectRepositoryDep
|
|
11
11
|
|
|
12
12
|
router = APIRouter(prefix="/management", tags=["management"])
|
|
@@ -41,6 +41,8 @@ async def start_watch_service(
|
|
|
41
41
|
# Watch service is already running
|
|
42
42
|
return WatchStatusResponse(running=True)
|
|
43
43
|
|
|
44
|
+
app_config = ConfigManager().config
|
|
45
|
+
|
|
44
46
|
# Create and start a new watch service
|
|
45
47
|
logger.info("Starting watch service via management API")
|
|
46
48
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Router for project management."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
from fastapi import APIRouter, HTTPException, Path, Body
|
|
4
5
|
from typing import Optional
|
|
5
6
|
|
|
@@ -32,40 +33,47 @@ async def get_project_info(
|
|
|
32
33
|
@project_router.patch("/{name}", response_model=ProjectStatusResponse)
|
|
33
34
|
async def update_project(
|
|
34
35
|
project_service: ProjectServiceDep,
|
|
35
|
-
|
|
36
|
-
path: Optional[str] = Body(None, description="New path for the project"),
|
|
36
|
+
name: str = Path(..., description="Name of the project to update"),
|
|
37
|
+
path: Optional[str] = Body(None, description="New absolute path for the project"),
|
|
37
38
|
is_active: Optional[bool] = Body(None, description="Status of the project (active/inactive)"),
|
|
38
39
|
) -> ProjectStatusResponse:
|
|
39
40
|
"""Update a project's information in configuration and database.
|
|
40
41
|
|
|
41
42
|
Args:
|
|
42
|
-
|
|
43
|
-
path: Optional new path for the project
|
|
43
|
+
name: The name of the project to update
|
|
44
|
+
path: Optional new absolute path for the project
|
|
44
45
|
is_active: Optional status update for the project
|
|
45
46
|
|
|
46
47
|
Returns:
|
|
47
48
|
Response confirming the project was updated
|
|
48
49
|
"""
|
|
49
|
-
try:
|
|
50
|
+
try:
|
|
51
|
+
# Validate that path is absolute if provided
|
|
52
|
+
if path and not os.path.isabs(path):
|
|
53
|
+
raise HTTPException(status_code=400, detail="Path must be absolute")
|
|
54
|
+
|
|
50
55
|
# Get original project info for the response
|
|
51
56
|
old_project_info = ProjectItem(
|
|
52
|
-
name=
|
|
53
|
-
path=project_service.projects.get(
|
|
57
|
+
name=name,
|
|
58
|
+
path=project_service.projects.get(name, ""),
|
|
54
59
|
)
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
if path:
|
|
62
|
+
await project_service.move_project(name, path)
|
|
63
|
+
elif is_active is not None:
|
|
64
|
+
await project_service.update_project(name, is_active=is_active)
|
|
57
65
|
|
|
58
66
|
# Get updated project info
|
|
59
|
-
updated_path = path if path else project_service.projects.get(
|
|
67
|
+
updated_path = path if path else project_service.projects.get(name, "")
|
|
60
68
|
|
|
61
69
|
return ProjectStatusResponse(
|
|
62
|
-
message=f"Project '{
|
|
70
|
+
message=f"Project '{name}' updated successfully",
|
|
63
71
|
status="success",
|
|
64
|
-
default=(
|
|
72
|
+
default=(name == project_service.default_project),
|
|
65
73
|
old_project=old_project_info,
|
|
66
|
-
new_project=ProjectItem(name=
|
|
74
|
+
new_project=ProjectItem(name=name, path=updated_path),
|
|
67
75
|
)
|
|
68
|
-
except ValueError as e:
|
|
76
|
+
except ValueError as e:
|
|
69
77
|
raise HTTPException(status_code=400, detail=str(e))
|
|
70
78
|
|
|
71
79
|
|
basic_memory/cli/app.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
|
-
from basic_memory.config import get_project_config
|
|
5
|
+
from basic_memory.config import get_project_config, ConfigManager
|
|
6
6
|
from basic_memory.mcp.project_session import session
|
|
7
7
|
|
|
8
8
|
|
|
@@ -10,8 +10,8 @@ def version_callback(value: bool) -> None:
|
|
|
10
10
|
"""Show version and exit."""
|
|
11
11
|
if value: # pragma: no cover
|
|
12
12
|
import basic_memory
|
|
13
|
-
from basic_memory.config import config
|
|
14
13
|
|
|
14
|
+
config = get_project_config()
|
|
15
15
|
typer.echo(f"Basic Memory version: {basic_memory.__version__}")
|
|
16
16
|
typer.echo(f"Current project: {config.project}")
|
|
17
17
|
typer.echo(f"Project path: {config.home}")
|
|
@@ -44,9 +44,9 @@ def app_callback(
|
|
|
44
44
|
|
|
45
45
|
# Run initialization for every command unless --version was specified
|
|
46
46
|
if not version and ctx.invoked_subcommand is not None:
|
|
47
|
-
from basic_memory.config import app_config
|
|
48
47
|
from basic_memory.services.initialization import ensure_initialization
|
|
49
48
|
|
|
49
|
+
app_config = ConfigManager().config
|
|
50
50
|
ensure_initialization(app_config)
|
|
51
51
|
|
|
52
52
|
# Initialize MCP session with the specified project or default
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"""CLI commands for basic-memory."""
|
|
2
2
|
|
|
3
|
-
from . import
|
|
3
|
+
from . import status, sync, db, import_memory_json, mcp, import_claude_conversations
|
|
4
4
|
from . import import_claude_projects, import_chatgpt, tool, project
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
|
-
"auth",
|
|
8
7
|
"status",
|
|
9
8
|
"sync",
|
|
10
9
|
"db",
|
basic_memory/cli/commands/db.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"""Database management commands."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
from pathlib import Path
|
|
5
4
|
|
|
6
5
|
import typer
|
|
7
6
|
from loguru import logger
|
|
8
7
|
|
|
9
8
|
from basic_memory import db
|
|
10
9
|
from basic_memory.cli.app import app
|
|
11
|
-
from basic_memory.config import
|
|
10
|
+
from basic_memory.config import ConfigManager, BasicMemoryConfig, save_basic_memory_config
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
@app.command()
|
|
@@ -18,6 +17,8 @@ def reset(
|
|
|
18
17
|
"""Reset database (drop all tables and recreate)."""
|
|
19
18
|
if typer.confirm("This will delete all data in your db. Are you sure?"):
|
|
20
19
|
logger.info("Resetting database...")
|
|
20
|
+
config_manager = ConfigManager()
|
|
21
|
+
app_config = config_manager.config
|
|
21
22
|
# Get database path
|
|
22
23
|
db_path = app_config.app_database_path
|
|
23
24
|
|
|
@@ -27,9 +28,8 @@ def reset(
|
|
|
27
28
|
logger.info(f"Database file deleted: {db_path}")
|
|
28
29
|
|
|
29
30
|
# Reset project configuration
|
|
30
|
-
|
|
31
|
-
config_manager.config
|
|
32
|
-
config_manager.save_config(config_manager.config)
|
|
31
|
+
config = BasicMemoryConfig()
|
|
32
|
+
save_basic_memory_config(config_manager.config_file, config)
|
|
33
33
|
logger.info("Project configuration reset to default")
|
|
34
34
|
|
|
35
35
|
# Create a new empty database
|
|
@@ -7,7 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
from basic_memory.cli.app import import_app
|
|
10
|
-
from basic_memory.config import
|
|
10
|
+
from basic_memory.config import get_project_config
|
|
11
11
|
from basic_memory.importers import ChatGPTImporter
|
|
12
12
|
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
13
13
|
from loguru import logger
|
|
@@ -19,6 +19,7 @@ console = Console()
|
|
|
19
19
|
|
|
20
20
|
async def get_markdown_processor() -> MarkdownProcessor:
|
|
21
21
|
"""Get MarkdownProcessor instance."""
|
|
22
|
+
config = get_project_config()
|
|
22
23
|
entity_parser = EntityParser(config.home)
|
|
23
24
|
return MarkdownProcessor(entity_parser)
|
|
24
25
|
|
|
@@ -49,7 +50,7 @@ def import_chatgpt(
|
|
|
49
50
|
|
|
50
51
|
# Get markdown processor
|
|
51
52
|
markdown_processor = asyncio.run(get_markdown_processor())
|
|
52
|
-
|
|
53
|
+
config = get_project_config()
|
|
53
54
|
# Process the file
|
|
54
55
|
base_path = config.home / folder
|
|
55
56
|
console.print(f"\nImporting chats from {conversations_json}...writing to {base_path}")
|
|
@@ -7,7 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
from basic_memory.cli.app import claude_app
|
|
10
|
-
from basic_memory.config import
|
|
10
|
+
from basic_memory.config import get_project_config
|
|
11
11
|
from basic_memory.importers.claude_conversations_importer import ClaudeConversationsImporter
|
|
12
12
|
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
13
13
|
from loguru import logger
|
|
@@ -19,6 +19,7 @@ console = Console()
|
|
|
19
19
|
|
|
20
20
|
async def get_markdown_processor() -> MarkdownProcessor:
|
|
21
21
|
"""Get MarkdownProcessor instance."""
|
|
22
|
+
config = get_project_config()
|
|
22
23
|
entity_parser = EntityParser(config.home)
|
|
23
24
|
return MarkdownProcessor(entity_parser)
|
|
24
25
|
|
|
@@ -42,6 +43,7 @@ def import_claude(
|
|
|
42
43
|
After importing, run 'basic-memory sync' to index the new files.
|
|
43
44
|
"""
|
|
44
45
|
|
|
46
|
+
config = get_project_config()
|
|
45
47
|
try:
|
|
46
48
|
if not conversations_json.exists():
|
|
47
49
|
typer.echo(f"Error: File not found: {conversations_json}", err=True)
|
|
@@ -7,7 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
from basic_memory.cli.app import claude_app
|
|
10
|
-
from basic_memory.config import
|
|
10
|
+
from basic_memory.config import get_project_config
|
|
11
11
|
from basic_memory.importers.claude_projects_importer import ClaudeProjectsImporter
|
|
12
12
|
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
13
13
|
from loguru import logger
|
|
@@ -19,6 +19,7 @@ console = Console()
|
|
|
19
19
|
|
|
20
20
|
async def get_markdown_processor() -> MarkdownProcessor:
|
|
21
21
|
"""Get MarkdownProcessor instance."""
|
|
22
|
+
config = get_project_config()
|
|
22
23
|
entity_parser = EntityParser(config.home)
|
|
23
24
|
return MarkdownProcessor(entity_parser)
|
|
24
25
|
|
|
@@ -41,6 +42,7 @@ def import_projects(
|
|
|
41
42
|
|
|
42
43
|
After importing, run 'basic-memory sync' to index the new files.
|
|
43
44
|
"""
|
|
45
|
+
config = get_project_config()
|
|
44
46
|
try:
|
|
45
47
|
if not projects_json.exists():
|
|
46
48
|
typer.echo(f"Error: File not found: {projects_json}", err=True)
|
|
@@ -7,7 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
from basic_memory.cli.app import import_app
|
|
10
|
-
from basic_memory.config import
|
|
10
|
+
from basic_memory.config import get_project_config
|
|
11
11
|
from basic_memory.importers.memory_json_importer import MemoryJsonImporter
|
|
12
12
|
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
13
13
|
from loguru import logger
|
|
@@ -19,6 +19,7 @@ console = Console()
|
|
|
19
19
|
|
|
20
20
|
async def get_markdown_processor() -> MarkdownProcessor:
|
|
21
21
|
"""Get MarkdownProcessor instance."""
|
|
22
|
+
config = get_project_config()
|
|
22
23
|
entity_parser = EntityParser(config.home)
|
|
23
24
|
return MarkdownProcessor(entity_parser)
|
|
24
25
|
|
|
@@ -46,6 +47,7 @@ def memory_json(
|
|
|
46
47
|
typer.echo(f"Error: File not found: {json_path}", err=True)
|
|
47
48
|
raise typer.Exit(1)
|
|
48
49
|
|
|
50
|
+
config = get_project_config()
|
|
49
51
|
try:
|
|
50
52
|
# Get markdown processor
|
|
51
53
|
markdown_processor = asyncio.run(get_markdown_processor())
|
|
@@ -74,7 +76,8 @@ def memory_json(
|
|
|
74
76
|
Panel(
|
|
75
77
|
f"[green]Import complete![/green]\n\n"
|
|
76
78
|
f"Created {result.entities} entities\n"
|
|
77
|
-
f"Added {result.relations} relations"
|
|
79
|
+
f"Added {result.relations} relations\n"
|
|
80
|
+
f"Skipped {result.skipped_entities} entities\n",
|
|
78
81
|
expand=False,
|
|
79
82
|
)
|
|
80
83
|
)
|
basic_memory/cli/commands/mcp.py
CHANGED
|
@@ -4,6 +4,7 @@ import asyncio
|
|
|
4
4
|
import typer
|
|
5
5
|
|
|
6
6
|
from basic_memory.cli.app import app
|
|
7
|
+
from basic_memory.config import ConfigManager
|
|
7
8
|
|
|
8
9
|
# Import mcp instance
|
|
9
10
|
from basic_memory.mcp.server import mcp as mcp_server # pragma: no cover
|
|
@@ -34,26 +35,13 @@ def mcp(
|
|
|
34
35
|
- sse: Server-Sent Events (for compatibility with existing clients)
|
|
35
36
|
"""
|
|
36
37
|
|
|
37
|
-
# Check if OAuth is enabled
|
|
38
|
-
import os
|
|
39
|
-
|
|
40
|
-
auth_enabled = os.getenv("FASTMCP_AUTH_ENABLED", "false").lower() == "true"
|
|
41
|
-
if auth_enabled:
|
|
42
|
-
logger.info("OAuth authentication is ENABLED")
|
|
43
|
-
logger.info(f"Issuer URL: {os.getenv('FASTMCP_AUTH_ISSUER_URL', 'http://localhost:8000')}")
|
|
44
|
-
if os.getenv("FASTMCP_AUTH_REQUIRED_SCOPES"):
|
|
45
|
-
logger.info(f"Required scopes: {os.getenv('FASTMCP_AUTH_REQUIRED_SCOPES')}")
|
|
46
|
-
else:
|
|
47
|
-
logger.info("OAuth authentication is DISABLED")
|
|
48
|
-
|
|
49
|
-
from basic_memory.config import app_config
|
|
50
38
|
from basic_memory.services.initialization import initialize_file_sync
|
|
51
39
|
|
|
52
|
-
# Start the MCP server with the specified transport
|
|
53
|
-
|
|
54
40
|
# Use unified thread-based sync approach for both transports
|
|
55
41
|
import threading
|
|
56
42
|
|
|
43
|
+
app_config = ConfigManager().config
|
|
44
|
+
|
|
57
45
|
def run_file_sync():
|
|
58
46
|
"""Run file sync in a separate thread with its own event loop."""
|
|
59
47
|
loop = asyncio.new_event_loop()
|
|
@@ -23,6 +23,7 @@ from basic_memory.mcp.tools.utils import call_post
|
|
|
23
23
|
from basic_memory.schemas.project_info import ProjectStatusResponse
|
|
24
24
|
from basic_memory.mcp.tools.utils import call_delete
|
|
25
25
|
from basic_memory.mcp.tools.utils import call_put
|
|
26
|
+
from basic_memory.mcp.tools.utils import call_patch
|
|
26
27
|
from basic_memory.utils import generate_permalink
|
|
27
28
|
|
|
28
29
|
console = Console()
|
|
@@ -148,6 +149,46 @@ def synchronize_projects() -> None:
|
|
|
148
149
|
raise typer.Exit(1)
|
|
149
150
|
|
|
150
151
|
|
|
152
|
+
@project_app.command("move")
|
|
153
|
+
def move_project(
|
|
154
|
+
name: str = typer.Argument(..., help="Name of the project to move"),
|
|
155
|
+
new_path: str = typer.Argument(..., help="New absolute path for the project"),
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Move a project to a new location."""
|
|
158
|
+
# Resolve to absolute path
|
|
159
|
+
resolved_path = os.path.abspath(os.path.expanduser(new_path))
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
data = {"path": resolved_path}
|
|
163
|
+
project_name = generate_permalink(name)
|
|
164
|
+
|
|
165
|
+
current_project = session.get_current_project()
|
|
166
|
+
response = asyncio.run(
|
|
167
|
+
call_patch(client, f"/{current_project}/project/{project_name}", json=data)
|
|
168
|
+
)
|
|
169
|
+
result = ProjectStatusResponse.model_validate(response.json())
|
|
170
|
+
|
|
171
|
+
console.print(f"[green]{result.message}[/green]")
|
|
172
|
+
|
|
173
|
+
# Show important file movement reminder
|
|
174
|
+
console.print() # Empty line for spacing
|
|
175
|
+
console.print(
|
|
176
|
+
Panel(
|
|
177
|
+
"[bold red]IMPORTANT:[/bold red] Project configuration updated successfully.\n\n"
|
|
178
|
+
"[yellow]You must manually move your project files from the old location to:[/yellow]\n"
|
|
179
|
+
f"[cyan]{resolved_path}[/cyan]\n\n"
|
|
180
|
+
"[dim]Basic Memory has only updated the configuration - your files remain in their original location.[/dim]",
|
|
181
|
+
title="⚠️ Manual File Movement Required",
|
|
182
|
+
border_style="yellow",
|
|
183
|
+
expand=False,
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
console.print(f"[red]Error moving project: {str(e)}[/red]")
|
|
189
|
+
raise typer.Exit(1)
|
|
190
|
+
|
|
191
|
+
|
|
151
192
|
@project_app.command("info")
|
|
152
193
|
def display_project_info(
|
|
153
194
|
json_output: bool = typer.Option(False, "--json", help="Output in JSON format"),
|
|
@@ -12,7 +12,7 @@ from rich.tree import Tree
|
|
|
12
12
|
from basic_memory import db
|
|
13
13
|
from basic_memory.cli.app import app
|
|
14
14
|
from basic_memory.cli.commands.sync import get_sync_service
|
|
15
|
-
from basic_memory.config import
|
|
15
|
+
from basic_memory.config import ConfigManager, get_project_config
|
|
16
16
|
from basic_memory.repository import ProjectRepository
|
|
17
17
|
from basic_memory.sync.sync_service import SyncReport
|
|
18
18
|
|
|
@@ -126,6 +126,9 @@ async def run_status(verbose: bool = False): # pragma: no cover
|
|
|
126
126
|
"""Check sync status of files vs database."""
|
|
127
127
|
# Check knowledge/ directory
|
|
128
128
|
|
|
129
|
+
app_config = ConfigManager().config
|
|
130
|
+
config = get_project_config()
|
|
131
|
+
|
|
129
132
|
_, session_maker = await db.get_or_create_db(
|
|
130
133
|
db_path=app_config.database_path, db_type=db.DatabaseType.FILESYSTEM
|
|
131
134
|
)
|
|
@@ -13,7 +13,7 @@ from rich.tree import Tree
|
|
|
13
13
|
|
|
14
14
|
from basic_memory import db
|
|
15
15
|
from basic_memory.cli.app import app
|
|
16
|
-
from basic_memory.config import
|
|
16
|
+
from basic_memory.config import ConfigManager, get_project_config
|
|
17
17
|
from basic_memory.markdown import EntityParser
|
|
18
18
|
from basic_memory.markdown.markdown_processor import MarkdownProcessor
|
|
19
19
|
from basic_memory.models import Project
|
|
@@ -29,7 +29,6 @@ from basic_memory.services.link_resolver import LinkResolver
|
|
|
29
29
|
from basic_memory.services.search_service import SearchService
|
|
30
30
|
from basic_memory.sync import SyncService
|
|
31
31
|
from basic_memory.sync.sync_service import SyncReport
|
|
32
|
-
from basic_memory.config import app_config
|
|
33
32
|
|
|
34
33
|
console = Console()
|
|
35
34
|
|
|
@@ -42,6 +41,8 @@ class ValidationIssue:
|
|
|
42
41
|
|
|
43
42
|
async def get_sync_service(project: Project) -> SyncService: # pragma: no cover
|
|
44
43
|
"""Get sync service instance with all dependencies."""
|
|
44
|
+
|
|
45
|
+
app_config = ConfigManager().config
|
|
45
46
|
_, session_maker = await db.get_or_create_db(
|
|
46
47
|
db_path=app_config.database_path, db_type=db.DatabaseType.FILESYSTEM
|
|
47
48
|
)
|
|
@@ -96,6 +97,7 @@ def group_issues_by_directory(issues: List[ValidationIssue]) -> Dict[str, List[V
|
|
|
96
97
|
|
|
97
98
|
def display_sync_summary(knowledge: SyncReport):
|
|
98
99
|
"""Display a one-line summary of sync changes."""
|
|
100
|
+
config = get_project_config()
|
|
99
101
|
total_changes = knowledge.total
|
|
100
102
|
project_name = config.project
|
|
101
103
|
|
|
@@ -124,6 +126,7 @@ def display_sync_summary(knowledge: SyncReport):
|
|
|
124
126
|
|
|
125
127
|
def display_detailed_sync_results(knowledge: SyncReport):
|
|
126
128
|
"""Display detailed sync results with trees."""
|
|
129
|
+
config = get_project_config()
|
|
127
130
|
project_name = config.project
|
|
128
131
|
|
|
129
132
|
if knowledge.total == 0:
|
|
@@ -158,6 +161,9 @@ def display_detailed_sync_results(knowledge: SyncReport):
|
|
|
158
161
|
|
|
159
162
|
async def run_sync(verbose: bool = False):
|
|
160
163
|
"""Run sync operation."""
|
|
164
|
+
app_config = ConfigManager().config
|
|
165
|
+
config = get_project_config()
|
|
166
|
+
|
|
161
167
|
_, session_maker = await db.get_or_create_db(
|
|
162
168
|
db_path=app_config.database_path, db_type=db.DatabaseType.FILESYSTEM
|
|
163
169
|
)
|
|
@@ -212,6 +218,8 @@ def sync(
|
|
|
212
218
|
),
|
|
213
219
|
) -> None:
|
|
214
220
|
"""Sync knowledge files with the database."""
|
|
221
|
+
config = get_project_config()
|
|
222
|
+
|
|
215
223
|
try:
|
|
216
224
|
# Show which project we're syncing
|
|
217
225
|
typer.echo(f"Syncing project: {config.project}")
|