@@ -18,6 +18,12 @@ import { homedir } from 'os';
1818import { fileURLToPath } from 'url' ;
1919import { getSystemPython } from '../utils/python-utils.js' ;
2020import { EXEC_TIMEOUTS } from '../utils/exec-constants.js' ;
21+ import {
22+ UvManager ,
23+ ensureUvInstalled ,
24+ isUvAvailable ,
25+ createCodexLensUvManager ,
26+ } from '../utils/uv-manager.js' ;
2127
2228// Get directory of this module
2329const __filename = fileURLToPath ( import . meta. url ) ;
@@ -363,6 +369,15 @@ async function ensureLiteLLMEmbedderReady(): Promise<BootstrapResult> {
363369 */
364370type GpuMode = 'cpu' | 'cuda' | 'directml' ;
365371
372+ /**
373+ * Mapping from GPU mode to codexlens extras for UV installation
374+ */
375+ const GPU_MODE_EXTRAS : Record < GpuMode , string [ ] > = {
376+ cpu : [ 'semantic' ] ,
377+ cuda : [ 'semantic-gpu' ] ,
378+ directml : [ 'semantic-directml' ] ,
379+ } ;
380+
366381/**
367382 * Python environment info for compatibility checks
368383 */
@@ -467,12 +482,165 @@ async function detectGpuSupport(): Promise<{ mode: GpuMode; available: GpuMode[]
467482 return { mode : recommendedMode , available, info : detectedInfo , pythonEnv } ;
468483}
469484
485+ /**
486+ * Bootstrap CodexLens venv using UV (fast package manager)
487+ * @param gpuMode - GPU acceleration mode for semantic search
488+ * @returns Bootstrap result
489+ */
490+ async function bootstrapWithUv ( gpuMode : GpuMode = 'cpu' ) : Promise < BootstrapResult > {
491+ console . log ( '[CodexLens] Bootstrapping with UV package manager...' ) ;
492+
493+ // Ensure UV is installed
494+ const uvInstalled = await ensureUvInstalled ( ) ;
495+ if ( ! uvInstalled ) {
496+ return { success : false , error : 'Failed to install UV package manager' } ;
497+ }
498+
499+ // Create UV manager for CodexLens
500+ const uv = createCodexLensUvManager ( ) ;
501+
502+ // Create venv if not exists
503+ if ( ! uv . isVenvValid ( ) ) {
504+ console . log ( '[CodexLens] Creating virtual environment with UV...' ) ;
505+ const createResult = await uv . createVenv ( ) ;
506+ if ( ! createResult . success ) {
507+ return { success : false , error : `Failed to create venv: ${ createResult . error } ` } ;
508+ }
509+ }
510+
511+ // Find local codex-lens package
512+ const possiblePaths = [
513+ join ( process . cwd ( ) , 'codex-lens' ) ,
514+ join ( __dirname , '..' , '..' , '..' , 'codex-lens' ) , // ccw/src/tools -> project root
515+ join ( homedir ( ) , 'codex-lens' ) ,
516+ ] ;
517+
518+ let codexLensPath : string | null = null ;
519+ for ( const localPath of possiblePaths ) {
520+ if ( existsSync ( join ( localPath , 'pyproject.toml' ) ) ) {
521+ codexLensPath = localPath ;
522+ break ;
523+ }
524+ }
525+
526+ // Determine extras based on GPU mode
527+ const extras = GPU_MODE_EXTRAS [ gpuMode ] ;
528+
529+ if ( codexLensPath ) {
530+ console . log ( `[CodexLens] Installing from local path with UV: ${ codexLensPath } ` ) ;
531+ console . log ( `[CodexLens] Extras: ${ extras . join ( ', ' ) } ` ) ;
532+ const installResult = await uv . installFromProject ( codexLensPath , extras ) ;
533+ if ( ! installResult . success ) {
534+ return { success : false , error : `Failed to install codexlens: ${ installResult . error } ` } ;
535+ }
536+ } else {
537+ // Install from PyPI with extras
538+ console . log ( '[CodexLens] Installing from PyPI with UV...' ) ;
539+ const packageSpec = `codexlens[${ extras . join ( ',' ) } ]` ;
540+ const installResult = await uv . install ( [ packageSpec ] ) ;
541+ if ( ! installResult . success ) {
542+ return { success : false , error : `Failed to install codexlens: ${ installResult . error } ` } ;
543+ }
544+ }
545+
546+ // Clear cache after successful installation
547+ clearVenvStatusCache ( ) ;
548+ console . log ( `[CodexLens] Bootstrap with UV complete (${ gpuMode } mode)` ) ;
549+ return { success : true , message : `Installed with UV (${ gpuMode } mode)` } ;
550+ }
551+
552+ /**
553+ * Install semantic search dependencies using UV (fast package manager)
554+ * UV automatically handles ONNX Runtime conflicts
555+ * @param gpuMode - GPU acceleration mode: 'cpu', 'cuda', or 'directml'
556+ * @returns Bootstrap result
557+ */
558+ async function installSemanticWithUv ( gpuMode : GpuMode = 'cpu' ) : Promise < BootstrapResult > {
559+ console . log ( '[CodexLens] Installing semantic dependencies with UV...' ) ;
560+
561+ // First check if CodexLens is installed
562+ const venvStatus = await checkVenvStatus ( ) ;
563+ if ( ! venvStatus . ready ) {
564+ return { success : false , error : 'CodexLens not installed. Install CodexLens first.' } ;
565+ }
566+
567+ // Check Python environment compatibility for DirectML
568+ if ( gpuMode === 'directml' ) {
569+ const pythonEnv = await checkPythonEnvForDirectML ( ) ;
570+ if ( ! pythonEnv . compatible ) {
571+ const errorDetails = pythonEnv . error || 'Unknown compatibility issue' ;
572+ return {
573+ success : false ,
574+ error : `DirectML installation failed: ${ errorDetails } \n\nTo fix this:\n1. Uninstall current Python\n2. Install 64-bit Python 3.10, 3.11, or 3.12 from python.org\n3. Delete ~/.codexlens/venv folder\n4. Reinstall CodexLens` ,
575+ } ;
576+ }
577+ console . log ( `[CodexLens] Python ${ pythonEnv . version } (${ pythonEnv . architecture } -bit) - DirectML compatible` ) ;
578+ }
579+
580+ // Create UV manager
581+ const uv = createCodexLensUvManager ( ) ;
582+
583+ // Find local codex-lens package
584+ const possiblePaths = [
585+ join ( process . cwd ( ) , 'codex-lens' ) ,
586+ join ( __dirname , '..' , '..' , '..' , 'codex-lens' ) ,
587+ join ( homedir ( ) , 'codex-lens' ) ,
588+ ] ;
589+
590+ let codexLensPath : string | null = null ;
591+ for ( const localPath of possiblePaths ) {
592+ if ( existsSync ( join ( localPath , 'pyproject.toml' ) ) ) {
593+ codexLensPath = localPath ;
594+ break ;
595+ }
596+ }
597+
598+ // Determine extras based on GPU mode
599+ const extras = GPU_MODE_EXTRAS [ gpuMode ] ;
600+ const modeDescription =
601+ gpuMode === 'cuda'
602+ ? 'NVIDIA CUDA GPU acceleration'
603+ : gpuMode === 'directml'
604+ ? 'Windows DirectML GPU acceleration'
605+ : 'CPU (ONNX Runtime)' ;
606+
607+ console . log ( `[CodexLens] Mode: ${ modeDescription } ` ) ;
608+ console . log ( `[CodexLens] Extras: ${ extras . join ( ', ' ) } ` ) ;
609+
610+ // Install with extras - UV handles dependency conflicts automatically
611+ if ( codexLensPath ) {
612+ console . log ( `[CodexLens] Reinstalling from local path with semantic extras...` ) ;
613+ const installResult = await uv . installFromProject ( codexLensPath , extras ) ;
614+ if ( ! installResult . success ) {
615+ return { success : false , error : `Installation failed: ${ installResult . error } ` } ;
616+ }
617+ } else {
618+ // Install from PyPI
619+ const packageSpec = `codexlens[${ extras . join ( ',' ) } ]` ;
620+ console . log ( `[CodexLens] Installing ${ packageSpec } from PyPI...` ) ;
621+ const installResult = await uv . install ( [ packageSpec ] ) ;
622+ if ( ! installResult . success ) {
623+ return { success : false , error : `Installation failed: ${ installResult . error } ` } ;
624+ }
625+ }
626+
627+ console . log ( `[CodexLens] Semantic dependencies installed successfully (${ gpuMode } mode)` ) ;
628+ return { success : true , message : `Installed with ${ modeDescription } ` } ;
629+ }
630+
470631/**
471632 * Install semantic search dependencies with optional GPU acceleration
472633 * @param gpuMode - GPU acceleration mode: 'cpu', 'cuda', or 'directml'
473634 * @returns Bootstrap result
474635 */
475636async function installSemantic ( gpuMode : GpuMode = 'cpu' ) : Promise < BootstrapResult > {
637+ // Prefer UV if available
638+ if ( await isUvAvailable ( ) ) {
639+ console . log ( '[CodexLens] Using UV for semantic installation...' ) ;
640+ return installSemanticWithUv ( gpuMode ) ;
641+ }
642+
643+ // Fall back to pip logic...
476644 // First ensure CodexLens is installed
477645 const venvStatus = await checkVenvStatus ( ) ;
478646 if ( ! venvStatus . ready ) {
@@ -617,6 +785,13 @@ async function installSemantic(gpuMode: GpuMode = 'cpu'): Promise<BootstrapResul
617785 * @returns Bootstrap result
618786 */
619787async function bootstrapVenv ( ) : Promise < BootstrapResult > {
788+ // Prefer UV if available (faster package resolution and installation)
789+ if ( await isUvAvailable ( ) ) {
790+ console . log ( '[CodexLens] Using UV for bootstrap...' ) ;
791+ return bootstrapWithUv ( ) ;
792+ }
793+
794+ // Fall back to pip logic...
620795 // Ensure data directory exists
621796 if ( ! existsSync ( CODEXLENS_DATA_DIR ) ) {
622797 mkdirSync ( CODEXLENS_DATA_DIR , { recursive : true } ) ;
@@ -1502,6 +1677,9 @@ export {
15021677 uninstallCodexLens ,
15031678 cancelIndexing ,
15041679 isIndexingInProgress ,
1680+ // UV-based installation functions
1681+ bootstrapWithUv ,
1682+ installSemanticWithUv ,
15051683} ;
15061684
15071685// Export Python path for direct spawn usage (e.g., watcher)
0 commit comments