@@ -81,11 +81,30 @@ final class ClaudeStatusExtractor: ObservableObject, Loggable {
8181 return nil
8282 }
8383
84- // Find window that matches our specific Claude instance
84+ // Try to find the window that matches this specific Claude instance
8585 for window in windowsArray {
86+ // Get window title to help with matching
87+ var titleRef : CFTypeRef ?
88+ var windowTitle = " "
89+ if AXUIElementCopyAttributeValue ( window, kAXTitleAttribute as CFString , & titleRef) == . success,
90+ let title = titleRef as? String {
91+ windowTitle = title
92+ }
93+
94+ // Check if this window matches our instance
95+ if windowMatchesInstance ( title: windowTitle, instance: instance) {
96+ if let content = extractTextFromWindow ( window) ,
97+ content. contains ( " esc to interrupt " ) {
98+ logger. debug ( " Found matching window for instance \( instance. folderName) by title " )
99+ return content
100+ }
101+ }
102+
103+ // Also check window content for matching
86104 if let content = extractTextFromWindow ( window) ,
87105 windowContainsInstance ( content: content, instance: instance) ,
88106 content. contains ( " esc to interrupt " ) {
107+ logger. debug ( " Found matching window for instance \( instance. folderName) by content " )
89108 return content
90109 }
91110 }
@@ -157,16 +176,47 @@ final class ClaudeStatusExtractor: ObservableObject, Loggable {
157176 // MARK: - ScreenCaptureKit-based Extraction
158177
159178 private nonisolated func extractViaScreenCapture( instance: ClaudeInstance ) async -> String ? {
160- logger. debug ( " Attempting ScreenCaptureKit extraction for \( instance. folderName) " )
179+ logger. debug ( " Attempting ScreenCaptureKit extraction for \( instance. folderName) with TTY: \( instance . ttyPath ) " )
161180
162181 do {
163182 let content = try await SCShareableContent . excludingDesktopWindows ( false , onScreenWindowsOnly: true )
164183
165- for window in content. windows {
184+ // Sort windows by how well they match the instance
185+ let matchingWindows = content. windows. compactMap { window -> ( window: SCWindow , score: Int ) ? in
166186 guard let windowTitle = window. title,
167187 let appName = window. owningApplication? . applicationName,
168- isTerminalApp ( appName) ,
169- windowMatchesInstance ( title: windowTitle, instance: instance) else {
188+ isTerminalApp ( appName) else {
189+ return nil
190+ }
191+
192+ var score = 0
193+
194+ // TTY match is most specific (highest score)
195+ if !instance. ttyPath. isEmpty {
196+ let ttyName = URL ( fileURLWithPath: instance. ttyPath) . lastPathComponent
197+ if windowTitle. contains ( ttyName) {
198+ score += 100
199+ }
200+ }
201+
202+ // Full working directory match
203+ if windowTitle. contains ( instance. workingDirectory) {
204+ score += 50
205+ }
206+
207+ // Folder name match (less specific)
208+ if instance. folderName. count > 3 && windowTitle. contains ( instance. folderName) {
209+ score += 10
210+ }
211+
212+ return score > 0 ? ( window, score) : nil
213+ } . sorted { $0. score > $1. score }
214+
215+ // Try the best matching window first
216+ for (window, score) in matchingWindows {
217+ logger. debug ( " Trying window ' \( window. title ?? " " ) ' with match score \( score) for instance \( instance. folderName) " )
218+
219+ guard let windowTitle = window. title else {
170220 continue
171221 }
172222
@@ -323,13 +373,59 @@ final class ClaudeStatusExtractor: ObservableObject, Loggable {
323373 }
324374
325375 private nonisolated func windowContainsInstance( content: String , instance: ClaudeInstance ) -> Bool {
326- content. contains ( instance. workingDirectory) || content. contains ( instance. folderName)
376+ // First check if TTY is mentioned in the content
377+ if !instance. ttyPath. isEmpty {
378+ let ttyName = URL ( fileURLWithPath: instance. ttyPath) . lastPathComponent
379+ if content. contains ( ttyName) {
380+ logger. debug ( " Window content matches instance by TTY: \( ttyName) " )
381+ return true
382+ }
383+ }
384+
385+ // Then check for specific working directory (more specific than just folder name)
386+ if content. contains ( instance. workingDirectory) {
387+ logger. debug ( " Window content matches instance by working directory: \( instance. workingDirectory) " )
388+ return true
389+ }
390+
391+ // Last resort: check folder name, but only if it's reasonably unique
392+ if instance. folderName. count > 3 && instance. folderName != " / " &&
393+ content. contains ( instance. folderName) {
394+ logger. debug ( " Window content matches instance by folder name: \( instance. folderName) " )
395+ return true
396+ }
397+
398+ return false
327399 }
328400
329401 private nonisolated func windowMatchesInstance( title: String , instance: ClaudeInstance ) -> Bool {
330- title. contains ( instance. folderName) ||
331- title. contains ( instance. workingDirectory) ||
332- title. contains ( " Claude " )
402+ // First check if TTY is in the title (most specific)
403+ if !instance. ttyPath. isEmpty {
404+ let ttyName = URL ( fileURLWithPath: instance. ttyPath) . lastPathComponent
405+ if title. contains ( ttyName) {
406+ logger. debug ( " Window title matches instance by TTY: \( ttyName) " )
407+ return true
408+ }
409+ }
410+
411+ // Check for full working directory path in title
412+ if title. contains ( instance. workingDirectory) {
413+ logger. debug ( " Window title matches instance by working directory: \( instance. workingDirectory) " )
414+ return true
415+ }
416+
417+ // Check folder name only if it's specific enough
418+ if instance. folderName. count > 3 && instance. folderName != " / " &&
419+ title. contains ( instance. folderName) {
420+ // Make sure it's not just a partial match
421+ let components = title. components ( separatedBy: " / " )
422+ if components. contains ( instance. folderName) {
423+ logger. debug ( " Window title matches instance by folder name: \( instance. folderName) " )
424+ return true
425+ }
426+ }
427+
428+ return false
333429 }
334430
335431 private nonisolated func isTerminalApp( _ appName: String ) -> Bool {
0 commit comments