@@ -87,18 +87,51 @@ crush --yolo
8787crush --data-dir /path/to/custom/.crush
8888 `,
8989 RunE: func(cmd *cobra.Command, args []string) error {
90− app, err := setupAppWithProgressBar(cmd)
90+ hostURL, err := server.ParseHostURL(clientHost)
9191 if err != nil {
92+ return fmt.Errorf("invalid host URL: %v", err)
93+ }
94+
95+ if err := ensureServer(cmd, hostURL); err != nil {
9296 return err
9397 }
94− defer app.Shutdown()
98+
99+ appInstance, err := setupAppWithProgressBar(cmd)
100+ if err != nil {
101+ return err
102+ }
103+ defer appInstance.Shutdown()
104+
105+ // Register the workspace with the server so it tracks active
106+ // clients and auto-shuts down when the last one exits.
107+ cwd, _ := ResolveCwd(cmd)
108+ dataDir, _ := cmd.Flags().GetString("data-dir")
109+ debug, _ := cmd.Flags().GetBool("debug")
110+ yolo, _ := cmd.Flags().GetBool("yolo")
111+
112+ c, err := client.NewClient(cwd, hostURL.Scheme, hostURL.Host)
113+ if err != nil {
114+ return fmt.Errorf("failed to create client: %v", err)
115+ }
116+
117+ ws, err := c.CreateWorkspace(cmd.Context(), proto.Workspace{
118+ Path: cwd,
119+ DataDir: dataDir,
120+ Debug: debug,
121+ YOLO: yolo,
122+ Version: version.Version,
123+ })
124+ if err != nil {
125+ return fmt.Errorf("failed to register workspace: %v", err)
126+ }
127+ defer func() { _ = c.DeleteWorkspace(cmd.Context(), ws.ID) }()
95128
96129 event.AppInitialized()
97130
98131 // Set up the TUI.
99132 var env uv.Environ = os.Environ()
100133
101− com := common.DefaultCommon(app)
134+ com := common.DefaultCommon(appInstance)
102135 model := ui.New(com)
103136
104137 program := tea.NewProgram(
@@ -107,7 +140,7 @@ crush --data-dir /path/to/custom/.crush
107140 tea.WithContext(cmd.Context()),
108141 tea.WithFilter(ui.MouseEventFilter), // Filter mouse events based on focus state
109142 )
110− go app.Subscribe(program)
143+ go appInstance.Subscribe(program)
111144
112145 if _, err := program.Run(); err != nil {
113146 event.Error(err)
@@ -283,7 +316,8 @@ func setupClientApp(cmd *cobra.Command, hostURL *url.URL) (*client.Client, *prot
283316}
284317
285318// ensureServer auto-starts a detached server if the socket file does not
286−// exist.
319+// exist. When connecting to an existing server, it waits for the health
320+// endpoint to respond.
287321func ensureServer(cmd *cobra.Command, hostURL *url.URL) error {
288322 switch hostURL.Scheme {
289323 case "unix", "npipe":
@@ -308,9 +342,8 @@ func ensureServer(cmd *cobra.Command, hostURL *url.URL) error {
308342 if err != nil {
309343 return fmt.Errorf("failed to initialize crush server: %v", err)
310344 }
311− default:
312− // TCP: assume server is already running.
313345 }
346+
314347 return nil
315348}
316349