Skip to content
This repository was archived by the owner on Sep 23, 2025. It is now read-only.

Commit 9acf01a

Browse files
committed
Complete Ask Socratic Shell integration with intelligent terminal routing
- Replace terminal naming hack with real PID-based capability detection - Implement async terminal.processId extraction and registry matching - Add terminal picker UI with VSCode QuickPick for multiple AI-enabled terminals - Add selection memory using workspace state with visual indicators (star icons) - Sort picker to show last-used terminal first with 'last used' labels - Update error messages to reflect intelligent routing capabilities - Add comprehensive logging for debugging terminal selection process The Ask Socratic Shell integration now provides: - Automatic AI capability detection (no naming required) - Single terminal auto-routing for seamless UX - Multi-terminal picker with smart defaults - Workspace-specific preference memory - Production-ready user experience Eliminates all terminal naming hacks with true capability-based routing.
1 parent 5e838de commit 9acf01a

File tree

1 file changed

+106
-13
lines changed

1 file changed

+106
-13
lines changed

extension/src/extension.ts

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)