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
JavaScript
// 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;