UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

146 lines 5.53 kB
import { readFile } from 'node:fs/promises'; import { useCallback, useEffect, useRef, useState } from 'react'; import { DEFAULT_PORT, getVSCodeServer } from '../vscode/index.js'; /** * Hook to manage VS Code server integration */ export function useVSCodeServer({ enabled, port = DEFAULT_PORT, currentModel, currentProvider, onPrompt, onDiagnosticsReceived, }) { const serverRef = useRef(null); const [isConnected, setIsConnected] = useState(false); const [connectionCount, setConnectionCount] = useState(0); const [actualPort, setActualPort] = useState(null); // Store callbacks in refs to avoid re-creating server on callback changes const onPromptRef = useRef(onPrompt); const onDiagnosticsReceivedRef = useRef(onDiagnosticsReceived); const currentModelRef = useRef(currentModel); const currentProviderRef = useRef(currentProvider); // Keep refs up to date useEffect(() => { onPromptRef.current = onPrompt; }, [onPrompt]); useEffect(() => { onDiagnosticsReceivedRef.current = onDiagnosticsReceived; }, [onDiagnosticsReceived]); useEffect(() => { currentModelRef.current = currentModel; }, [currentModel]); useEffect(() => { currentProviderRef.current = currentProvider; }, [currentProvider]); // Initialize server on mount if enabled useEffect(() => { if (!enabled) { return; } const initServer = async () => { const server = await getVSCodeServer(port); serverRef.current = server; // Set up callbacks using refs server.onCallbacks({ onPrompt: (prompt, context) => { onPromptRef.current?.(prompt, context); }, onDiagnosticsResponse: diagnostics => { onDiagnosticsReceivedRef.current?.(diagnostics); }, onConnect: () => { setIsConnected(true); setConnectionCount(server.getConnectionCount()); // Send current status if (currentModelRef.current || currentProviderRef.current) { server.sendStatus(currentModelRef.current, currentProviderRef.current); } }, onDisconnect: () => { const hasConnections = server.hasConnections(); setIsConnected(hasConnections); setConnectionCount(server.getConnectionCount()); }, }); // Start the server await server.start(); setActualPort(server.getPort()); }; void initServer(); // Cleanup on unmount return () => { if (serverRef.current) { void serverRef.current.stop(); serverRef.current = null; } }; }, [enabled, port]); // Update status when model/provider changes useEffect(() => { if (serverRef.current && enabled && isConnected) { serverRef.current.sendStatus(currentModel, currentProvider); } }, [enabled, currentModel, currentProvider, isConnected]); const sendAssistantMessage = useCallback((content, isGenerating = false) => { if (serverRef.current && enabled) { serverRef.current.sendAssistantMessage(content, isGenerating); } }, [enabled]); const notifyFileChange = useCallback((filePath, originalContent, newContent, toolName, toolArgs) => { if (serverRef.current && enabled && isConnected) { return serverRef.current.sendFileChange(filePath, originalContent, newContent, toolName, toolArgs); } return null; }, [enabled, isConnected]); const requestDiagnostics = useCallback((filePath) => { if (serverRef.current && enabled) { serverRef.current.requestDiagnostics(filePath); } }, [enabled]); const updateStatus = useCallback(() => { if (serverRef.current && enabled) { serverRef.current.sendStatus(currentModel, currentProvider); } }, [enabled, currentModel, currentProvider]); return { isConnected, connectionCount, actualPort, requestedPort: port, sendAssistantMessage, notifyFileChange, requestDiagnostics, updateStatus, }; } /** * Check if VS Code mode was requested via CLI flag */ export function isVSCodeModeEnabled() { return process.argv.includes('--vscode'); } /** * Get VS Code server port from CLI args or default */ export function getVSCodePort() { const portArgIndex = process.argv.findIndex(arg => arg === '--vscode-port' || arg === '-p'); if (portArgIndex !== -1 && process.argv[portArgIndex + 1]) { const port = parseInt(process.argv[portArgIndex + 1], 10); if (!isNaN(port) && port > 0 && port < 65536) { return port; } } return DEFAULT_PORT; } /** * Helper to create file change notification with automatic content reading */ export async function createFileChangeFromTool(filePath, newContent, _toolName, _toolArgs) { let originalContent = ''; try { originalContent = await readFile(filePath, 'utf-8'); } catch { // File doesn't exist or can't be read - that's fine for create operations } return { originalContent, newContent, }; } //# sourceMappingURL=useVSCodeServer.js.map