UNPKG

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
"use strict"; 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;