@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
JavaScript
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