basic-memory 0.14.1__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.

Files changed (52) hide show
  1. basic_memory/__init__.py +1 -1
  2. basic_memory/alembic/env.py +3 -1
  3. basic_memory/api/app.py +4 -1
  4. basic_memory/api/routers/management_router.py +3 -1
  5. basic_memory/api/routers/project_router.py +21 -13
  6. basic_memory/cli/app.py +3 -3
  7. basic_memory/cli/commands/__init__.py +1 -2
  8. basic_memory/cli/commands/db.py +5 -5
  9. basic_memory/cli/commands/import_chatgpt.py +3 -2
  10. basic_memory/cli/commands/import_claude_conversations.py +3 -1
  11. basic_memory/cli/commands/import_claude_projects.py +3 -1
  12. basic_memory/cli/commands/import_memory_json.py +5 -2
  13. basic_memory/cli/commands/mcp.py +3 -15
  14. basic_memory/cli/commands/project.py +41 -0
  15. basic_memory/cli/commands/status.py +4 -1
  16. basic_memory/cli/commands/sync.py +10 -2
  17. basic_memory/cli/main.py +0 -1
  18. basic_memory/config.py +46 -31
  19. basic_memory/db.py +2 -6
  20. basic_memory/deps.py +3 -2
  21. basic_memory/importers/chatgpt_importer.py +19 -9
  22. basic_memory/importers/memory_json_importer.py +22 -7
  23. basic_memory/mcp/async_client.py +22 -2
  24. basic_memory/mcp/project_session.py +6 -4
  25. basic_memory/mcp/prompts/__init__.py +0 -2
  26. basic_memory/mcp/server.py +8 -71
  27. basic_memory/mcp/tools/move_note.py +24 -12
  28. basic_memory/mcp/tools/project_management.py +7 -2
  29. basic_memory/mcp/tools/read_content.py +16 -0
  30. basic_memory/mcp/tools/read_note.py +12 -0
  31. basic_memory/mcp/tools/sync_status.py +3 -2
  32. basic_memory/mcp/tools/write_note.py +9 -1
  33. basic_memory/models/project.py +3 -3
  34. basic_memory/repository/project_repository.py +18 -0
  35. basic_memory/schemas/importer.py +1 -0
  36. basic_memory/services/entity_service.py +49 -3
  37. basic_memory/services/initialization.py +0 -75
  38. basic_memory/services/project_service.py +85 -28
  39. basic_memory/sync/background_sync.py +4 -3
  40. basic_memory/sync/sync_service.py +50 -1
  41. basic_memory/utils.py +107 -4
  42. {basic_memory-0.14.1.dist-info → basic_memory-0.14.3.dist-info}/METADATA +2 -2
  43. {basic_memory-0.14.1.dist-info → basic_memory-0.14.3.dist-info}/RECORD +46 -52
  44. basic_memory/cli/commands/auth.py +0 -136
  45. basic_memory/mcp/auth_provider.py +0 -270
  46. basic_memory/mcp/external_auth_provider.py +0 -321
  47. basic_memory/mcp/prompts/sync_status.py +0 -112
  48. basic_memory/mcp/supabase_auth_provider.py +0 -463
  49. basic_memory/services/migration_service.py +0 -168
  50. {basic_memory-0.14.1.dist-info → basic_memory-0.14.3.dist-info}/WHEEL +0 -0
  51. {basic_memory-0.14.1.dist-info → basic_memory-0.14.3.dist-info}/entry_points.txt +0 -0
  52. {basic_memory-0.14.1.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.1"
4
+ __version__ = "0.14.3"
5
5
 
6
6
  # API version for FastAPI - independent of package version
7
7
  __api_version__ = "v0"
@@ -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 app_config
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 app_config
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
- project_name: str = Path(..., description="Name of the project to update"),
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
- project_name: The name of the project to update
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: # pragma: no cover
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=project_name,
53
- path=project_service.projects.get(project_name, ""),
57
+ name=name,
58
+ path=project_service.projects.get(name, ""),
54
59
  )
55
60
 
56
- await project_service.update_project(project_name, updated_path=path, is_active=is_active)
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(project_name, "")
67
+ updated_path = path if path else project_service.projects.get(name, "")
60
68
 
61
69
  return ProjectStatusResponse(
62
- message=f"Project '{project_name}' updated successfully",
70
+ message=f"Project '{name}' updated successfully",
63
71
  status="success",
64
- default=(project_name == project_service.default_project),
72
+ default=(name == project_service.default_project),
65
73
  old_project=old_project_info,
66
- new_project=ProjectItem(name=project_name, path=updated_path),
74
+ new_project=ProjectItem(name=name, path=updated_path),
67
75
  )
68
- except ValueError as e: # pragma: no cover
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 auth, status, sync, db, import_memory_json, mcp, import_claude_conversations
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",
@@ -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 app_config, config_manager
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
- config_manager.config.projects = {"main": str(Path.home() / "basic-memory")}
31
- config_manager.config.default_project = "main"
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 config
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 config
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 config
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 config
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
  )
@@ -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 config, app_config
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 config
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}")
basic_memory/cli/main.py CHANGED
@@ -4,7 +4,6 @@ from basic_memory.cli.app import app # pragma: no cover
4
4
 
5
5
  # Register commands
6
6
  from basic_memory.cli.commands import ( # noqa: F401 # pragma: no cover
7
- auth,
8
7
  db,
9
8
  import_chatgpt,
10
9
  import_claude_conversations,