@@ -411,7 +411,7 @@ export function activate(context: vscode.ExtensionContext) {
411411 daemonClient . start ( ) ;
412412
413413 // 💡: Set up universal selection detection for interactive code review
414- setupSelectionDetection ( context , outputChannel ) ;
414+ setupSelectionDetection ( context , outputChannel , daemonClient ) ;
415415
416416 // Register commands
417417 const showReviewCommand = vscode . commands . registerCommand ( 'dialectic.showReview' , ( ) => {
@@ -439,7 +439,7 @@ export function activate(context: vscode.ExtensionContext) {
439439}
440440
441441// 💡: Set up universal selection detection for interactive code review
442- function setupSelectionDetection ( context : vscode . ExtensionContext , outputChannel : vscode . OutputChannel ) : void {
442+ function setupSelectionDetection ( context : vscode . ExtensionContext , outputChannel : vscode . OutputChannel , daemonClient : DaemonClient ) : void {
443443 outputChannel . appendLine ( 'Setting up universal selection detection...' ) ;
444444
445445 // 💡: Track current selection state
@@ -491,7 +491,7 @@ function setupSelectionDetection(context: vscode.ExtensionContext, outputChannel
491491 ) ;
492492
493493 // 💡: Register command for when user clicks the code action
494- const chatIconCommand = vscode . commands . registerCommand ( 'dialectic.chatAboutSelection' , ( ) => {
494+ const chatIconCommand = vscode . commands . registerCommand ( 'dialectic.chatAboutSelection' , async ( ) => {
495495 if ( currentSelection ) {
496496 const selectedText = currentSelection . editor . document . getText ( currentSelection . selection ) ;
497497 const filePath = currentSelection . editor . document . fileName ;
@@ -505,15 +505,15 @@ function setupSelectionDetection(context: vscode.ExtensionContext, outputChannel
505505 outputChannel . appendLine ( `Location: ${ filePath } :${ startLine } :${ startColumn } -${ endLine } :${ endColumn } ` ) ;
506506
507507 // 💡: Phase 4 & 5: Find Q chat terminal and inject formatted message
508- const targetTerminal = findQChatTerminal ( outputChannel ) ;
508+ const targetTerminal = await findQChatTerminal ( outputChannel , daemonClient , context ) ;
509509 if ( targetTerminal ) {
510510 const formattedMessage = formatSelectionMessage ( selectedText , filePath , startLine , startColumn , endLine , endColumn ) ;
511511 targetTerminal . sendText ( formattedMessage , false ) ; // false = don't execute, just insert text
512512 targetTerminal . show ( ) ; // Bring terminal into focus
513513 outputChannel . appendLine ( `Message injected into terminal: ${ targetTerminal . name } ` ) ;
514514 } else {
515515 outputChannel . appendLine ( 'No suitable Q chat terminal found' ) ;
516- vscode . window . showWarningMessage ( 'No suitable terminal found. Please ensure you have either: 1) Only one terminal open, or 2) A terminal named "Socratic Shell" or "AI" .' ) ;
516+ vscode . window . showWarningMessage ( 'No suitable terminal found. Please ensure you have a terminal with an active MCP server (like Q chat) running .' ) ;
517517 }
518518 } else {
519519 outputChannel . appendLine ( 'Chat action triggered but no current selection found' ) ;
@@ -524,8 +524,8 @@ function setupSelectionDetection(context: vscode.ExtensionContext, outputChannel
524524 outputChannel . appendLine ( 'Selection detection with Code Actions setup complete' ) ;
525525}
526526
527- // 💡: Phase 4 - Simplified terminal detection logic
528- function findQChatTerminal ( outputChannel : vscode . OutputChannel ) : vscode . Terminal | null {
527+ // 💡: Phase 4 - Intelligent terminal detection using registry
528+ async function findQChatTerminal ( outputChannel : vscode . OutputChannel , daemonClient : DaemonClient , context : vscode . ExtensionContext ) : Promise < vscode . Terminal | null > {
529529 const terminals = vscode . window . terminals ;
530530 outputChannel . appendLine ( `Found ${ terminals . length } open terminals` ) ;
531531
@@ -534,26 +534,119 @@ function findQChatTerminal(outputChannel: vscode.OutputChannel): vscode.Terminal
534534 return null ;
535535 }
536536
537- // 💡: Simple case - exactly one terminal
537+ // Get active terminals with MCP servers from registry
538+ const activeTerminals = daemonClient . getActiveTerminals ( ) ;
539+ outputChannel . appendLine ( `Active MCP server terminals: [${ Array . from ( activeTerminals ) . join ( ', ' ) } ]` ) ;
540+
541+ if ( activeTerminals . size === 0 ) {
542+ outputChannel . appendLine ( 'No terminals with active MCP servers found' ) ;
543+ return null ;
544+ }
545+
546+ // Filter terminals to only those with active MCP servers (async)
547+ const terminalChecks = await Promise . all (
548+ terminals . map ( async ( terminal ) => {
549+ // Extract the shell PID from the terminal (async)
550+ const shellPID = await terminal . processId ;
551+
552+ // Log terminal for debugging
553+ outputChannel . appendLine ( ` Checking terminal: "${ terminal . name } " (PID: ${ shellPID } )` ) ;
554+
555+ // Check if this terminal's shell PID is in our active registry
556+ if ( shellPID && activeTerminals . has ( shellPID ) ) {
557+ outputChannel . appendLine ( ` ✅ Terminal "${ terminal . name } " has active MCP server (PID: ${ shellPID } )` ) ;
558+ return { terminal, isAiEnabled : true } ;
559+ } else {
560+ outputChannel . appendLine ( ` ❌ Terminal "${ terminal . name } " has no active MCP server (PID: ${ shellPID } )` ) ;
561+ return { terminal, isAiEnabled : false } ;
562+ }
563+ } )
564+ ) ;
565+
566+ // Extract only the AI-enabled terminals
567+ const aiEnabledTerminals = terminalChecks
568+ . filter ( check => check . isAiEnabled )
569+ . map ( check => check . terminal ) ;
570+
571+ outputChannel . appendLine ( `AI-enabled terminals found: ${ aiEnabledTerminals . length } ` ) ;
572+
573+ // 💡: Simple case - exactly one AI-enabled terminal
574+ if ( aiEnabledTerminals . length === 1 ) {
575+ const terminal = aiEnabledTerminals [ 0 ] ;
576+ outputChannel . appendLine ( `Using single AI-enabled terminal: ${ terminal . name } ` ) ;
577+ return terminal ;
578+ }
579+
580+ // 💡: Multiple AI-enabled terminals - show picker UI with memory
581+ if ( aiEnabledTerminals . length > 1 ) {
582+ outputChannel . appendLine ( `Multiple AI-enabled terminals found: ${ aiEnabledTerminals . length } ` ) ;
583+
584+ // Get previously selected terminal PID from workspace state
585+ const lastSelectedPID = context . workspaceState . get < number > ( 'dialectic.lastSelectedTerminalPID' ) ;
586+ outputChannel . appendLine ( `Last selected terminal PID: ${ lastSelectedPID } ` ) ;
587+
588+ // Create picker items with terminal info
589+ const terminalItems = await Promise . all (
590+ aiEnabledTerminals . map ( async ( terminal ) => {
591+ const pid = await terminal . processId ;
592+ const isLastSelected = pid === lastSelectedPID ;
593+ return {
594+ label : isLastSelected ? `$(star-full) ${ terminal . name } ` : terminal . name ,
595+ description : `PID: ${ pid } ${ isLastSelected ? ' (last used)' : '' } ` ,
596+ detail : 'Terminal with active MCP server' ,
597+ terminal : terminal ,
598+ pid : pid
599+ } ;
600+ } )
601+ ) ;
602+
603+ // Sort items to put last selected first
604+ terminalItems . sort ( ( a , b ) => {
605+ if ( a . pid === lastSelectedPID ) return - 1 ;
606+ if ( b . pid === lastSelectedPID ) return 1 ;
607+ return 0 ;
608+ } ) ;
609+
610+ // Show the picker to user
611+ const selectedItem = await vscode . window . showQuickPick ( terminalItems , {
612+ placeHolder : 'Select terminal for AI chat (⭐ = last used)' ,
613+ title : 'Multiple AI-enabled terminals found'
614+ } ) ;
615+
616+ if ( selectedItem ) {
617+ outputChannel . appendLine ( `User selected terminal: ${ selectedItem . terminal . name } (PID: ${ selectedItem . pid } )` ) ;
618+
619+ // Remember this selection for next time
620+ await context . workspaceState . update ( 'dialectic.lastSelectedTerminalPID' , selectedItem . pid ) ;
621+ outputChannel . appendLine ( `Saved terminal PID ${ selectedItem . pid } as last selected` ) ;
622+
623+ return selectedItem . terminal ;
624+ } else {
625+ outputChannel . appendLine ( 'User cancelled terminal selection' ) ;
626+ return null ;
627+ }
628+ }
629+
630+ // 💡: No AI-enabled terminals found - fall back to old logic for compatibility
631+ outputChannel . appendLine ( 'No AI-enabled terminals found, falling back to name-based detection' ) ;
632+
538633 if ( terminals . length === 1 ) {
539634 const terminal = terminals [ 0 ] ;
540- outputChannel . appendLine ( `Using single terminal: ${ terminal . name } ` ) ;
635+ outputChannel . appendLine ( `Using single terminal (fallback) : ${ terminal . name } ` ) ;
541636 return terminal ;
542637 }
543638
544- // 💡: Multiple terminals - look for "Socratic Shell" or "AI" named terminal
545639 const targetTerminal = terminals . find ( terminal => {
546640 const name = terminal . name . toLowerCase ( ) ;
547641 return name . includes ( 'socratic shell' ) || name . includes ( 'ai' ) ;
548642 } ) ;
549643
550644 if ( targetTerminal ) {
551- outputChannel . appendLine ( `Found target terminal: ${ targetTerminal . name } ` ) ;
645+ outputChannel . appendLine ( `Found target terminal (fallback) : ${ targetTerminal . name } ` ) ;
552646 return targetTerminal ;
553647 }
554648
555- // 💡: Multiple terminals, no clear choice - could present user with options in future
556- outputChannel . appendLine ( 'Multiple terminals found, but none named "Socratic Shell" or "AI"' ) ;
649+ outputChannel . appendLine ( 'Multiple terminals found, but none are AI-enabled or named appropriately' ) ;
557650 return null ;
558651}
559652
0 commit comments