@@ -12,6 +12,7 @@ use crate::ui::App;
1212use anyhow:: Result ;
1313use context:: Context as ContextType ;
1414use mcp:: run_server;
15+ use tokio:: signal;
1516use tracing:: { error, info} ;
1617use tracing_subscriber:: {
1718 EnvFilter , Layer , fmt:: format:: PrettyFields , layer:: SubscriberExt , util:: SubscriberInitExt ,
@@ -43,42 +44,71 @@ async fn main() -> Result<()> {
4344
4445 // Run the MCP Server
4546 let cloned_context = context. clone ( ) ;
46- tokio:: spawn ( async move {
47+ let server_handle = tokio:: spawn ( async move {
4748 run_server ( cloned_context) . await . unwrap ( ) ;
4849 } ) ;
4950
50- if no_ui {
51- info ! (
52- "Running in CLI mode on port {}:{}" ,
53- context. address_information( ) . 0 ,
54- context. address_information( ) . 1
55- ) ;
56- info ! ( "Configuration file: {}" , context. configuration_file( ) ) ;
57- if context. project_descriptions ( ) . await . is_empty ( ) {
58- error ! ( "No projects found, please run without `--no-ui` or edit configuration file" ) ;
59- return Ok ( ( ) ) ;
51+ let main_loop_fut = async {
52+ if no_ui {
53+ info ! (
54+ "Running in CLI mode on port {}:{}" ,
55+ context. address_information( ) . 0 ,
56+ context. address_information( ) . 1
57+ ) ;
58+ info ! ( "Configuration file: {}" , context. configuration_file( ) ) ;
59+ if context. project_descriptions ( ) . await . is_empty ( ) {
60+ error ! (
61+ "No projects found, please run without `--no-ui` or edit configuration file"
62+ ) ;
63+ return Ok ( ( ) ) ; // Early return for no projects in CLI mode
64+ }
65+ info ! (
66+ "Cursor mcp json (project/.cursor.mcp.json):\n ```json\n {}\n ```" ,
67+ context. mcp_configuration( )
68+ ) ;
69+ // Keep the CLI mode running indefinitely until Ctrl+C
70+ loop {
71+ while let Ok ( notification) = receiver. try_recv ( ) {
72+ info ! ( " {}" , notification. description( ) ) ;
73+ }
74+ // Add a small sleep to avoid busy-waiting if desired, or just rely on Ctrl+C
75+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 100 ) ) . await ;
76+ }
77+ // Note: This loop will now only exit via Ctrl+C handled by tokio::select!
78+ } else {
79+ let project_descriptions = context. project_descriptions ( ) . await ;
80+ // run_ui blocks, so we need to handle its potential error
81+ run_ui ( context, receiver, project_descriptions)
6082 }
61- info ! (
62- "Cursor mcp json (project/.cursor.mcp.json):\n ```json\n {}\n ```" ,
63- context. mcp_configuration( )
64- ) ;
65- loop {
66- while let Ok ( notification) = receiver. try_recv ( ) {
67- info ! ( " {}" , notification. description( ) ) ;
83+ } ;
84+
85+ tokio:: select! {
86+ res = main_loop_fut => {
87+ if let Err ( e) = res {
88+ error!( "Main loop finished with error: {}" , e) ;
89+ } else {
90+ info!( "Main loop finished normally." ) ;
6891 }
92+ } ,
93+ _ = signal:: ctrl_c( ) => {
94+ info!( "Ctrl+C received, shutting down..." ) ;
95+ }
96+ _ = server_handle => {
97+ info!( "Server task finished unexpectedly." ) ;
6998 }
70- } else {
71- run_ui ( context, receiver) ?;
7299 }
73100
74- final_context. shutdown_all ( ) . await ;
101+ if no_ui {
102+ final_context. shutdown_all ( ) . await ;
103+ }
75104
76105 Ok ( ( ) )
77106}
78107
79108fn run_ui (
80109 context : context:: Context ,
81110 receiver : flume:: Receiver < context:: ContextNotification > ,
111+ project_descriptions : Vec < ui:: ProjectDescription > ,
82112) -> Result < ( ) > {
83113 // Configure eframe options (window title, size, etc.)
84114 let options = eframe:: NativeOptions {
@@ -88,7 +118,7 @@ fn run_ui(
88118 ..Default :: default ( )
89119 } ;
90120
91- let app = App :: new ( context, receiver) ;
121+ let app = App :: new ( context, receiver, project_descriptions ) ;
92122
93123 eframe:: run_native (
94124 "Cursor Rust Tools" ,
0 commit comments