remcode
Version:
Turn your AI assistant into a codebase expert. Intelligent code analysis, semantic search, and software engineering guidance through MCP integration.
439 lines (438 loc) • 17 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemcodeConfigManager = exports.ProcessingStatus = exports.VectorProvider = exports.EmbeddingModel = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const semver = __importStar(require("semver"));
const logger_1 = require("../utils/logger");
const logger = (0, logger_1.getLogger)('RemcodeConfigManager');
/**
* Available embedding models
*/
/**
* Available embedding models (Inference API compatible)
*/
var EmbeddingModel;
(function (EmbeddingModel) {
EmbeddingModel["CODEBERT"] = "microsoft/codebert-base";
EmbeddingModel["BGE_BASE"] = "BAAI/bge-base-en-v1.5";
EmbeddingModel["BGE_SMALL"] = "BAAI/bge-small-en-v1.5";
EmbeddingModel["MINILM"] = "sentence-transformers/all-MiniLM-L12-v2";
// Legacy models (may not be available via Inference API)
EmbeddingModel["GRAPHCODEBERT"] = "microsoft/graphcodebert-base";
EmbeddingModel["UNIXCODER"] = "microsoft/unixcoder-base";
EmbeddingModel["CODELLAMA"] = "codellama/CodeLlama-7b-hf";
})(EmbeddingModel || (exports.EmbeddingModel = EmbeddingModel = {}));
/**
* Vector DB providers
*/
var VectorProvider;
(function (VectorProvider) {
VectorProvider["PINECONE"] = "pinecone";
VectorProvider["QDRANT"] = "qdrant";
VectorProvider["MILVUS"] = "milvus";
VectorProvider["OPENSEARCH"] = "opensearch";
})(VectorProvider || (exports.VectorProvider = VectorProvider = {}));
/**
* Processing status values
*/
var ProcessingStatus;
(function (ProcessingStatus) {
ProcessingStatus["PENDING"] = "pending";
ProcessingStatus["PROCESSING"] = "processing";
ProcessingStatus["COMPLETED"] = "completed";
ProcessingStatus["FAILED"] = "failed";
})(ProcessingStatus || (exports.ProcessingStatus = ProcessingStatus = {}));
/**
* Class to manage Remcode configuration
*/
class RemcodeConfigManager {
/**
* Constructor
* @param repoPath Path to the repository
*/
constructor(repoPath = process.cwd()) {
this.currentVersion = '0.2.0';
this.repoPath = repoPath;
this.configPath = path.join(this.repoPath, '.remcode');
logger.debug(`RemcodeConfigManager initialized for path: ${repoPath}`);
}
/**
* Create initial configuration file
* @param owner Repository owner
* @param repo Repository name
* @param options Additional configuration options
* @returns The created configuration
*/
async createInitialConfig(owner, repo, options = {}) {
logger.info('Creating initial .remcode configuration');
try {
// Check if config already exists
if (fs.existsSync(this.configPath)) {
logger.warn('.remcode configuration already exists, updating instead');
return this.updateConfig(options);
}
// Build initial configuration
const config = this.buildInitialConfig(owner, repo, options);
// Validate configuration
const validation = this.validateConfig(config);
if (!validation.valid) {
throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`);
}
// Write configuration to file
fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
logger.info('Initial configuration created successfully');
return config;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to create initial configuration: ${errorMessage}`);
throw new Error(`Failed to create .remcode configuration: ${errorMessage}`);
}
}
/**
* Read configuration from file
* @returns The current configuration
*/
readConfig() {
try {
if (!fs.existsSync(this.configPath)) {
throw new Error('Configuration file does not exist');
}
const configContent = fs.readFileSync(this.configPath, 'utf8');
const config = JSON.parse(configContent);
return config;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to read configuration: ${errorMessage}`);
throw new Error(`Failed to read .remcode configuration: ${errorMessage}`);
}
}
/**
* Update configuration with new values
* @param updates Configuration updates
* @returns The updated configuration
*/
updateConfig(updates = {}) {
try {
// Read current configuration or create default if it doesn't exist
let config;
try {
config = this.readConfig();
}
catch (error) {
// If config doesn't exist, create a default one
logger.warn('Configuration file does not exist, creating default');
const owner = updates.repository?.owner || 'unknown';
const repo = updates.repository?.name || 'remcode-repo';
config = this.buildInitialConfig(owner, repo, {});
}
// Apply updates using deep merge
const updatedConfig = this.deepMerge(config, updates);
// Update version and last modified
updatedConfig.lastModified = new Date().toISOString();
// Check if we need to upgrade the config format
if (semver.lt(config.version, this.currentVersion)) {
logger.info(`Upgrading config from ${config.version} to ${this.currentVersion}`);
this.upgradeConfig(updatedConfig);
}
// Validate configuration
const validation = this.validateConfig(updatedConfig);
if (!validation.valid) {
throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`);
}
// Write updated configuration to file
fs.writeFileSync(this.configPath, JSON.stringify(updatedConfig, null, 2));
logger.info('Configuration updated successfully');
return updatedConfig;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to update configuration: ${errorMessage}`);
throw new Error(`Failed to update .remcode configuration: ${errorMessage}`);
}
}
/**
* Update processing status and statistics
* @param status New processing status
* @param stats Updated statistics
* @param commit Last processed commit hash
* @returns The updated configuration
*/
updateProcessingStatus(status, stats, commit) {
try {
const config = this.readConfig();
// Update processing information
config.processing.status = status;
config.processing.lastUpdate = new Date().toISOString();
if (commit) {
config.processing.lastCommit = commit;
}
// Update statistics if provided
if (stats) {
config.statistics = {
...config.statistics,
...stats,
lastUpdated: new Date().toISOString()
};
}
// Write updated configuration to file
fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
logger.info(`Processing status updated to ${status}`);
return config;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to update processing status: ${errorMessage}`);
throw new Error(`Failed to update processing status: ${errorMessage}`);
}
}
/**
* Validate configuration
* @param config Configuration to validate
* @returns Validation result
*/
validateConfig(config) {
const errors = [];
const warnings = [];
// Required fields
if (!config.version)
errors.push('Missing version');
if (!config.repository)
errors.push('Missing repository configuration');
if (!config.processing)
errors.push('Missing processing configuration');
if (!config.vectorization)
errors.push('Missing vectorization configuration');
if (!config.statistics)
errors.push('Missing statistics configuration');
// Repository validation
if (config.repository) {
if (!config.repository.name)
errors.push('Missing repository name');
if (!config.repository.owner)
errors.push('Missing repository owner');
}
// Vectorization validation
if (config.vectorization) {
// Check if provider is valid
const validProviders = Object.values(VectorProvider);
if (!validProviders.includes(config.vectorization.provider)) {
errors.push(`Invalid vector provider: ${config.vectorization.provider}`);
}
// Check embedding model
const validModels = Object.values(EmbeddingModel);
if (!validModels.includes(config.vectorization.embeddingModel)) {
warnings.push(`Unknown embedding model: ${config.vectorization.embeddingModel}`);
}
// Check dimension
if (config.vectorization.embeddingDimension <= 0) {
errors.push('Embedding dimension must be a positive number');
}
}
return {
valid: errors.length === 0,
errors,
warnings
};
}
/**
* Build initial configuration
* @param owner Repository owner
* @param repo Repository name
* @param options Additional configuration options
* @returns Initial configuration
*/
buildInitialConfig(owner, repo, options) {
const defaultBranch = options.repository?.defaultBranch || 'main';
// Create base configuration
const baseConfig = {
version: this.currentVersion,
initialized: new Date().toISOString(),
repository: {
name: repo,
owner,
url: `https://github.com/${owner}/${repo}`,
defaultBranch
},
processing: {
lastCommit: '',
lastUpdate: '',
status: ProcessingStatus.PENDING
},
vectorization: {
provider: VectorProvider.PINECONE,
indexName: `remcode-${repo.toLowerCase().replace(/[^a-z0-9]/g, '-')}`,
namespace: 'main',
embeddingModel: EmbeddingModel.CODEBERT,
embeddingModelName: 'CodeBERT-Base',
embeddingDimension: 768,
chunkSize: 1000,
modelHealthy: false,
lastModelCheck: new Date().toISOString(),
availableModels: []
},
statistics: {
filesProcessed: 0,
chunksCreated: 0,
vectorsStored: 0,
lastUpdated: new Date().toISOString()
},
advanced: {
ignorePaths: [
'node_modules/**',
'.git/**',
'dist/**',
'build/**',
'**/*.min.js',
'**/*.map'
],
includeExtensions: [
'.js', '.jsx', '.ts', '.tsx', '.py', '.java', '.go', '.rb', '.php',
'.html', '.css', '.md', '.json', '.yaml', '.yml'
],
maxFileSize: 1000000, // 1MB
useCache: true
},
lastModified: new Date().toISOString()
};
// Merge with provided options
return this.deepMerge(baseConfig, options);
}
/**
* Deep merge two objects
* @param target Target object
* @param source Source object
* @returns Merged object
*/
deepMerge(target, source) {
const output = { ...target };
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach(key => {
const sourceValue = source[key];
if (sourceValue !== undefined) {
if (isObject(sourceValue)) {
if (!(key in target)) {
output[key] = sourceValue;
}
else {
output[key] = this.deepMerge(target[key], sourceValue);
}
}
else {
output[key] = sourceValue;
}
}
});
}
return output;
}
/**
* Upgrade configuration to the latest version
* @param config Configuration to upgrade
*/
upgradeConfig(config) {
// Store the original version for logging
const originalVersion = config.version;
// Upgrade from 0.1.0 to 0.2.0
if (semver.satisfies(config.version, '0.1.0')) {
// Add advanced section if it doesn't exist
if (!config.advanced) {
config.advanced = {
ignorePaths: [
'node_modules/**',
'.git/**',
'dist/**',
'build/**',
'**/*.min.js',
'**/*.map'
],
includeExtensions: [
'.js', '.jsx', '.ts', '.tsx', '.py', '.java', '.go', '.rb', '.php',
'.html', '.css', '.md', '.json', '.yaml', '.yml'
],
maxFileSize: 1000000,
useCache: true
};
}
// Add new fields to vectorization
if (!config.vectorization.chunkSize) {
config.vectorization.chunkSize = 1000;
}
// Update version
config.version = '0.2.0';
}
// Set to current version
config.version = this.currentVersion;
logger.info(`Configuration upgraded from ${originalVersion} to ${config.version}`);
}
/**
* Check if configuration exists
* @returns True if configuration exists
*/
configExists() {
return fs.existsSync(this.configPath);
}
/**
* Delete configuration file
* @returns True if configuration was deleted
*/
deleteConfig() {
try {
if (this.configExists()) {
fs.unlinkSync(this.configPath);
logger.info('Configuration file deleted');
return true;
}
return false;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to delete configuration: ${errorMessage}`);
return false;
}
}
}
exports.RemcodeConfigManager = RemcodeConfigManager;
/**
* Helper function to check if a value is an object
* @param item Item to check
* @returns True if the item is an object
*/
function isObject(item) {
return item && typeof item === 'object' && !Array.isArray(item);
}
;