remcode
Version:
Turn your AI assistant into a codebase expert. Intelligent code analysis, semantic search, and software engineering guidance through MCP integration.
297 lines (296 loc) • 11.8 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.SetupDetector = exports.SetupReason = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const util = __importStar(require("util"));
const child_process = __importStar(require("child_process"));
const logger_1 = require("../utils/logger");
const logger = (0, logger_1.getLogger)('SetupDetector');
const execAsync = util.promisify(child_process.exec);
/**
* Possible reasons for setup requirements
*/
var SetupReason;
(function (SetupReason) {
SetupReason["SETUP_COMPLETE"] = "Setup complete";
SetupReason["NO_GIT_REPO"] = "No Git repository detected";
SetupReason["NO_GITHUB_REPO"] = "No GitHub repository detected";
SetupReason["NO_REMCODE_FILE"] = "No .remcode configuration file found";
SetupReason["NO_WORKFLOW"] = "No GitHub Actions workflow found";
SetupReason["INVALID_CONFIG"] = "Invalid .remcode configuration file";
SetupReason["NO_REQUIRED_SECRETS"] = "Missing required GitHub secrets";
SetupReason["NEEDS_UPDATE"] = "Remcode configuration needs to be updated";
})(SetupReason || (exports.SetupReason = SetupReason = {}));
/**
* Class to detect repository setup requirements for Remcode
*/
class SetupDetector {
/**
* Constructor
* @param repoPath Path to the repository to analyze
*/
constructor(repoPath = process.cwd()) {
this.repoPath = repoPath;
logger.debug(`SetupDetector initialized for path: ${repoPath}`);
}
/**
* Detect all setup requirements
*/
async detectSetupNeeds() {
logger.info('Detecting setup requirements');
try {
// Basic checks
const hasGitRepo = this.hasGitRepository();
const hasRemcodeFile = this.hasRemcodeFile();
const hasWorkflow = this.hasGitHubWorkflow();
// Advanced checks
const gitInfo = hasGitRepo ? await this.getGitRemoteInfo() : undefined;
const hasGitHubRepo = !!gitInfo?.isGitHub;
const hasValidConfig = hasRemcodeFile ? this.validateRemcodeConfig() : false;
const hasRequiredSecrets = hasGitHubRepo ? await this.checkRequiredSecrets(gitInfo) : false;
// Version information
const configVersion = hasValidConfig ? this.getConfigVersion() : undefined;
const workflowVersion = hasWorkflow ? await this.getWorkflowVersion() : undefined;
// Determine setup needs
const needsSetup = !hasGitRepo || !hasGitHubRepo || !hasRemcodeFile || !hasWorkflow || !hasValidConfig || !hasRequiredSecrets;
const reason = this.getSetupReason(hasGitRepo, hasGitHubRepo, hasRemcodeFile, hasWorkflow, hasValidConfig, hasRequiredSecrets);
return {
needsSetup,
hasRemcodeFile,
hasGitRepo,
hasGitHubRepo,
hasWorkflow,
hasRequiredSecrets,
hasValidConfig,
reason,
gitInfo,
configVersion,
workflowVersion,
remcodeVersion: this.getRemcodeVersion(),
detectionTimestamp: new Date().toISOString()
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Error detecting setup requirements: ${errorMessage}`);
throw new Error(`Failed to detect setup requirements: ${errorMessage}`);
}
}
/**
* Check if directory is a Git repository
*/
hasGitRepository() {
try {
return fs.existsSync(path.join(this.repoPath, '.git'));
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn(`Error checking for Git repository: ${errorMessage}`);
return false;
}
}
/**
* Check if .remcode configuration file exists
*/
hasRemcodeFile() {
try {
return fs.existsSync(path.join(this.repoPath, '.remcode'));
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn(`Error checking for .remcode file: ${errorMessage}`);
return false;
}
}
/**
* Check if GitHub workflow file exists
*/
hasGitHubWorkflow() {
try {
const workflowPath = path.join(this.repoPath, '.github', 'workflows', 'remcode.yml');
return fs.existsSync(workflowPath);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn(`Error checking for GitHub workflow: ${errorMessage}`);
return false;
}
}
/**
* Get Git remote information
*/
async getGitRemoteInfo() {
try {
// Check if git remote exists
const { stdout: remoteUrl } = await execAsync('git remote get-url origin', {
cwd: this.repoPath
});
if (!remoteUrl) {
return { exists: false, url: '', isGitHub: false };
}
const trimmedUrl = remoteUrl.trim();
const isGitHub = trimmedUrl.includes('github.com');
// Parse owner and repo from URL
let owner, repo;
if (isGitHub) {
// Handle HTTPS and SSH formats
// https://github.com/owner/repo.git or git@github.com:owner/repo.git
const httpsMatch = trimmedUrl.match(/github\.com[\/:]([\w.-]+)\/([\w.-]+)(?:\.git)?$/);
if (httpsMatch) {
[, owner, repo] = httpsMatch;
// Remove .git suffix if present
repo = repo.replace(/\.git$/, '');
}
// Get default branch
const { stdout: branchOutput } = await execAsync('git symbolic-ref refs/remotes/origin/HEAD --short', {
cwd: this.repoPath
}).catch(() => ({ stdout: 'origin/main' })); // Default to origin/main if command fails
const defaultBranch = branchOutput.trim().replace('origin/', '');
return {
exists: true,
url: trimmedUrl,
owner,
repo,
isGitHub,
defaultBranch
};
}
return { exists: true, url: trimmedUrl, isGitHub: false };
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn(`Error getting Git remote info: ${errorMessage}`);
return { exists: false, url: '', isGitHub: false };
}
}
/**
* Validate .remcode configuration file
*/
validateRemcodeConfig() {
try {
const configPath = path.join(this.repoPath, '.remcode');
const configContent = fs.readFileSync(configPath, 'utf8');
const config = JSON.parse(configContent);
// Basic validation
return !!config.version &&
!!config.repository &&
!!config.repository.owner &&
!!config.repository.name;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn(`Error validating .remcode config: ${errorMessage}`);
return false;
}
}
/**
* Get .remcode configuration version
*/
getConfigVersion() {
try {
const configPath = path.join(this.repoPath, '.remcode');
const configContent = fs.readFileSync(configPath, 'utf8');
const config = JSON.parse(configContent);
return config.version;
}
catch (error) {
return undefined;
}
}
/**
* Get GitHub workflow version
*/
async getWorkflowVersion() {
try {
const workflowPath = path.join(this.repoPath, '.github', 'workflows', 'remcode.yml');
const workflowContent = fs.readFileSync(workflowPath, 'utf8');
// Look for version comment in workflow file
const versionMatch = workflowContent.match(/# Remcode workflow version: (\S+)/);
return versionMatch ? versionMatch[1] : '0.1.0'; // Default to 0.1.0 if not found
}
catch (error) {
return undefined;
}
}
/**
* Get current remcode version
*/
getRemcodeVersion() {
try {
// Try to get version from package.json
const packageJsonPath = path.resolve(__dirname, '..', '..', 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
return packageJson.version || '0.1.0';
}
return '0.1.0'; // Default version
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.debug(`Error getting remcode version: ${errorMessage}`);
return '0.1.0';
}
}
/**
* Check if required GitHub secrets are set
*/
async checkRequiredSecrets(gitInfo) {
// This would typically use GitHub API to check if secrets exist
// For now, we'll return true as a stub implementation
// TODO: Implement actual GitHub API check for secrets
return true;
}
/**
* Determine the reason for setup requirements
*/
getSetupReason(hasGitRepo, hasGitHubRepo, hasRemcodeFile, hasWorkflow, hasValidConfig, hasRequiredSecrets) {
if (!hasGitRepo)
return SetupReason.NO_GIT_REPO;
if (!hasGitHubRepo)
return SetupReason.NO_GITHUB_REPO;
if (!hasRemcodeFile)
return SetupReason.NO_REMCODE_FILE;
if (hasRemcodeFile && !hasValidConfig)
return SetupReason.INVALID_CONFIG;
if (!hasWorkflow)
return SetupReason.NO_WORKFLOW;
if (!hasRequiredSecrets)
return SetupReason.NO_REQUIRED_SECRETS;
return SetupReason.SETUP_COMPLETE;
}
}
exports.SetupDetector = SetupDetector;
;