UNPKG

supernal-coding

Version:

Comprehensive development workflow CLI with kanban task management, project validation, git safety hooks, and cross-project distribution system

267 lines (225 loc) 8.21 kB
#!/usr/bin/env node // Development Monitoring System // Integrated continuous build and workflow monitoring const { execSync } = require('child_process'); const chalk = require('chalk'); const fs = require('fs'); const path = require('path'); class DevelopmentMonitor { constructor() { this.projectRoot = process.cwd(); this.monitorConfig = path.join(this.projectRoot, '.supernal-monitor.json'); } async checkGitHubCLI() { try { execSync('gh --version', { stdio: 'pipe' }); return true; } catch (error) { console.log(chalk.yellow('⚠️ GitHub CLI not found. Install with: brew install gh')); return false; } } async getWorkflowStatus() { if (!await this.checkGitHubCLI()) { return null; } try { const result = execSync('gh run list --limit 5 --json status,conclusion,name,createdAt,headBranch', { encoding: 'utf8', stdio: 'pipe' }); return JSON.parse(result); } catch (error) { console.log(chalk.red(`❌ Failed to get workflow status: ${error.message}`)); return null; } } async getBranchStatus() { try { // Get current branch const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim(); // Get ahead/behind status let aheadBehind = ''; try { aheadBehind = execSync(`git rev-list --left-right --count origin/main...HEAD`, { encoding: 'utf8' }).trim(); } catch (error) { // Might not have origin/main aheadBehind = '0\t0'; } const [behind, ahead] = aheadBehind.split('\t').map(n => parseInt(n)); return { branch: currentBranch, ahead, behind, hasUncommitted: execSync('git status --porcelain', { encoding: 'utf8' }).trim().length > 0 }; } catch (error) { return null; } } formatWorkflowStatus(workflows) { if (!workflows || workflows.length === 0) { return chalk.gray(' No recent workflows found'); } return workflows.slice(0, 3).map(workflow => { const status = workflow.status === 'completed' ? workflow.conclusion : workflow.status; const icon = this.getStatusIcon(status); const time = new Date(workflow.createdAt).toLocaleString(); const branch = workflow.headBranch || 'main'; return ` ${icon} ${workflow.name} (${branch}) - ${time}`; }).join('\n'); } getStatusIcon(status) { switch (status) { case 'success': return chalk.green('✅'); case 'failure': return chalk.red('❌'); case 'cancelled': return chalk.yellow('⚠️'); case 'in_progress': return chalk.blue('🔄'); case 'queued': return chalk.gray('⏳'); default: return chalk.gray('❓'); } } async showStatus() { console.log(chalk.blue('\n📊 Development Status Monitor')); console.log(chalk.blue('=' .repeat(50))); // Git status const gitStatus = await this.getBranchStatus(); if (gitStatus) { console.log(chalk.green('\n🌳 Git Status:')); console.log(` Branch: ${chalk.cyan(gitStatus.branch)}`); if (gitStatus.ahead > 0) { console.log(` Ahead: ${chalk.green(gitStatus.ahead)} commits`); } if (gitStatus.behind > 0) { console.log(` Behind: ${chalk.red(gitStatus.behind)} commits (git pull needed)`); } if (gitStatus.hasUncommitted) { console.log(` ${chalk.yellow('⚠️ Uncommitted changes detected')}`); } if (gitStatus.ahead === 0 && gitStatus.behind === 0 && !gitStatus.hasUncommitted) { console.log(` ${chalk.green('✅ Clean and up to date')}`); } } // GitHub workflows const workflows = await this.getWorkflowStatus(); console.log(chalk.green('\n🔄 Recent CI/CD Workflows:')); console.log(this.formatWorkflowStatus(workflows)); // Local build status console.log(chalk.green('\n🔨 Local Build Status:')); await this.checkLocalBuild(); } async checkLocalBuild() { try { // Check if we can run tests console.log(' Testing local build...'); execSync('npm test -- --passWithNoTests', { stdio: 'pipe' }); console.log(chalk.green(' ✅ Local tests pass')); } catch (error) { console.log(chalk.red(' ❌ Local tests failing')); console.log(chalk.gray(` Run: npm test for details`)); } // Check linting try { execSync('npm run lint', { stdio: 'pipe' }); console.log(chalk.green(' ✅ Linting passes')); } catch (error) { console.log(chalk.yellow(' ⚠️ Linting issues detected')); console.log(chalk.gray(` Run: npm run lint for details`)); } } async watchMode() { console.log(chalk.blue('👀 Starting continuous monitoring...')); console.log(chalk.gray('Press Ctrl+C to stop')); const interval = setInterval(async () => { process.stdout.write('\u001Bc'); // Clear screen await this.showStatus(); console.log(chalk.gray(`\n⏰ Last updated: ${new Date().toLocaleTimeString()}`)); console.log(chalk.gray('📊 Monitoring every 30 seconds...')); }, 30000); // Show initial status await this.showStatus(); console.log(chalk.gray(`\n⏰ Last updated: ${new Date().toLocaleTimeString()}`)); console.log(chalk.gray('📊 Monitoring every 30 seconds...')); // Handle Ctrl+C process.on('SIGINT', () => { clearInterval(interval); console.log(chalk.blue('\n👋 Monitoring stopped')); process.exit(0); }); } async setupAutoMonitor() { console.log(chalk.blue('🔧 Setting up automatic monitoring integration...')); // Create monitoring config const config = { enabled: true, checkOnCommit: true, checkOnPull: true, checkOnPush: true, alertOnFailure: true, lastCheck: new Date().toISOString() }; fs.writeFileSync(this.monitorConfig, JSON.stringify(config, null, 2)); console.log(chalk.green('✅ Auto-monitoring configured')); console.log(chalk.gray(' Status checks will now run automatically with git operations')); } async integrationCheck() { if (!fs.existsSync(this.monitorConfig)) { return; } const config = JSON.parse(fs.readFileSync(this.monitorConfig, 'utf8')); if (!config.enabled) { return; } // Quick status check for integration const workflows = await this.getWorkflowStatus(); if (workflows && workflows.length > 0) { const latest = workflows[0]; const status = latest.status === 'completed' ? latest.conclusion : latest.status; if (status === 'failure') { console.log(chalk.red('⚠️ Latest CI/CD workflow failed!')); console.log(chalk.gray(` Run: sc development monitor for details`)); } else if (status === 'success') { console.log(chalk.green('✅ Latest CI/CD workflow successful')); } } } } // CLI Interface async function main() { const args = process.argv.slice(2); const command = args[0]; const monitor = new DevelopmentMonitor(); switch (command) { case 'status': case undefined: await monitor.showStatus(); break; case 'watch': await monitor.watchMode(); break; case 'setup': await monitor.setupAutoMonitor(); break; case 'check': await monitor.integrationCheck(); break; default: console.log(chalk.blue('\n📊 Development Monitor Commands:')); console.log(''); console.log(' sc development monitor status Show current status'); console.log(' sc development monitor watch Continuous monitoring'); console.log(' sc development monitor setup Enable auto-monitoring'); console.log(' sc development monitor check Quick integration check'); console.log(''); } } // Export the class for use in other modules if (require.main === module) { main().catch(error => { console.error(chalk.red('❌ Monitor error:'), error.message); process.exit(1); }); } module.exports = DevelopmentMonitor;