99from rich .table import Table
1010
1111from basic_memory .cli .app import app
12- from basic_memory .cli .commands .cloud import get_authenticated_headers
1312from basic_memory .cli .commands .command_utils import get_project_info
1413from basic_memory .config import ConfigManager
1514import json
1615from datetime import datetime
1716
1817from rich .panel import Panel
19- from basic_memory .mcp .async_client import client
18+ from basic_memory .mcp .async_client import get_client
2019from basic_memory .mcp .tools .utils import call_get
2120from basic_memory .schemas .project_info import ProjectList
2221from basic_memory .mcp .tools .utils import call_post
@@ -46,14 +45,14 @@ def format_path(path: str) -> str:
4645@project_app .command ("list" )
4746def list_projects () -> None :
4847 """List all Basic Memory projects."""
49- # Use API to list projects
50- try :
51- auth_headers = {}
52- if config .cloud_mode_enabled :
53- auth_headers = asyncio .run (get_authenticated_headers ())
5448
55- response = asyncio .run (call_get (client , "/projects/projects" , headers = auth_headers ))
56- result = ProjectList .model_validate (response .json ())
49+ async def _list_projects ():
50+ async with get_client () as client :
51+ response = await call_get (client , "/projects/projects" )
52+ return ProjectList .model_validate (response .json ())
53+
54+ try :
55+ result = asyncio .run (_list_projects ())
5756
5857 table = Table (title = "Basic Memory Projects" )
5958 table .add_column ("Name" , style = "cyan" )
@@ -79,16 +78,14 @@ def add_project_cloud(
7978 ) -> None :
8079 """Add a new project to Basic Memory Cloud"""
8180
82- try :
83- auth_headers = asyncio .run (get_authenticated_headers ())
84-
85- data = {"name" : name , "path" : generate_permalink (name ), "set_default" : set_default }
86-
87- response = asyncio .run (
88- call_post (client , "/projects/projects" , json = data , headers = auth_headers )
89- )
90- result = ProjectStatusResponse .model_validate (response .json ())
81+ async def _add_project ():
82+ async with get_client () as client :
83+ data = {"name" : name , "path" : generate_permalink (name ), "set_default" : set_default }
84+ response = await call_post (client , "/projects/projects" , json = data )
85+ return ProjectStatusResponse .model_validate (response .json ())
9186
87+ try :
88+ result = asyncio .run (_add_project ())
9289 console .print (f"[green]{ result .message } [/green]" )
9390 except Exception as e :
9491 console .print (f"[red]Error adding project: { str (e )} [/red]" )
@@ -109,12 +106,14 @@ def add_project(
109106 # Resolve to absolute path
110107 resolved_path = Path (os .path .abspath (os .path .expanduser (path ))).as_posix ()
111108
112- try :
113- data = { "name" : name , "path" : resolved_path , "set_default" : set_default }
114-
115- response = asyncio . run ( call_post (client , "/projects/projects" , json = data ) )
116- result = ProjectStatusResponse .model_validate (response .json ())
109+ async def _add_project () :
110+ async with get_client () as client :
111+ data = { "name" : name , "path" : resolved_path , "set_default" : set_default }
112+ response = await call_post (client , "/projects/projects" , json = data )
113+ return ProjectStatusResponse .model_validate (response .json ())
117114
115+ try :
116+ result = asyncio .run (_add_project ())
118117 console .print (f"[green]{ result .message } [/green]" )
119118 except Exception as e :
120119 console .print (f"[red]Error adding project: { str (e )} [/red]" )
@@ -130,17 +129,15 @@ def remove_project(
130129 name : str = typer .Argument (..., help = "Name of the project to remove" ),
131130) -> None :
132131 """Remove a project."""
133- try :
134- auth_headers = {}
135- if config .cloud_mode_enabled :
136- auth_headers = asyncio .run (get_authenticated_headers ())
137132
138- project_permalink = generate_permalink ( name )
139- response = asyncio . run (
140- call_delete ( client , f"/projects/ { project_permalink } " , headers = auth_headers )
141- )
142- result = ProjectStatusResponse .model_validate (response .json ())
133+ async def _remove_project ():
134+ async with get_client () as client :
135+ project_permalink = generate_permalink ( name )
136+ response = await call_delete ( client , f"/projects/ { project_permalink } " )
137+ return ProjectStatusResponse .model_validate (response .json ())
143138
139+ try :
140+ result = asyncio .run (_remove_project ())
144141 console .print (f"[green]{ result .message } [/green]" )
145142 except Exception as e :
146143 console .print (f"[red]Error removing project: { str (e )} [/red]" )
@@ -157,11 +154,15 @@ def set_default_project(
157154 name : str = typer .Argument (..., help = "Name of the project to set as CLI default" ),
158155 ) -> None :
159156 """Set the default project when 'config.default_project_mode' is set."""
160- try :
161- project_permalink = generate_permalink (name )
162- response = asyncio .run (call_put (client , f"/projects/{ project_permalink } /default" ))
163- result = ProjectStatusResponse .model_validate (response .json ())
164157
158+ async def _set_default ():
159+ async with get_client () as client :
160+ project_permalink = generate_permalink (name )
161+ response = await call_put (client , f"/projects/{ project_permalink } /default" )
162+ return ProjectStatusResponse .model_validate (response .json ())
163+
164+ try :
165+ result = asyncio .run (_set_default ())
165166 console .print (f"[green]{ result .message } [/green]" )
166167 except Exception as e :
167168 console .print (f"[red]Error setting default project: { str (e )} [/red]" )
@@ -170,12 +171,14 @@ def set_default_project(
170171 @project_app .command ("sync-config" )
171172 def synchronize_projects () -> None :
172173 """Synchronize project config between configuration file and database."""
173- # Call the API to synchronize projects
174174
175- try :
176- response = asyncio .run (call_post (client , "/projects/config/sync" ))
177- result = ProjectStatusResponse .model_validate (response .json ())
175+ async def _sync_config ():
176+ async with get_client () as client :
177+ response = await call_post (client , "/projects/config/sync" )
178+ return ProjectStatusResponse .model_validate (response .json ())
178179
180+ try :
181+ result = asyncio .run (_sync_config ())
179182 console .print (f"[green]{ result .message } [/green]" )
180183 except Exception as e : # pragma: no cover
181184 console .print (f"[red]Error synchronizing projects: { str (e )} [/red]" )
@@ -190,17 +193,19 @@ def move_project(
190193 # Resolve to absolute path
191194 resolved_path = Path (os .path .abspath (os .path .expanduser (new_path ))).as_posix ()
192195
193- try :
194- data = { "path" : resolved_path }
195-
196- project_permalink = generate_permalink (name )
196+ async def _move_project () :
197+ async with get_client () as client :
198+ data = { "path" : resolved_path }
199+ project_permalink = generate_permalink (name )
197200
198- # TODO fix route to use ProjectPathDep
199- response = asyncio . run (
200- call_patch ( client , f"/{ name } /project/{ project_permalink } " , json = data )
201- )
202- result = ProjectStatusResponse .model_validate (response .json ())
201+ # TODO fix route to use ProjectPathDep
202+ response = await call_patch (
203+ client , f"/{ name } /project/{ project_permalink } " , json = data
204+ )
205+ return ProjectStatusResponse .model_validate (response .json ())
203206
207+ try :
208+ result = asyncio .run (_move_project ())
204209 console .print (f"[green]{ result .message } [/green]" )
205210
206211 # Show important file movement reminder
0 commit comments