import chalk from 'chalk' import * as path from 'path' import React, { useCallback, useEffect, useRef, useState } from 'react' import { logEvent } from 'src/services/analytics/index.js' import type { CommandResultDisplay, LocalJSXCommandContext, } from '../../commands.js' import { Select } from '../../components/CustomSelect/index.js' import { Dialog } from '@anthropic/ink' import { IdeAutoConnectDialog, IdeDisableAutoConnectDialog, shouldShowAutoConnectDialog, shouldShowDisableAutoConnectDialog, } from '../../components/IdeAutoConnectDialog.js' import { Box, Text } from '@anthropic/ink' import { clearServerCache } from '../../services/mcp/client.js' import type { ScopedMcpServerConfig } from '../../services/mcp/types.js' import { useAppState, useSetAppState } from '../../state/AppState.js' import { getCwd } from '../../utils/cwd.js' import { execFileNoThrow } from '../../utils/execFileNoThrow.js' import { type DetectedIDEInfo, detectIDEs, detectRunningIDEs, type IdeType, isJetBrainsIde, isSupportedJetBrainsTerminal, isSupportedTerminal, toIDEDisplayName, } from '../../utils/ide.js' import { getCurrentWorktreeSession } from '../../utils/worktree.js' type IDEScreenProps = { availableIDEs: DetectedIDEInfo[] unavailableIDEs: DetectedIDEInfo[] selectedIDE?: DetectedIDEInfo | null onClose: () => void onSelect: (ide?: DetectedIDEInfo) => void } function IDEScreen({ availableIDEs, unavailableIDEs, selectedIDE, onClose, onSelect, }: IDEScreenProps): React.ReactNode { const [selectedValue, setSelectedValue] = useState( selectedIDE?.port?.toString() ?? 'None', ) const [showAutoConnectDialog, setShowAutoConnectDialog] = useState(false) const [showDisableAutoConnectDialog, setShowDisableAutoConnectDialog] = useState(false) const handleSelectIDE = useCallback( (value: string) => { if (value !== 'None' && shouldShowAutoConnectDialog()) { setShowAutoConnectDialog(true) } else if (value === 'None' && shouldShowDisableAutoConnectDialog()) { setShowDisableAutoConnectDialog(true) } else { onSelect(availableIDEs.find(ide => ide.port === parseInt(value, 10))) } }, [availableIDEs, onSelect], ) const ideCounts = availableIDEs.reduce>((acc, ide) => { acc[ide.name] = (acc[ide.name] || 0) + 1 return acc }, {}) const options = availableIDEs .map(ide => { const hasMultipleInstances = (ideCounts[ide.name] || 0) > 1 const showWorkspace = hasMultipleInstances && ide.workspaceFolders.length > 0 return { label: ide.name, value: ide.port.toString(), description: showWorkspace ? formatWorkspaceFolders(ide.workspaceFolders) : undefined, } }) .concat([{ label: 'None', value: 'None', description: undefined }]) if (showAutoConnectDialog) { return ( handleSelectIDE(selectedValue)} /> ) } if (showDisableAutoConnectDialog) { return ( { // Always disconnect when user selects "None", regardless of their // choice about disabling auto-connect onSelect(undefined) }} /> ) } return ( {availableIDEs.length === 0 && ( {isSupportedJetBrainsTerminal() ? 'No available IDEs detected. Please install the plugin and restart your IDE:\n' + 'https://docs.claude.com/s/claude-code-jetbrains' : 'No available IDEs detected. Make sure your IDE has the Claude Code extension or plugin installed and is running.'} )} {availableIDEs.length !== 0 && ( { setSelectedValue(value) handleSelectIDE(value) }} /> ) } function RunningIDESelector({ runningIDEs, onSelectIDE, onDone, }: { runningIDEs: IdeType[] onSelectIDE: (ide: IdeType) => void onDone: ( result?: string, options?: { display?: CommandResultDisplay }, ) => void }): React.ReactNode { const [selectedValue, setSelectedValue] = useState(runningIDEs[0] ?? '') const handleSelectIDE = useCallback( (value: string) => { onSelectIDE(value as IdeType) }, [onSelectIDE], ) const options = runningIDEs.map(ide => ({ label: toIDEDisplayName(ide), value: ide, })) function handleCancel(): void { onDone('IDE selection cancelled', { display: 'system' }) } return (