basic-memory 0.8.0__py3-none-any.whl → 0.10.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.

Files changed (76) hide show
  1. basic_memory/__init__.py +1 -1
  2. basic_memory/alembic/migrations.py +4 -9
  3. basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +106 -0
  4. basic_memory/api/app.py +9 -6
  5. basic_memory/api/routers/__init__.py +2 -1
  6. basic_memory/api/routers/knowledge_router.py +30 -4
  7. basic_memory/api/routers/memory_router.py +3 -2
  8. basic_memory/api/routers/project_info_router.py +274 -0
  9. basic_memory/api/routers/search_router.py +22 -4
  10. basic_memory/cli/app.py +54 -3
  11. basic_memory/cli/commands/__init__.py +15 -2
  12. basic_memory/cli/commands/db.py +9 -13
  13. basic_memory/cli/commands/import_chatgpt.py +31 -36
  14. basic_memory/cli/commands/import_claude_conversations.py +32 -35
  15. basic_memory/cli/commands/import_claude_projects.py +34 -37
  16. basic_memory/cli/commands/import_memory_json.py +26 -28
  17. basic_memory/cli/commands/mcp.py +7 -1
  18. basic_memory/cli/commands/project.py +119 -0
  19. basic_memory/cli/commands/project_info.py +167 -0
  20. basic_memory/cli/commands/status.py +7 -9
  21. basic_memory/cli/commands/sync.py +54 -9
  22. basic_memory/cli/commands/{tools.py → tool.py} +92 -19
  23. basic_memory/cli/main.py +40 -1
  24. basic_memory/config.py +157 -10
  25. basic_memory/db.py +19 -4
  26. basic_memory/deps.py +10 -3
  27. basic_memory/file_utils.py +34 -18
  28. basic_memory/markdown/markdown_processor.py +1 -1
  29. basic_memory/markdown/utils.py +5 -0
  30. basic_memory/mcp/main.py +1 -2
  31. basic_memory/mcp/prompts/__init__.py +6 -2
  32. basic_memory/mcp/prompts/ai_assistant_guide.py +9 -10
  33. basic_memory/mcp/prompts/continue_conversation.py +65 -126
  34. basic_memory/mcp/prompts/recent_activity.py +55 -13
  35. basic_memory/mcp/prompts/search.py +72 -17
  36. basic_memory/mcp/prompts/utils.py +139 -82
  37. basic_memory/mcp/server.py +1 -1
  38. basic_memory/mcp/tools/__init__.py +11 -22
  39. basic_memory/mcp/tools/build_context.py +85 -0
  40. basic_memory/mcp/tools/canvas.py +17 -19
  41. basic_memory/mcp/tools/delete_note.py +28 -0
  42. basic_memory/mcp/tools/project_info.py +51 -0
  43. basic_memory/mcp/tools/{resource.py → read_content.py} +42 -5
  44. basic_memory/mcp/tools/read_note.py +190 -0
  45. basic_memory/mcp/tools/recent_activity.py +100 -0
  46. basic_memory/mcp/tools/search.py +56 -17
  47. basic_memory/mcp/tools/utils.py +245 -17
  48. basic_memory/mcp/tools/write_note.py +124 -0
  49. basic_memory/models/search.py +2 -1
  50. basic_memory/repository/entity_repository.py +3 -2
  51. basic_memory/repository/project_info_repository.py +9 -0
  52. basic_memory/repository/repository.py +23 -6
  53. basic_memory/repository/search_repository.py +33 -10
  54. basic_memory/schemas/__init__.py +12 -0
  55. basic_memory/schemas/memory.py +3 -2
  56. basic_memory/schemas/project_info.py +96 -0
  57. basic_memory/schemas/search.py +27 -32
  58. basic_memory/services/context_service.py +3 -3
  59. basic_memory/services/entity_service.py +8 -2
  60. basic_memory/services/file_service.py +107 -57
  61. basic_memory/services/link_resolver.py +5 -45
  62. basic_memory/services/search_service.py +45 -16
  63. basic_memory/sync/sync_service.py +274 -39
  64. basic_memory/sync/watch_service.py +174 -34
  65. basic_memory/utils.py +40 -40
  66. basic_memory-0.10.0.dist-info/METADATA +386 -0
  67. basic_memory-0.10.0.dist-info/RECORD +99 -0
  68. basic_memory/mcp/prompts/json_canvas_spec.py +0 -25
  69. basic_memory/mcp/tools/knowledge.py +0 -68
  70. basic_memory/mcp/tools/memory.py +0 -177
  71. basic_memory/mcp/tools/notes.py +0 -201
  72. basic_memory-0.8.0.dist-info/METADATA +0 -379
  73. basic_memory-0.8.0.dist-info/RECORD +0 -91
  74. {basic_memory-0.8.0.dist-info → basic_memory-0.10.0.dist-info}/WHEEL +0 -0
  75. {basic_memory-0.8.0.dist-info → basic_memory-0.10.0.dist-info}/entry_points.txt +0 -0
  76. {basic_memory-0.8.0.dist-info → basic_memory-0.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,18 @@
1
1
  """CLI commands for basic-memory."""
2
2
 
3
- from . import status, sync, db, import_memory_json, mcp
3
+ from . import status, sync, db, import_memory_json, mcp, import_claude_conversations
4
+ from . import import_claude_projects, import_chatgpt, tool, project, project_info
4
5
 
5
- __all__ = ["status", "sync", "db", "import_memory_json", "mcp"]
6
+ __all__ = [
7
+ "status",
8
+ "sync",
9
+ "db",
10
+ "import_memory_json",
11
+ "mcp",
12
+ "import_claude_conversations",
13
+ "import_claude_projects",
14
+ "import_chatgpt",
15
+ "tool",
16
+ "project",
17
+ "project_info",
18
+ ]
@@ -1,8 +1,5 @@
1
1
  """Database management commands."""
2
2
 
3
- import asyncio
4
-
5
- import logfire
6
3
  import typer
7
4
  from loguru import logger
8
5
 
@@ -12,17 +9,16 @@ from basic_memory.cli.app import app
12
9
 
13
10
  @app.command()
14
11
  def reset(
15
- reindex: bool = typer.Option(False, "--reindex", help="Rebuild indices from filesystem"),
12
+ reindex: bool = typer.Option(False, "--reindex", help="Rebuild db index from filesystem"),
16
13
  ): # pragma: no cover
17
14
  """Reset database (drop all tables and recreate)."""
18
- with logfire.span("reset"): # pyright: ignore [reportGeneralTypeIssues]
19
- if typer.confirm("This will delete all data in your db. Are you sure?"):
20
- logger.info("Resetting database...")
21
- asyncio.run(migrations.reset_database())
15
+ if typer.confirm("This will delete all data in your db. Are you sure?"):
16
+ logger.info("Resetting database...")
17
+ migrations.reset_database()
22
18
 
23
- if reindex:
24
- # Import and run sync
25
- from basic_memory.cli.commands.sync import sync
19
+ if reindex:
20
+ # Import and run sync
21
+ from basic_memory.cli.commands.sync import sync
26
22
 
27
- logger.info("Rebuilding search index from filesystem...")
28
- sync(watch=False) # pyright: ignore
23
+ logger.info("Rebuilding search index from filesystem...")
24
+ sync(watch=False) # pyright: ignore
@@ -6,17 +6,15 @@ from datetime import datetime
6
6
  from pathlib import Path
7
7
  from typing import Dict, Any, List, Annotated, Set, Optional
8
8
 
9
- import logfire
10
9
  import typer
11
- from loguru import logger
12
- from rich.console import Console
13
- from rich.panel import Panel
14
- from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
15
-
16
10
  from basic_memory.cli.app import import_app
17
11
  from basic_memory.config import config
18
12
  from basic_memory.markdown import EntityParser, MarkdownProcessor
19
13
  from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter
14
+ from loguru import logger
15
+ from rich.console import Console
16
+ from rich.panel import Panel
17
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
20
18
 
21
19
  console = Console()
22
20
 
@@ -168,7 +166,7 @@ async def process_chatgpt_json(
168
166
  read_task = progress.add_task("Reading chat data...", total=None)
169
167
 
170
168
  # Read conversations
171
- conversations = json.loads(json_path.read_text())
169
+ conversations = json.loads(json_path.read_text(encoding="utf-8"))
172
170
  progress.update(read_task, total=len(conversations))
173
171
 
174
172
  # Process each conversation
@@ -226,38 +224,35 @@ def import_chatgpt(
226
224
  After importing, run 'basic-memory sync' to index the new files.
227
225
  """
228
226
 
229
- with logfire.span("import chatgpt"): # pyright: ignore [reportGeneralTypeIssues]
230
- try:
231
- if conversations_json:
232
- if not conversations_json.exists():
233
- typer.echo(f"Error: File not found: {conversations_json}", err=True)
234
- raise typer.Exit(1)
227
+ try:
228
+ if conversations_json:
229
+ if not conversations_json.exists():
230
+ typer.echo(f"Error: File not found: {conversations_json}", err=True)
231
+ raise typer.Exit(1)
235
232
 
236
- # Get markdown processor
237
- markdown_processor = asyncio.run(get_markdown_processor())
233
+ # Get markdown processor
234
+ markdown_processor = asyncio.run(get_markdown_processor())
238
235
 
239
- # Process the file
240
- base_path = config.home / folder
241
- console.print(
242
- f"\nImporting chats from {conversations_json}...writing to {base_path}"
243
- )
244
- results = asyncio.run(
245
- process_chatgpt_json(conversations_json, folder, markdown_processor)
246
- )
236
+ # Process the file
237
+ base_path = config.home / folder
238
+ console.print(f"\nImporting chats from {conversations_json}...writing to {base_path}")
239
+ results = asyncio.run(
240
+ process_chatgpt_json(conversations_json, folder, markdown_processor)
241
+ )
247
242
 
248
- # Show results
249
- console.print(
250
- Panel(
251
- f"[green]Import complete![/green]\n\n"
252
- f"Imported {results['conversations']} conversations\n"
253
- f"Containing {results['messages']} messages",
254
- expand=False,
255
- )
243
+ # Show results
244
+ console.print(
245
+ Panel(
246
+ f"[green]Import complete![/green]\n\n"
247
+ f"Imported {results['conversations']} conversations\n"
248
+ f"Containing {results['messages']} messages",
249
+ expand=False,
256
250
  )
251
+ )
257
252
 
258
- console.print("\nRun 'basic-memory sync' to index the new files.")
253
+ console.print("\nRun 'basic-memory sync' to index the new files.")
259
254
 
260
- except Exception as e:
261
- logger.error("Import failed")
262
- typer.echo(f"Error during import: {e}", err=True)
263
- raise typer.Exit(1)
255
+ except Exception as e:
256
+ logger.error("Import failed")
257
+ typer.echo(f"Error during import: {e}", err=True)
258
+ raise typer.Exit(1)
@@ -6,17 +6,15 @@ from datetime import datetime
6
6
  from pathlib import Path
7
7
  from typing import Dict, Any, List, Annotated
8
8
 
9
- import logfire
10
9
  import typer
11
- from loguru import logger
12
- from rich.console import Console
13
- from rich.panel import Panel
14
- from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
15
-
16
10
  from basic_memory.cli.app import claude_app
17
11
  from basic_memory.config import config
18
12
  from basic_memory.markdown import EntityParser, MarkdownProcessor
19
13
  from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter
14
+ from loguru import logger
15
+ from rich.console import Console
16
+ from rich.panel import Panel
17
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
20
18
 
21
19
  console = Console()
22
20
 
@@ -125,7 +123,7 @@ async def process_conversations_json(
125
123
  read_task = progress.add_task("Reading chat data...", total=None)
126
124
 
127
125
  # Read chat data - handle array of arrays format
128
- data = json.loads(json_path.read_text())
126
+ data = json.loads(json_path.read_text(encoding="utf-8"))
129
127
  conversations = [chat for chat in data]
130
128
  progress.update(read_task, total=len(conversations))
131
129
 
@@ -179,35 +177,34 @@ def import_claude(
179
177
  After importing, run 'basic-memory sync' to index the new files.
180
178
  """
181
179
 
182
- with logfire.span("import claude conversations"): # pyright: ignore [reportGeneralTypeIssues]
183
- try:
184
- if not conversations_json.exists():
185
- typer.echo(f"Error: File not found: {conversations_json}", err=True)
186
- raise typer.Exit(1)
187
-
188
- # Get markdown processor
189
- markdown_processor = asyncio.run(get_markdown_processor())
190
-
191
- # Process the file
192
- base_path = config.home / folder
193
- console.print(f"\nImporting chats from {conversations_json}...writing to {base_path}")
194
- results = asyncio.run(
195
- process_conversations_json(conversations_json, base_path, markdown_processor)
196
- )
180
+ try:
181
+ if not conversations_json.exists():
182
+ typer.echo(f"Error: File not found: {conversations_json}", err=True)
183
+ raise typer.Exit(1)
197
184
 
198
- # Show results
199
- console.print(
200
- Panel(
201
- f"[green]Import complete![/green]\n\n"
202
- f"Imported {results['conversations']} conversations\n"
203
- f"Containing {results['messages']} messages",
204
- expand=False,
205
- )
185
+ # Get markdown processor
186
+ markdown_processor = asyncio.run(get_markdown_processor())
187
+
188
+ # Process the file
189
+ base_path = config.home / folder
190
+ console.print(f"\nImporting chats from {conversations_json}...writing to {base_path}")
191
+ results = asyncio.run(
192
+ process_conversations_json(conversations_json, base_path, markdown_processor)
193
+ )
194
+
195
+ # Show results
196
+ console.print(
197
+ Panel(
198
+ f"[green]Import complete![/green]\n\n"
199
+ f"Imported {results['conversations']} conversations\n"
200
+ f"Containing {results['messages']} messages",
201
+ expand=False,
206
202
  )
203
+ )
207
204
 
208
- console.print("\nRun 'basic-memory sync' to index the new files.")
205
+ console.print("\nRun 'basic-memory sync' to index the new files.")
209
206
 
210
- except Exception as e:
211
- logger.error("Import failed")
212
- typer.echo(f"Error during import: {e}", err=True)
213
- raise typer.Exit(1)
207
+ except Exception as e:
208
+ logger.error("Import failed")
209
+ typer.echo(f"Error during import: {e}", err=True)
210
+ raise typer.Exit(1)
@@ -5,17 +5,15 @@ import json
5
5
  from pathlib import Path
6
6
  from typing import Dict, Any, Annotated, Optional
7
7
 
8
- import logfire
9
8
  import typer
10
- from loguru import logger
11
- from rich.console import Console
12
- from rich.panel import Panel
13
- from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
14
-
15
9
  from basic_memory.cli.app import claude_app
16
10
  from basic_memory.config import config
17
11
  from basic_memory.markdown import EntityParser, MarkdownProcessor
18
12
  from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter
13
+ from loguru import logger
14
+ from rich.console import Console
15
+ from rich.panel import Panel
16
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
19
17
 
20
18
  console = Console()
21
19
 
@@ -104,7 +102,7 @@ async def process_projects_json(
104
102
  read_task = progress.add_task("Reading project data...", total=None)
105
103
 
106
104
  # Read project data
107
- data = json.loads(json_path.read_text())
105
+ data = json.loads(json_path.read_text(encoding="utf-8"))
108
106
  progress.update(read_task, total=len(data))
109
107
 
110
108
  # Track import counts
@@ -161,36 +159,35 @@ def import_projects(
161
159
 
162
160
  After importing, run 'basic-memory sync' to index the new files.
163
161
  """
164
- with logfire.span("import claude projects"): # pyright: ignore [reportGeneralTypeIssues]
165
- try:
166
- if projects_json:
167
- if not projects_json.exists():
168
- typer.echo(f"Error: File not found: {projects_json}", err=True)
169
- raise typer.Exit(1)
170
-
171
- # Get markdown processor
172
- markdown_processor = asyncio.run(get_markdown_processor())
173
-
174
- # Process the file
175
- base_path = config.home / base_folder if base_folder else config.home
176
- console.print(f"\nImporting projects from {projects_json}...writing to {base_path}")
177
- results = asyncio.run(
178
- process_projects_json(projects_json, base_path, markdown_processor)
179
- )
180
-
181
- # Show results
182
- console.print(
183
- Panel(
184
- f"[green]Import complete![/green]\n\n"
185
- f"Imported {results['documents']} project documents\n"
186
- f"Imported {results['prompts']} prompt templates",
187
- expand=False,
188
- )
162
+ try:
163
+ if projects_json:
164
+ if not projects_json.exists():
165
+ typer.echo(f"Error: File not found: {projects_json}", err=True)
166
+ raise typer.Exit(1)
167
+
168
+ # Get markdown processor
169
+ markdown_processor = asyncio.run(get_markdown_processor())
170
+
171
+ # Process the file
172
+ base_path = config.home / base_folder if base_folder else config.home
173
+ console.print(f"\nImporting projects from {projects_json}...writing to {base_path}")
174
+ results = asyncio.run(
175
+ process_projects_json(projects_json, base_path, markdown_processor)
176
+ )
177
+
178
+ # Show results
179
+ console.print(
180
+ Panel(
181
+ f"[green]Import complete![/green]\n\n"
182
+ f"Imported {results['documents']} project documents\n"
183
+ f"Imported {results['prompts']} prompt templates",
184
+ expand=False,
189
185
  )
186
+ )
190
187
 
191
- console.print("\nRun 'basic-memory sync' to index the new files.")
188
+ console.print("\nRun 'basic-memory sync' to index the new files.")
192
189
 
193
- except Exception as e:
194
- logger.error("Import failed")
195
- typer.echo(f"Error during import: {e}", err=True)
196
- raise typer.Exit(1)
190
+ except Exception as e:
191
+ logger.error("Import failed")
192
+ typer.echo(f"Error during import: {e}", err=True)
193
+ raise typer.Exit(1)
@@ -5,7 +5,6 @@ import json
5
5
  from pathlib import Path
6
6
  from typing import Dict, Any, List, Annotated
7
7
 
8
- import logfire
9
8
  import typer
10
9
  from loguru import logger
11
10
  from rich.console import Console
@@ -114,33 +113,32 @@ def memory_json(
114
113
  After importing, run 'basic-memory sync' to index the new files.
115
114
  """
116
115
 
117
- with logfire.span("import memory_json"): # pyright: ignore [reportGeneralTypeIssues]
118
- if not json_path.exists():
119
- typer.echo(f"Error: File not found: {json_path}", err=True)
120
- raise typer.Exit(1)
121
-
122
- try:
123
- # Get markdown processor
124
- markdown_processor = asyncio.run(get_markdown_processor())
125
-
126
- # Process the file
127
- base_path = config.home
128
- console.print(f"\nImporting from {json_path}...writing to {base_path}")
129
- results = asyncio.run(process_memory_json(json_path, base_path, markdown_processor))
130
-
131
- # Show results
132
- console.print(
133
- Panel(
134
- f"[green]Import complete![/green]\n\n"
135
- f"Created {results['entities']} entities\n"
136
- f"Added {results['relations']} relations",
137
- expand=False,
138
- )
116
+ if not json_path.exists():
117
+ typer.echo(f"Error: File not found: {json_path}", err=True)
118
+ raise typer.Exit(1)
119
+
120
+ try:
121
+ # Get markdown processor
122
+ markdown_processor = asyncio.run(get_markdown_processor())
123
+
124
+ # Process the file
125
+ base_path = config.home
126
+ console.print(f"\nImporting from {json_path}...writing to {base_path}")
127
+ results = asyncio.run(process_memory_json(json_path, base_path, markdown_processor))
128
+
129
+ # Show results
130
+ console.print(
131
+ Panel(
132
+ f"[green]Import complete![/green]\n\n"
133
+ f"Created {results['entities']} entities\n"
134
+ f"Added {results['relations']} relations",
135
+ expand=False,
139
136
  )
137
+ )
140
138
 
141
- console.print("\nRun 'basic-memory sync' to index the new files.")
139
+ console.print("\nRun 'basic-memory sync' to index the new files.")
142
140
 
143
- except Exception as e:
144
- logger.error("Import failed")
145
- typer.echo(f"Error during import: {e}", err=True)
146
- raise typer.Exit(1)
141
+ except Exception as e:
142
+ logger.error("Import failed")
143
+ typer.echo(f"Error during import: {e}", err=True)
144
+ raise typer.Exit(1)
@@ -1,6 +1,8 @@
1
1
  """MCP server command."""
2
2
 
3
3
  from loguru import logger
4
+
5
+ import basic_memory
4
6
  from basic_memory.cli.app import app
5
7
  from basic_memory.config import config
6
8
 
@@ -15,6 +17,10 @@ import basic_memory.mcp.tools # noqa: F401 # pragma: no cover
15
17
  def mcp(): # pragma: no cover
16
18
  """Run the MCP server for Claude Desktop integration."""
17
19
  home_dir = config.home
20
+ project_name = config.project
21
+
18
22
  logger.info(f"Starting Basic Memory MCP server {basic_memory.__version__}")
19
- logger.info(f"Home directory: {home_dir}")
23
+ logger.info(f"Project: {project_name}")
24
+ logger.info(f"Project directory: {home_dir}")
25
+
20
26
  mcp_server.run()
@@ -0,0 +1,119 @@
1
+ """Command module for basic-memory project management."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ import typer
7
+ from rich.console import Console
8
+ from rich.table import Table
9
+
10
+ from basic_memory.cli.app import app
11
+ from basic_memory.config import ConfigManager, config
12
+
13
+ console = Console()
14
+
15
+ # Create a project subcommand
16
+ project_app = typer.Typer(help="Manage multiple Basic Memory projects")
17
+ app.add_typer(project_app, name="project")
18
+
19
+
20
+ def format_path(path: str) -> str:
21
+ """Format a path for display, using ~ for home directory."""
22
+ home = str(Path.home())
23
+ if path.startswith(home):
24
+ return path.replace(home, "~", 1)
25
+ return path
26
+
27
+
28
+ @project_app.command("list")
29
+ def list_projects() -> None:
30
+ """List all configured projects."""
31
+ config_manager = ConfigManager()
32
+ projects = config_manager.projects
33
+
34
+ table = Table(title="Basic Memory Projects")
35
+ table.add_column("Name", style="cyan")
36
+ table.add_column("Path", style="green")
37
+ table.add_column("Default", style="yellow")
38
+ table.add_column("Active", style="magenta")
39
+
40
+ default_project = config_manager.default_project
41
+ active_project = config.project
42
+
43
+ for name, path in projects.items():
44
+ is_default = "✓" if name == default_project else ""
45
+ is_active = "✓" if name == active_project else ""
46
+ table.add_row(name, format_path(path), is_default, is_active)
47
+
48
+ console.print(table)
49
+
50
+
51
+ @project_app.command("add")
52
+ def add_project(
53
+ name: str = typer.Argument(..., help="Name of the project"),
54
+ path: str = typer.Argument(..., help="Path to the project directory"),
55
+ ) -> None:
56
+ """Add a new project."""
57
+ config_manager = ConfigManager()
58
+
59
+ try:
60
+ # Resolve to absolute path
61
+ resolved_path = os.path.abspath(os.path.expanduser(path))
62
+ config_manager.add_project(name, resolved_path)
63
+ console.print(f"[green]Project '{name}' added at {format_path(resolved_path)}[/green]")
64
+
65
+ # Display usage hint
66
+ console.print("\nTo use this project:")
67
+ console.print(f" basic-memory --project={name} <command>")
68
+ console.print(" # or")
69
+ console.print(f" basic-memory project default {name}")
70
+ except ValueError as e:
71
+ console.print(f"[red]Error: {e}[/red]")
72
+ raise typer.Exit(1)
73
+
74
+
75
+ @project_app.command("remove")
76
+ def remove_project(
77
+ name: str = typer.Argument(..., help="Name of the project to remove"),
78
+ ) -> None:
79
+ """Remove a project from configuration."""
80
+ config_manager = ConfigManager()
81
+
82
+ try:
83
+ config_manager.remove_project(name)
84
+ console.print(f"[green]Project '{name}' removed from configuration[/green]")
85
+ console.print("[yellow]Note: The project files have not been deleted from disk.[/yellow]")
86
+ except ValueError as e: # pragma: no cover
87
+ console.print(f"[red]Error: {e}[/red]")
88
+ raise typer.Exit(1)
89
+
90
+
91
+ @project_app.command("default")
92
+ def set_default_project(
93
+ name: str = typer.Argument(..., help="Name of the project to set as default"),
94
+ ) -> None:
95
+ """Set the default project."""
96
+ config_manager = ConfigManager()
97
+
98
+ try:
99
+ config_manager.set_default_project(name)
100
+ console.print(f"[green]Project '{name}' set as default[/green]")
101
+ except ValueError as e: # pragma: no cover
102
+ console.print(f"[red]Error: {e}[/red]")
103
+ raise typer.Exit(1)
104
+
105
+
106
+ @project_app.command("current")
107
+ def show_current_project() -> None:
108
+ """Show the current project."""
109
+ config_manager = ConfigManager()
110
+ current = os.environ.get("BASIC_MEMORY_PROJECT", config_manager.default_project)
111
+
112
+ try:
113
+ path = config_manager.get_project_path(current)
114
+ console.print(f"Current project: [cyan]{current}[/cyan]")
115
+ console.print(f"Path: [green]{format_path(str(path))}[/green]")
116
+ console.print(f"Database: [blue]{format_path(str(config.database_path))}[/blue]")
117
+ except ValueError: # pragma: no cover
118
+ console.print(f"[yellow]Warning: Project '{current}' not found in configuration[/yellow]")
119
+ console.print(f"Using default project: [cyan]{config_manager.default_project}[/cyan]")