git-contextor
Version:
A code context tool with vector search and real-time monitoring, with optional Git integration.
123 lines (102 loc) ⢠3.84 kB
JavaScript
const { GitContextor } = require('../../index');
const ConfigManager = require('../../core/ConfigManager');
const logger = require('../utils/logger');
const path = require('path');
const { spawn } = require('child_process');
const fs = require('fs');
const fsp = fs.promises;
const ora = require('ora');
const { checkQdrant } = require('../utils/checkQdrant');
async function waitForService(port, maxRetries = 30) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(`http://localhost:${port}/health`, {
signal: AbortSignal.timeout(1000)
});
if (response.ok) return true;
} catch (error) {
logger.debug(`Service not ready yet (attempt ${i + 1}/${maxRetries}):`, error.message);
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
logger.error('Service failed to start within the expected time frame.');
return false;
}
async function start(options) {
const repoPath = process.cwd();
const spinner = ora('Preparing to start Git Contextor...').start();
try {
await fsp.access(path.join(repoPath, '.gitcontextor', 'config.json'));
} catch (error) {
spinner.fail('Start failed.');
logger.error('Git Contextor not initialized. Please run "git-contextor init" first.');
process.exit(1);
}
const configManager = new ConfigManager(repoPath);
await configManager.load();
const config = configManager.config;
const updates = { services: { ...config.services } };
let configChanged = false;
if (options.port && config.services.port !== parseInt(options.port, 10)) {
updates.services.port = parseInt(options.port, 10);
configChanged = true;
}
// The --clean flag will force deletion of the collection. The default is to keep it.
const keepCollection = !options.clean;
if (config.services.keepCollectionOnExit !== keepCollection) {
updates.services.keepCollectionOnExit = keepCollection;
configChanged = true;
}
if (configChanged) {
await configManager.updateConfig(updates);
logger.info('Configuration updated.');
}
try {
spinner.text = 'Connecting to Qdrant...';
await checkQdrant(configManager);
} catch (error) {
spinner.fail('Qdrant check failed.');
// The error message from checkQdrant is already logged, so we just exit.
process.exit(1);
}
const mainScript = path.resolve(process.argv[1]);
if (options.daemon) {
spinner.text = 'Starting background service...';
const logFile = path.join(configManager.configDir, 'daemon.log');
const out = fs.openSync(logFile, 'a');
const err = fs.openSync(logFile, 'a');
const child = spawn(process.execPath, [mainScript, 'serve'], {
detached: true,
stdio: ['ignore', out, err],
cwd: repoPath,
});
child.unref();
spinner.text = 'Waiting for service to become available...';
if (await waitForService(config.services.port)) {
spinner.succeed(`
š Git Contextor is running in the background!
PID: ${child.pid}
Logs: ${logFile}
š Dashboard: http://localhost:${config.services.port}
š Quick search: git-contextor query "your question"
š To stop: git-contextor stop
`);
} else {
spinner.fail('Service failed to start within 30 seconds.');
logger.error(`Check the log file for errors: ${logFile}`);
process.exit(1);
}
} else {
// Start in foreground
spinner.succeed('Starting Git Contextor in foreground. Press Ctrl+C to stop.');
const child = spawn(process.execPath, [mainScript, 'serve'], {
stdio: 'inherit',
cwd: repoPath
});
child.on('close', (code) => {
logger.info(`Git Contextor service exited with code ${code}.`);
process.exit(code);
});
}
}
module.exports = start;