From 1a56ba1412b6a1e3ec7cd0d8a736663ae704c979 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Thu, 11 Dec 2025 23:46:21 +0900 Subject: [PATCH 01/18] feat: Implement comprehensive system health checks for #128 --- cortex/health/checks/disk.py | 33 ++++ cortex/health/checks/performance.py | 63 +++++++ cortex/health/checks/security.py | 57 ++++++ cortex/health/checks/updates.py | 56 ++++++ scripts/verify_ubuntu_compatibility.py | 245 +++++++++++++++++++++++++ 5 files changed, 454 insertions(+) create mode 100644 cortex/health/checks/disk.py create mode 100644 cortex/health/checks/performance.py create mode 100644 cortex/health/checks/security.py create mode 100644 cortex/health/checks/updates.py create mode 100644 scripts/verify_ubuntu_compatibility.py diff --git a/cortex/health/checks/disk.py b/cortex/health/checks/disk.py new file mode 100644 index 00000000..21dcc945 --- /dev/null +++ b/cortex/health/checks/disk.py @@ -0,0 +1,33 @@ +import shutil +from ..monitor import HealthCheck, CheckResult + +class DiskCheck(HealthCheck): + def run(self) -> CheckResult: + total, used, free = shutil.disk_usage("/") + # Calculate usage percentage + usage_percent = (used / total) * 100 + + score = 100 + status = "OK" + details = f"{usage_percent:.1f}% Used" + rec = None + + # Scoring logic (Spec compliant) + if usage_percent > 90: + score = 0 + status = "CRITICAL" + rec = "Clean package cache (+50 pts)" + elif usage_percent > 80: + score = 50 + status = "WARNING" + rec = "Clean package cache (+10 pts)" + + return CheckResult( + name="Disk Space", + category="disk", + score=score, + status=status, + details=details, + recommendation=rec, + weight=0.15 # 15% + ) \ No newline at end of file diff --git a/cortex/health/checks/performance.py b/cortex/health/checks/performance.py new file mode 100644 index 00000000..9e5e66f1 --- /dev/null +++ b/cortex/health/checks/performance.py @@ -0,0 +1,63 @@ +import os +import multiprocessing +from ..monitor import HealthCheck, CheckResult + +class PerformanceCheck(HealthCheck): + def run(self) -> CheckResult: + score = 100 + issues = [] + rec = None + + # 1. Load Average (1min) + try: + load1, _, _ = os.getloadavg() + cores = multiprocessing.cpu_count() + # Load ratio against core count + load_ratio = load1 / cores + + if load_ratio > 1.0: + score -= 50 + issues.append(f"High Load ({load1:.2f})") + rec = "Check top processes" + except Exception: + pass # Skip on Windows etc. + + # 2. Memory Usage (Linux /proc/meminfo) + try: + with open('/proc/meminfo', 'r') as f: + meminfo = {} + for line in f: + parts = line.split(':') + if len(parts) == 2: + meminfo[parts[0].strip()] = int(parts[1].strip().split()[0]) + + if 'MemTotal' in meminfo and 'MemAvailable' in meminfo: + total = meminfo['MemTotal'] + avail = meminfo['MemAvailable'] + used_percent = ((total - avail) / total) * 100 + + if used_percent > 80: + penalty = int(used_percent - 80) + score -= penalty + issues.append(f"High Memory ({used_percent:.0f}%)") + except FileNotFoundError: + pass # Non-Linux systems + + # Summary of results + status = "OK" + if score < 50: + status = "CRITICAL" + elif score < 90: + status = "WARNING" + + details = ", ".join(issues) if issues else "Optimal" + + return CheckResult( + name="System Load", + category="performance", + score=max(0, score), + status=status, + details=details, + recommendation=rec, + weight=0.20 # 20% + ) \ No newline at end of file diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py new file mode 100644 index 00000000..7e21afb2 --- /dev/null +++ b/cortex/health/checks/security.py @@ -0,0 +1,57 @@ +import subprocess +import os +from ..monitor import HealthCheck, CheckResult + +class SecurityCheck(HealthCheck): + def run(self) -> CheckResult: + score = 100 + issues = [] + recommendations = [] + + # 1. Firewall (UFW) Check + ufw_active = False + try: + res = subprocess.run( + ["systemctl", "is-active", "ufw"], + capture_output=True, text=True + ) + # Fix: Use exact match to avoid matching "inactive" which contains "active" + if res.returncode == 0 and res.stdout.strip() == "active": + ufw_active = True + except FileNotFoundError: + pass # Environment without systemctl (e.g., Docker or non-systemd) + + if not ufw_active: + score = 0 # Spec: 0 points if Firewall is inactive + issues.append("Firewall Inactive") + recommendations.append("Enable UFW Firewall") + + # 2. SSH Root Login Check + try: + ssh_config = "/etc/ssh/sshd_config" + if os.path.exists(ssh_config): + with open(ssh_config, 'r') as f: + for line in f: + line = line.strip() + # Check for uncommented PermitRootLogin yes + if line.startswith("PermitRootLogin") and "yes" in line.split(): + score -= 50 + issues.append("Root SSH Allowed") + recommendations.append("Disable SSH Root Login in sshd_config") + break + except PermissionError: + pass # Cannot read config, skip check + + status = "OK" + if score < 50: status = "CRITICAL" + elif score < 100: status = "WARNING" + + return CheckResult( + name="Security Posture", + category="security", + score=max(0, score), + status=status, + details=", ".join(issues) if issues else "Secure", + recommendation=", ".join(recommendations) if recommendations else None, + weight=0.35 + ) \ No newline at end of file diff --git a/cortex/health/checks/updates.py b/cortex/health/checks/updates.py new file mode 100644 index 00000000..27c01cbe --- /dev/null +++ b/cortex/health/checks/updates.py @@ -0,0 +1,56 @@ +import subprocess +from ..monitor import HealthCheck, CheckResult + +class UpdateCheck(HealthCheck): + def run(self) -> CheckResult: + score = 100 + pkg_count = 0 + sec_count = 0 + rec = None + + # Parse apt list --upgradable + try: + # Execute safely without pipeline + res = subprocess.run( + ["apt", "list", "--upgradable"], + capture_output=True, text=True + ) + + lines = res.stdout.splitlines() + # Skip first line "Listing..." + for line in lines[1:]: + if line.strip(): + pkg_count += 1 + if "security" in line.lower(): + sec_count += 1 + + # Scoring + score -= (pkg_count * 2) # -2 pts per normal package + score -= (sec_count * 10) # -10 pts per security package + + if pkg_count > 0: + rec = f"Install {pkg_count} updates (+{100-score} pts)" + + except FileNotFoundError: + # Skip on non-apt environments (100 pts) + return CheckResult("Updates", "updates", 100, "SKIP", "apt not found", weight=0.30) + except Exception: + pass # Ignore errors + + status = "OK" + if score < 60: status = "CRITICAL" + elif score < 100: status = "WARNING" + + details = f"{pkg_count} pending" + if sec_count > 0: + details += f" ({sec_count} security)" + + return CheckResult( + name="System Updates", + category="updates", + score=max(0, score), + status=status, + details=details, + recommendation=rec, + weight=0.30 # 30% + ) \ No newline at end of file diff --git a/scripts/verify_ubuntu_compatibility.py b/scripts/verify_ubuntu_compatibility.py new file mode 100644 index 00000000..dca1c0bf --- /dev/null +++ b/scripts/verify_ubuntu_compatibility.py @@ -0,0 +1,245 @@ +import subprocess +import os +import sys +import json +import datetime +import shutil + +# File name to store history data +HISTORY_FILE = "security_history.json" + +def load_history(): + """Load past execution history""" + if os.path.exists(HISTORY_FILE): + try: + with open(HISTORY_FILE, 'r') as f: + return json.load(f) + except json.JSONDecodeError: + return [] + return [] + +def save_history(score, status, details): + """Save execution result to history""" + history = load_history() + record = { + "timestamp": datetime.datetime.now().isoformat(), + "score": score, + "status": status, + "details": details + } + history.append(record) + # Keep only the latest 10 records + history = history[-10:] + + with open(HISTORY_FILE, 'w') as f: + json.dump(history, f, indent=4) + + return history + +def show_trend(history): + """Show historical trend (Trend Tracking)""" + print("\n=== šŸ“Š Historical Trend Analysis ===") + if not history: + print(" No historical data available yet.") + return + + scores = [h["score"] for h in history] + avg_score = sum(scores) / len(scores) + last_score = scores[-1] + + print(f" History Count: {len(history)} runs") + print(f" Average Score: {avg_score:.1f}") + print(f" Last Run Score: {last_score}") + + if len(scores) > 1: + prev_score = scores[-2] + diff = last_score - prev_score + if diff > 0: + print(f" Trend: šŸ“ˆ Improved by {diff} points since previous run") + elif diff < 0: + print(f" Trend: šŸ“‰ Dropped by {abs(diff)} points since previous run") + else: + print(f" Trend: āž”ļø Stable") + +def fix_firewall(): + """Enable Firewall (Automated Fix)""" + print("\n [Fixing] Enabling UFW Firewall...") + + # Check if ufw is installed using 'which' or checking path + # (Since sudo is used, we check if we can find ufw path) + if not shutil.which("ufw") and not os.path.exists("/usr/sbin/ufw"): + print(" -> āš ļø UFW is not installed. Cannot enable.") + print(" (Try: sudo apt install ufw)") + return False + + try: + # Depends on execution environment, sudo might be required + subprocess.run(["sudo", "ufw", "enable"], check=True) + print(" -> āœ… Success: Firewall enabled.") + return True + except subprocess.CalledProcessError as e: + print(f" -> āŒ Failed to enable firewall: {e}") + return False + +def fix_ssh_config(config_path): + """Disable SSH Root Login (Automated Fix)""" + print(f"\n [Fixing] Disabling Root Login in {config_path}...") + + # Check if file exists before trying to fix + if not os.path.exists(config_path): + print(f" -> āš ļø Config file not found: {config_path}") + return False + + # 1. Create backup + backup_path = config_path + ".bak." + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + try: + shutil.copy2(config_path, backup_path) + print(f" -> Backup created at: {backup_path}") + except PermissionError: + print(" -> āŒ Failed to create backup (Permission denied). Need sudo?") + return False + + # 2. Rewrite configuration + try: + new_lines = [] + with open(config_path, 'r') as f: + lines = f.readlines() + + fixed = False + for line in lines: + if line.strip().startswith("PermitRootLogin") and "yes" in line: + # Comment out and add disabled setting + new_lines.append(f"# {line.strip()} (Disabled by Auto-Fix)\n") + new_lines.append("PermitRootLogin no\n") + fixed = True + else: + new_lines.append(line) + + if fixed: + with open(config_path, 'w') as f: + f.writelines(new_lines) + print(" -> āœ… Success: sshd_config updated.") + + # Attempt to restart SSH service + print(" -> Restarting sshd service...") + subprocess.run(["sudo", "systemctl", "restart", "ssh"], check=False) + return True + else: + print(" -> No changes needed.") + return True + + except PermissionError: + print(" -> āŒ Failed to write config (Permission denied). Need sudo?") + return False + except Exception as e: + print(f" -> āŒ Error during fix: {e}") + return False + +def verify_security_logic(): + print("=== Ubuntu Security Logic Verification ===") + + # --------------------------------------------------------- + # 1. Firewall (UFW) Check Logic + # --------------------------------------------------------- + print("\n[1] Checking Firewall (UFW)...") + ufw_active = False + ufw_needs_fix = False + try: + print(" Running: systemctl is-active ufw") + res = subprocess.run( + ["systemctl", "is-active", "ufw"], + capture_output=True, text=True + ) + output = res.stdout.strip() + print(f" Output: '{output}'") + + if res.returncode == 0 and output == "active": + ufw_active = True + print(" -> JUDGEMENT: Firewall is ACTIVE (Score: 100)") + else: + print(" -> JUDGEMENT: Firewall is INACTIVE (Score: 0)") + ufw_needs_fix = True + + except FileNotFoundError: + print(" -> ERROR: 'systemctl' command not found.") + except Exception as e: + print(f" -> ERROR: {e}") + + # --------------------------------------------------------- + # 2. SSH Root Login Check Logic + # --------------------------------------------------------- + print("\n[2] Checking SSH Configuration...") + ssh_config = "/etc/ssh/sshd_config" + score_penalty = 0 + ssh_needs_fix = False + + if os.path.exists(ssh_config): + print(f" File found: {ssh_config}") + try: + with open(ssh_config, 'r') as f: + found_risky_setting = False + for line in f: + if line.strip().startswith("PermitRootLogin") and "yes" in line: + print(f" -> FOUND RISKY LINE: {line.strip()}") + score_penalty = 50 + found_risky_setting = True + ssh_needs_fix = True + break + + if not found_risky_setting: + print(" -> No 'PermitRootLogin yes' found (Safe)") + + except PermissionError: + print(" -> ERROR: Permission denied. Try running with 'sudo'.") + else: + print(f" -> WARNING: {ssh_config} does not exist.") + + # --------------------------------------------------------- + # Final Report & History + # --------------------------------------------------------- + print("\n=== Summary ===") + final_score = 100 + if not ufw_active: + final_score = 0 + final_score -= score_penalty + final_score = max(0, final_score) + + status = "OK" + if final_score < 50: status = "CRITICAL" + elif final_score < 100: status = "WARNING" + + print(f"Current Score: {final_score}") + print(f"Status: {status}") + + # --- Trend Tracking --- + print("\n... Saving history ...") + details = [] + if ufw_needs_fix: details.append("Firewall Inactive") + if ssh_needs_fix: details.append("Root SSH Allowed") + + history = save_history(final_score, status, ", ".join(details)) + show_trend(history) + + # --------------------------------------------------------- + # Automated Fixes (Interactive) + # --------------------------------------------------------- + if ufw_needs_fix or ssh_needs_fix: + print("\n=== šŸ› ļø Automated Fixes Available ===") + print("Issues detected that can be automatically fixed.") + user_input = input("Do you want to apply fixes now? (y/n): ").strip().lower() + + if user_input == 'y': + if ufw_needs_fix: + fix_firewall() + if ssh_needs_fix: + fix_ssh_config(ssh_config) + + print("\nāœ… Fixes attempt complete. Please re-run script to verify.") + else: + print("Skipping fixes.") + +if __name__ == "__main__": + # Warn that sudo might be required for execution + if os.geteuid() != 0: + print("NOTE: This script works best with 'sudo' for fixing issues.") + verify_security_logic() \ No newline at end of file From dc54e53dbb94348ffd742719a94bcd4abd8545f4 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Thu, 11 Dec 2025 23:48:31 +0900 Subject: [PATCH 02/18] feat: Add health monitor core logic, CLI integration, and unit tests --- cortex/cli.py | 99 ++++++++++++++++++++++--- cortex/health/__init__.py | 0 cortex/health/monitor.py | 106 +++++++++++++++++++++++++++ tests/test_health_monitor.py | 137 +++++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+), 12 deletions(-) create mode 100644 cortex/health/__init__.py create mode 100644 cortex/health/monitor.py create mode 100644 tests/test_health_monitor.py diff --git a/cortex/cli.py b/cortex/cli.py index b1cf1421..e5d5a3db 100644 --- a/cortex/cli.py +++ b/cortex/cli.py @@ -29,6 +29,7 @@ ) + class CortexCLI: def __init__(self, verbose: bool = False): self.spinner_chars = ["ā ‹", "ā ™", "ā ¹", "ā ø", "ā ¼", "ā “", "ā ¦", "ā §", "ā ‡", "ā "] @@ -100,10 +101,9 @@ def _clear_line(self): sys.stdout.write("\r\033[K") sys.stdout.flush() - # --- New Notification Method --- + # --- Notification Method --- def notify(self, args): """Handle notification commands""" - # Addressing CodeRabbit feedback: Handle missing subcommand gracefully if not args.notify_action: self._print_error("Please specify a subcommand (config/enable/disable/dnd/send)") return 1 @@ -126,16 +126,14 @@ def notify(self, args): elif args.notify_action == "enable": mgr.config["enabled"] = True - # Addressing CodeRabbit feedback: Ideally should use a public method instead of private _save_config, - # but keeping as is for a simple fix (or adding a save method to NotificationManager would be best). - mgr._save_config() + mgr.save_config() self._print_success("Notifications enabled") return 0 elif args.notify_action == "disable": mgr.config["enabled"] = False - mgr._save_config() - cx_print("Notifications disabled (Critical alerts will still show)", "warning") + mgr.save_config() + self._print_success("Notifications disabled (Critical alerts will still show)") return 0 elif args.notify_action == "dnd": @@ -143,7 +141,7 @@ def notify(self, args): self._print_error("Please provide start and end times (HH:MM)") return 1 - # Addressing CodeRabbit feedback: Add time format validation + # Add time format validation try: datetime.strptime(args.start, "%H:%M") datetime.strptime(args.end, "%H:%M") @@ -153,7 +151,7 @@ def notify(self, args): mgr.config["dnd_start"] = args.start mgr.config["dnd_end"] = args.end - mgr._save_config() + mgr.save_config() self._print_success(f"DND Window updated: {args.start} - {args.end}") return 0 @@ -169,6 +167,78 @@ def notify(self, args): self._print_error("Unknown notify command") return 1 + # --- Health Command --- + def health(self, args): + """Run system health checks and show recommendations. + + Args: + args: Parsed command line arguments. + + Returns: + int: 0 on success, 1 on failure. + """ + try: + from cortex.health.monitor import HealthMonitor + + self._print_status("šŸ”", "Running system health checks...") + monitor = HealthMonitor() + report = monitor.run_all() + + # --- Display Results --- + score = report["total_score"] + + # Color code the score + score_color = "green" + if score < 60: + score_color = "red" + elif score < 80: + score_color = "yellow" + + console.print() + console.print( + f"šŸ“Š [bold]System Health Score:[/bold] [{score_color}]{score}/100[/{score_color}]" + ) + console.print() + + console.print("[bold]Factors:[/bold]") + recommendations = [] + + for res in report["results"]: + status_icon = "āœ…" + if res["status"] == "WARNING": + status_icon = "āš ļø " + elif res["status"] == "CRITICAL": + status_icon = "āŒ" + + console.print( + f" {status_icon} {res['name']:<15}: {res['score']}/100 ({res['details']})" + ) + + if res["recommendation"]: + recommendations.append(res["recommendation"]) + + console.print() + + if recommendations: + console.print("[bold]Recommendations:[/bold]") + for i, rec in enumerate(recommendations, 1): + console.print(f" {i}. {rec}") + + console.print() + console.print( + "[dim]Run suggested commands manually to improve your score.[/dim]" + ) + else: + self._print_success("System is in excellent health! No actions needed.") + + return 0 + except ImportError as e: + self._print_error(f"Health module not available: {e}") + return 1 + except Exception as e: + self._print_error(f"Health check failed: {e}") + return 1 + # ------------------------------- def stack(self, args: argparse.Namespace) -> int: @@ -686,9 +756,10 @@ def show_rich_help(): table.add_row("install ", "Install software") table.add_row("history", "View history") table.add_row("rollback ", "Undo installation") - table.add_row("notify", "Manage desktop notifications") # Added this line + table.add_row("notify", "Manage desktop notifications") table.add_row("cache stats", "Show LLM cache statistics") table.add_row("stack ", "Install the stack") + table.add_row("health", "Check system health score") table.add_row("doctor", "System health check") console.print(table) @@ -766,7 +837,7 @@ def main(): edit_pref_parser.add_argument("key", nargs="?") edit_pref_parser.add_argument("value", nargs="?") - # --- New Notify Command --- + # --- Notify Command --- notify_parser = subparsers.add_parser("notify", help="Manage desktop notifications") notify_subs = notify_parser.add_subparsers(dest="notify_action", help="Notify actions") @@ -783,6 +854,9 @@ def main(): send_parser.add_argument("--title", default="Cortex Notification") send_parser.add_argument("--level", choices=["low", "normal", "critical"], default="normal") send_parser.add_argument("--actions", nargs="*", help="Action buttons") + + # --- Health Command --- + subparsers.add_parser("health", help="Check system health score") # -------------------------- # Stack command @@ -827,9 +901,10 @@ def main(): return cli.check_pref(key=args.key) elif args.command == "edit-pref": return cli.edit_pref(action=args.action, key=args.key, value=args.value) - # Handle the new notify command elif args.command == "notify": return cli.notify(args) + elif args.command == "health": + return cli.health(args) elif args.command == "stack": return cli.stack(args) elif args.command == "doctor": diff --git a/cortex/health/__init__.py b/cortex/health/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cortex/health/monitor.py b/cortex/health/monitor.py new file mode 100644 index 00000000..c85b6246 --- /dev/null +++ b/cortex/health/monitor.py @@ -0,0 +1,106 @@ +import json +import time +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from pathlib import Path +from typing import List, Dict, Optional +from rich.console import Console + +console = Console() + +@dataclass +class CheckResult: + """Data class to hold the result of each check""" + name: str # Item name (e.g. "Disk Space") + category: str # Category (security, updates, performance, disk) + score: int # Score 0-100 + status: str # "OK", "WARNING", "CRITICAL" + details: str # Detailed message + recommendation: Optional[str] = None # Recommended action (if any) + weight: float = 1.0 # Weight for weighted average + +class HealthCheck(ABC): + """Base class inherited by all health check modules""" + @abstractmethod + def run(self) -> CheckResult: + pass + +class HealthMonitor: + """ + Main engine for system health monitoring. + """ + def __init__(self): + self.history_file = Path.home() / ".cortex" / "health_history.json" + self.history_file.parent.mkdir(exist_ok=True) + self.checks: List[HealthCheck] = [] + + # Register each check here + # (Import here to prevent circular references) + from .checks.security import SecurityCheck + from .checks.updates import UpdateCheck + from .checks.performance import PerformanceCheck + from .checks.disk import DiskCheck + + self.register_check(SecurityCheck()) + self.register_check(UpdateCheck()) + self.register_check(PerformanceCheck()) + self.register_check(DiskCheck()) + + def register_check(self, check: HealthCheck): + self.checks.append(check) + + def run_all(self) -> Dict: + results = [] + total_weighted_score = 0 + total_weight = 0 + + for check in self.checks: + try: + result = check.run() + results.append(result) + total_weighted_score += result.score * result.weight + total_weight += result.weight + except Exception as e: + console.print(f"[red]Error running check {check.__class__.__name__}: {e}[/red]") + + final_score = 0 + if total_weight > 0: + final_score = int(total_weighted_score / total_weight) + + report = { + "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"), + "total_score": final_score, + "results": [ + { + "name": r.name, + "category": r.category, + "score": r.score, + "status": r.status, + "details": r.details, + "recommendation": r.recommendation + } + for r in results + ] + } + + self._save_history(report) + return report + + def _save_history(self, report: Dict): + history = [] + if self.history_file.exists(): + try: + with open(self.history_file, 'r') as f: + history = json.load(f) + except json.JSONDecodeError: + pass + + history.append(report) + history = history[-100:] + + with open(self.history_file, 'w') as f: + json.dump(history, f, indent=4) + +if __name__ == "__main__": + # For testing execution + print("HealthMonitor initialized.") \ No newline at end of file diff --git a/tests/test_health_monitor.py b/tests/test_health_monitor.py new file mode 100644 index 00000000..d352f0d4 --- /dev/null +++ b/tests/test_health_monitor.py @@ -0,0 +1,137 @@ +import unittest +from unittest.mock import patch, MagicMock, mock_open +from cortex.health.monitor import HealthMonitor, CheckResult +from cortex.health.checks.disk import DiskCheck +from cortex.health.checks.performance import PerformanceCheck +from cortex.health.checks.security import SecurityCheck +from cortex.health.checks.updates import UpdateCheck + +class TestDiskCheck(unittest.TestCase): + @patch('shutil.disk_usage') + def test_disk_usage_scoring(self, mock_usage): + # Case 1: Healthy (50% used) -> 100 pts + # total=100, used=50, free=50 + mock_usage.return_value = (100, 50, 50) + check = DiskCheck() + result = check.run() + self.assertEqual(result.score, 100) + self.assertEqual(result.status, "OK") + + # Case 2: Warning (85% used) -> 50 pts + mock_usage.return_value = (100, 85, 15) + result = check.run() + self.assertEqual(result.score, 50) + self.assertEqual(result.status, "WARNING") + + # Case 3: Critical (95% used) -> 0 pts + mock_usage.return_value = (100, 95, 5) + result = check.run() + self.assertEqual(result.score, 0) + self.assertEqual(result.status, "CRITICAL") + +class TestPerformanceCheck(unittest.TestCase): + @patch('os.getloadavg') + @patch('multiprocessing.cpu_count') + def test_load_average(self, mock_cpu, mock_load): + # Case 1: Load OK (Load 2.0 / 4 Cores = 0.5 ratio) + mock_cpu.return_value = 4 + mock_load.return_value = (2.0, 2.0, 2.0) + + # Mock reading /proc/meminfo (Normal case) + mem_data = "MemTotal: 1000 kB\nMemAvailable: 500 kB\n" + with patch('builtins.open', mock_open(read_data=mem_data)): + check = PerformanceCheck() + result = check.run() + self.assertEqual(result.score, 100) # No penalty + + @patch('os.getloadavg') + @patch('multiprocessing.cpu_count') + def test_high_load_penalty(self, mock_cpu, mock_load): + # Case 2: High Load (Load 5.0 / 4 Cores = 1.25 ratio) -> -50 pts + mock_cpu.return_value = 4 + mock_load.return_value = (5.0, 5.0, 5.0) + + # Assume memory is normal + mem_data = "MemTotal: 1000 kB\nMemAvailable: 500 kB\n" + with patch('builtins.open', mock_open(read_data=mem_data)): + check = PerformanceCheck() + result = check.run() + self.assertEqual(result.score, 50) # 100 - 50 = 50 + +class TestSecurityCheck(unittest.TestCase): + @patch('subprocess.run') + def test_ufw_status(self, mock_run): + # Case 1: UFW Inactive -> 0 pts + mock_run.return_value.stdout = "inactive" + mock_run.return_value.returncode = 0 + + check = SecurityCheck() + result = check.run() + self.assertEqual(result.score, 0) + self.assertIn("Firewall Inactive", result.details) + + @patch('subprocess.run') + def test_ufw_active(self, mock_run): + # Case 2: UFW Active -> 100 pts (SSH config is safe by default mock) + mock_run.return_value.stdout = "active" + mock_run.return_value.returncode = 0 + + # Test error handling when sshd_config does not exist + with patch('os.path.exists', return_value=False): + check = SecurityCheck() + result = check.run() + self.assertEqual(result.score, 100) + +class TestUpdateCheck(unittest.TestCase): + @patch('subprocess.run') + def test_apt_updates(self, mock_run): + # Mock output for apt list --upgradable + # Ignore first line, packages start from 2nd line + apt_output = """Listing... Done +package1/stable 1.0.0 amd64 [upgradable from: 0.9.9] +package2/stable 2.0.0 amd64 [upgradable from: 1.9.9] +security-pkg/stable 1.0.1 amd64 [upgradable from: 1.0.0] - Security Update +""" + mock_run.return_value.stdout = apt_output + mock_run.return_value.returncode = 0 + + check = UpdateCheck() + result = check.run() + + # Calculation: + # Total packages: 3 + # Security packages: 1 (line containing "security") + # Penalty: (3 * 2) + (1 * 10) = 6 + 10 = 16 pts + # Expected score: 100 - 16 = 84 pts + + self.assertEqual(result.score, 84) + self.assertIn("3 pending", result.details) + +class TestHealthMonitor(unittest.TestCase): + def test_monitor_aggregation(self): + monitor = HealthMonitor() + # Register mock checks instead of real check classes + + mock_check1 = MagicMock() + mock_check1.run.return_value = CheckResult( + name="Check1", category="test", score=100, status="OK", details="", weight=0.5 + ) + + mock_check2 = MagicMock() + mock_check2.run.return_value = CheckResult( + name="Check2", category="test", score=0, status="CRITICAL", details="", weight=0.5 + ) + + monitor.checks = [mock_check1, mock_check2] + + # Mock history saving to prevent file write + with patch.object(monitor, '_save_history'): + report = monitor.run_all() + + # Weighted average calculation: + # (100 * 0.5) + (0 * 0.5) = 50 / (0.5 + 0.5) = 50 pts + self.assertEqual(report['total_score'], 50) + self.assertEqual(len(report['results']), 2) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 0889c76318e18a6626c07ce5e0a6f4e86406c211 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Thu, 11 Dec 2025 23:57:14 +0900 Subject: [PATCH 03/18] fix: Add timeouts to subprocess calls to improve reliability --- cortex/health/checks/security.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index 7e21afb2..c7313197 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -11,15 +11,22 @@ def run(self) -> CheckResult: # 1. Firewall (UFW) Check ufw_active = False try: + # Add timeout to prevent hanging (Fixes Reliability Issue) res = subprocess.run( ["systemctl", "is-active", "ufw"], - capture_output=True, text=True + capture_output=True, + text=True, + timeout=5 ) # Fix: Use exact match to avoid matching "inactive" which contains "active" if res.returncode == 0 and res.stdout.strip() == "active": ufw_active = True + except subprocess.TimeoutExpired: + pass # Command timed out, treat as inactive or unavailable except FileNotFoundError: pass # Environment without systemctl (e.g., Docker or non-systemd) + except Exception: + pass # Generic error protection if not ufw_active: score = 0 # Spec: 0 points if Firewall is inactive @@ -41,6 +48,8 @@ def run(self) -> CheckResult: break except PermissionError: pass # Cannot read config, skip check + except Exception: + pass # Generic error protection status = "OK" if score < 50: status = "CRITICAL" From fd88be025d42a07618bce1760cc78eabaffa993e Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Fri, 12 Dec 2025 00:01:28 +0900 Subject: [PATCH 04/18] refactor: Address code review feedback (docstrings, timeouts, complexity) --- cortex/health/checks/disk.py | 28 ++++++---- cortex/health/checks/security.py | 88 ++++++++++++++++++-------------- cortex/health/checks/updates.py | 49 +++++++++--------- 3 files changed, 92 insertions(+), 73 deletions(-) diff --git a/cortex/health/checks/disk.py b/cortex/health/checks/disk.py index 21dcc945..bd126d7f 100644 --- a/cortex/health/checks/disk.py +++ b/cortex/health/checks/disk.py @@ -2,32 +2,38 @@ from ..monitor import HealthCheck, CheckResult class DiskCheck(HealthCheck): + """Check root filesystem disk usage.""" + def run(self) -> CheckResult: - total, used, free = shutil.disk_usage("/") - # Calculate usage percentage + """ + Calculate disk usage percentage. + + Returns: + CheckResult based on usage thresholds. + """ + # Use _ for unused variable (free space) + total, used, _ = shutil.disk_usage("/") usage_percent = (used / total) * 100 score = 100 status = "OK" - details = f"{usage_percent:.1f}% Used" rec = None - - # Scoring logic (Spec compliant) + if usage_percent > 90: score = 0 status = "CRITICAL" - rec = "Clean package cache (+50 pts)" + rec = "Clean up disk space immediately" elif usage_percent > 80: score = 50 status = "WARNING" - rec = "Clean package cache (+10 pts)" - + rec = "Consider cleaning up disk space" + return CheckResult( - name="Disk Space", + name="Disk Usage", category="disk", score=score, status=status, - details=details, + details=f"{usage_percent:.1f}% used", recommendation=rec, - weight=0.15 # 15% + weight=0.20 ) \ No newline at end of file diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index c7313197..0b59c674 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -3,53 +3,32 @@ from ..monitor import HealthCheck, CheckResult class SecurityCheck(HealthCheck): + """Check security configuration including firewall and SSH settings.""" + def run(self) -> CheckResult: + """ + Run security checks for firewall status and SSH configuration. + + Returns: + CheckResult with security score based on detected issues. + """ score = 100 issues = [] recommendations = [] # 1. Firewall (UFW) Check - ufw_active = False - try: - # Add timeout to prevent hanging (Fixes Reliability Issue) - res = subprocess.run( - ["systemctl", "is-active", "ufw"], - capture_output=True, - text=True, - timeout=5 - ) - # Fix: Use exact match to avoid matching "inactive" which contains "active" - if res.returncode == 0 and res.stdout.strip() == "active": - ufw_active = True - except subprocess.TimeoutExpired: - pass # Command timed out, treat as inactive or unavailable - except FileNotFoundError: - pass # Environment without systemctl (e.g., Docker or non-systemd) - except Exception: - pass # Generic error protection - + ufw_active, ufw_issue, ufw_rec = self._check_firewall() if not ufw_active: - score = 0 # Spec: 0 points if Firewall is inactive - issues.append("Firewall Inactive") - recommendations.append("Enable UFW Firewall") + score = 0 + issues.append(ufw_issue) + recommendations.append(ufw_rec) # 2. SSH Root Login Check - try: - ssh_config = "/etc/ssh/sshd_config" - if os.path.exists(ssh_config): - with open(ssh_config, 'r') as f: - for line in f: - line = line.strip() - # Check for uncommented PermitRootLogin yes - if line.startswith("PermitRootLogin") and "yes" in line.split(): - score -= 50 - issues.append("Root SSH Allowed") - recommendations.append("Disable SSH Root Login in sshd_config") - break - except PermissionError: - pass # Cannot read config, skip check - except Exception: - pass # Generic error protection + ssh_penalty, ssh_issue, ssh_rec = self._check_ssh_root_login() + if ssh_penalty > 0: + score -= ssh_penalty + issues.append(ssh_issue) + recommendations.append(ssh_rec) status = "OK" if score < 50: status = "CRITICAL" @@ -63,4 +42,35 @@ def run(self) -> CheckResult: details=", ".join(issues) if issues else "Secure", recommendation=", ".join(recommendations) if recommendations else None, weight=0.35 - ) \ No newline at end of file + ) + + def _check_firewall(self): + """Check if UFW is active.""" + try: + res = subprocess.run( + ["systemctl", "is-active", "ufw"], + capture_output=True, + text=True, + timeout=10 + ) + if res.returncode == 0 and res.stdout.strip() == "active": + return True, None, None + except (subprocess.TimeoutExpired, FileNotFoundError, Exception): + pass + + return False, "Firewall Inactive", "Enable UFW Firewall" + + def _check_ssh_root_login(self): + """Check for PermitRootLogin yes in sshd_config.""" + try: + ssh_config = "/etc/ssh/sshd_config" + if os.path.exists(ssh_config): + with open(ssh_config, 'r') as f: + for line in f: + line = line.strip() + if line.startswith("PermitRootLogin") and "yes" in line.split(): + return 50, "Root SSH Allowed", "Disable SSH Root Login in sshd_config" + except (PermissionError, Exception): + pass + + return 0, None, None \ No newline at end of file diff --git a/cortex/health/checks/updates.py b/cortex/health/checks/updates.py index 27c01cbe..d40b2519 100644 --- a/cortex/health/checks/updates.py +++ b/cortex/health/checks/updates.py @@ -2,48 +2,51 @@ from ..monitor import HealthCheck, CheckResult class UpdateCheck(HealthCheck): + """Check for pending system updates and security patches.""" + def run(self) -> CheckResult: + """ + Check for available updates using apt. + + Returns: + CheckResult with score based on pending updates. + """ score = 100 pkg_count = 0 sec_count = 0 - rec = None - # Parse apt list --upgradable try: - # Execute safely without pipeline + # Add timeout to prevent hangs res = subprocess.run( ["apt", "list", "--upgradable"], - capture_output=True, text=True + capture_output=True, + text=True, + timeout=30 ) - lines = res.stdout.splitlines() - # Skip first line "Listing..." + + # apt list output header usually takes first line for line in lines[1:]: if line.strip(): - pkg_count += 1 if "security" in line.lower(): sec_count += 1 + else: + pkg_count += 1 # Scoring - score -= (pkg_count * 2) # -2 pts per normal package - score -= (sec_count * 10) # -10 pts per security package - - if pkg_count > 0: - rec = f"Install {pkg_count} updates (+{100-score} pts)" + score -= (pkg_count * 2) + score -= (sec_count * 10) - except FileNotFoundError: - # Skip on non-apt environments (100 pts) - return CheckResult("Updates", "updates", 100, "SKIP", "apt not found", weight=0.30) except Exception: - pass # Ignore errors + pass status = "OK" - if score < 60: status = "CRITICAL" - elif score < 100: status = "WARNING" + if score < 50: status = "CRITICAL" + elif score < 90: status = "WARNING" - details = f"{pkg_count} pending" - if sec_count > 0: - details += f" ({sec_count} security)" + details = f"{pkg_count} packages, {sec_count} security updates pending" + if pkg_count == 0 and sec_count == 0: + details = "System up to date" return CheckResult( name="System Updates", @@ -51,6 +54,6 @@ def run(self) -> CheckResult: score=max(0, score), status=status, details=details, - recommendation=rec, - weight=0.30 # 30% + recommendation="Run 'apt upgrade'" if score < 100 else None, + weight=0.25 ) \ No newline at end of file From f4ced4395c8e8a7733fb48764466674cf6a8f645 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Fri, 12 Dec 2025 00:06:21 +0900 Subject: [PATCH 05/18] refactor: Improve security check complexity and SSH parsing logic --- cortex/health/checks/security.py | 68 +++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index 0b59c674..0f80fbb6 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -3,32 +3,41 @@ from ..monitor import HealthCheck, CheckResult class SecurityCheck(HealthCheck): - """Check security configuration including firewall and SSH settings.""" + """ + Checks system security posture including firewall status and SSH configuration. + + Evaluates UFW firewall activity and SSH root login permissions, + returning a weighted score and actionable recommendations. + """ def run(self) -> CheckResult: """ - Run security checks for firewall status and SSH configuration. + Execute security checks and return aggregated results. Returns: - CheckResult with security score based on detected issues. + CheckResult: Security assessment with score (0-100), status, + detected issues, and recommendations. """ score = 100 issues = [] recommendations = [] # 1. Firewall (UFW) Check - ufw_active, ufw_issue, ufw_rec = self._check_firewall() - if not ufw_active: - score = 0 - issues.append(ufw_issue) - recommendations.append(ufw_rec) + # Returns: score_delta (negative for penalty), issues, recommendations + fw_score_delta, fw_issues, fw_recs = self._check_firewall() + + # If firewall is inactive, score becomes 0 immediately per requirements + if fw_score_delta == -100: + score = 0 + + issues.extend(fw_issues) + recommendations.extend(fw_recs) # 2. SSH Root Login Check - ssh_penalty, ssh_issue, ssh_rec = self._check_ssh_root_login() - if ssh_penalty > 0: - score -= ssh_penalty - issues.append(ssh_issue) - recommendations.append(ssh_rec) + ssh_score_delta, ssh_issues, ssh_recs = self._check_ssh_root_login() + score += ssh_score_delta + issues.extend(ssh_issues) + recommendations.extend(ssh_recs) status = "OK" if score < 50: status = "CRITICAL" @@ -44,8 +53,13 @@ def run(self) -> CheckResult: weight=0.35 ) - def _check_firewall(self): - """Check if UFW is active.""" + def _check_firewall(self) -> tuple[int, list[str], list[str]]: + """ + Check if UFW is active. + + Returns: + tuple: (score_delta, issues_list, recommendations_list) + """ try: res = subprocess.run( ["systemctl", "is-active", "ufw"], @@ -54,23 +68,31 @@ def _check_firewall(self): timeout=10 ) if res.returncode == 0 and res.stdout.strip() == "active": - return True, None, None + return 0, [], [] except (subprocess.TimeoutExpired, FileNotFoundError, Exception): pass - return False, "Firewall Inactive", "Enable UFW Firewall" + # Return -100 to signal immediate failure condition + return -100, ["Firewall Inactive"], ["Enable UFW Firewall"] - def _check_ssh_root_login(self): - """Check for PermitRootLogin yes in sshd_config.""" + def _check_ssh_root_login(self) -> tuple[int, list[str], list[str]]: + """ + Check for PermitRootLogin yes in sshd_config. + + Returns: + tuple: (score_delta, issues_list, recommendations_list) + """ try: ssh_config = "/etc/ssh/sshd_config" if os.path.exists(ssh_config): with open(ssh_config, 'r') as f: for line in f: - line = line.strip() - if line.startswith("PermitRootLogin") and "yes" in line.split(): - return 50, "Root SSH Allowed", "Disable SSH Root Login in sshd_config" + parts = line.split() + # Precise check: PermitRootLogin must be the first word, yes the second + # This avoids matching commented lines or "no" followed by comments + if len(parts) >= 2 and parts[0] == "PermitRootLogin" and parts[1] == "yes": + return -50, ["Root SSH Allowed"], ["Disable SSH Root Login in sshd_config"] except (PermissionError, Exception): pass - return 0, None, None \ No newline at end of file + return 0, [], [] \ No newline at end of file From f87bd8cb87747e1b7d852de168006ed0d426b174 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Fri, 12 Dec 2025 00:11:13 +0900 Subject: [PATCH 06/18] fix: Resolve SonarCloud code smells and reduce complexity --- cortex/health/checks/disk.py | 38 +++++++---- cortex/health/checks/security.py | 11 ++- scripts/verify_ubuntu_compatibility.py | 94 ++++++++++++-------------- 3 files changed, 71 insertions(+), 72 deletions(-) diff --git a/cortex/health/checks/disk.py b/cortex/health/checks/disk.py index bd126d7f..631e9742 100644 --- a/cortex/health/checks/disk.py +++ b/cortex/health/checks/disk.py @@ -15,25 +15,35 @@ def run(self) -> CheckResult: total, used, _ = shutil.disk_usage("/") usage_percent = (used / total) * 100 - score = 100 - status = "OK" - rec = None - + # Explicit early returns to avoid static analysis confusion if usage_percent > 90: - score = 0 - status = "CRITICAL" - rec = "Clean up disk space immediately" - elif usage_percent > 80: - score = 50 - status = "WARNING" - rec = "Consider cleaning up disk space" + return CheckResult( + name="Disk Usage", + category="disk", + score=0, + status="CRITICAL", + details=f"{usage_percent:.1f}% used", + recommendation="Clean up disk space immediately", + weight=0.20 + ) + if usage_percent > 80: + return CheckResult( + name="Disk Usage", + category="disk", + score=50, + status="WARNING", + details=f"{usage_percent:.1f}% used", + recommendation="Consider cleaning up disk space", + weight=0.20 + ) + return CheckResult( name="Disk Usage", category="disk", - score=score, - status=status, + score=100, + status="OK", details=f"{usage_percent:.1f}% used", - recommendation=rec, + recommendation=None, weight=0.20 ) \ No newline at end of file diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index 0f80fbb6..64d594c7 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -23,10 +23,8 @@ def run(self) -> CheckResult: recommendations = [] # 1. Firewall (UFW) Check - # Returns: score_delta (negative for penalty), issues, recommendations fw_score_delta, fw_issues, fw_recs = self._check_firewall() - # If firewall is inactive, score becomes 0 immediately per requirements if fw_score_delta == -100: score = 0 @@ -69,10 +67,10 @@ def _check_firewall(self) -> tuple[int, list[str], list[str]]: ) if res.returncode == 0 and res.stdout.strip() == "active": return 0, [], [] - except (subprocess.TimeoutExpired, FileNotFoundError, Exception): + except Exception: + # Catch-all is intentional here for robustness against missing systemctl etc. pass - # Return -100 to signal immediate failure condition return -100, ["Firewall Inactive"], ["Enable UFW Firewall"] def _check_ssh_root_login(self) -> tuple[int, list[str], list[str]]: @@ -88,11 +86,10 @@ def _check_ssh_root_login(self) -> tuple[int, list[str], list[str]]: with open(ssh_config, 'r') as f: for line in f: parts = line.split() - # Precise check: PermitRootLogin must be the first word, yes the second - # This avoids matching commented lines or "no" followed by comments if len(parts) >= 2 and parts[0] == "PermitRootLogin" and parts[1] == "yes": return -50, ["Root SSH Allowed"], ["Disable SSH Root Login in sshd_config"] - except (PermissionError, Exception): + except Exception: + # Catch-all is intentional here for file permission issues etc. pass return 0, [], [] \ No newline at end of file diff --git a/scripts/verify_ubuntu_compatibility.py b/scripts/verify_ubuntu_compatibility.py index dca1c0bf..1d1beac8 100644 --- a/scripts/verify_ubuntu_compatibility.py +++ b/scripts/verify_ubuntu_compatibility.py @@ -4,13 +4,14 @@ import json import datetime import shutil +import pathlib -# File name to store history data -HISTORY_FILE = "security_history.json" +# Use absolute path for history file +HISTORY_FILE = pathlib.Path.home() / ".cortex" / "security_history.json" def load_history(): """Load past execution history""" - if os.path.exists(HISTORY_FILE): + if HISTORY_FILE.exists(): try: with open(HISTORY_FILE, 'r') as f: return json.load(f) @@ -20,6 +21,8 @@ def load_history(): def save_history(score, status, details): """Save execution result to history""" + HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True) + history = load_history() record = { "timestamp": datetime.datetime.now().isoformat(), @@ -28,7 +31,6 @@ def save_history(score, status, details): "details": details } history.append(record) - # Keep only the latest 10 records history = history[-10:] with open(HISTORY_FILE, 'w') as f: @@ -59,25 +61,21 @@ def show_trend(history): elif diff < 0: print(f" Trend: šŸ“‰ Dropped by {abs(diff)} points since previous run") else: - print(f" Trend: āž”ļø Stable") + print(" Trend: āž”ļø Stable") def fix_firewall(): """Enable Firewall (Automated Fix)""" print("\n [Fixing] Enabling UFW Firewall...") - # Check if ufw is installed using 'which' or checking path - # (Since sudo is used, we check if we can find ufw path) if not shutil.which("ufw") and not os.path.exists("/usr/sbin/ufw"): print(" -> āš ļø UFW is not installed. Cannot enable.") - print(" (Try: sudo apt install ufw)") return False try: - # Depends on execution environment, sudo might be required - subprocess.run(["sudo", "ufw", "enable"], check=True) + subprocess.run(["sudo", "ufw", "enable"], check=True, timeout=30) print(" -> āœ… Success: Firewall enabled.") return True - except subprocess.CalledProcessError as e: + except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: print(f" -> āŒ Failed to enable firewall: {e}") return False @@ -85,12 +83,10 @@ def fix_ssh_config(config_path): """Disable SSH Root Login (Automated Fix)""" print(f"\n [Fixing] Disabling Root Login in {config_path}...") - # Check if file exists before trying to fix if not os.path.exists(config_path): print(f" -> āš ļø Config file not found: {config_path}") return False - # 1. Create backup backup_path = config_path + ".bak." + datetime.datetime.now().strftime("%Y%m%d%H%M%S") try: shutil.copy2(config_path, backup_path) @@ -99,7 +95,6 @@ def fix_ssh_config(config_path): print(" -> āŒ Failed to create backup (Permission denied). Need sudo?") return False - # 2. Rewrite configuration try: new_lines = [] with open(config_path, 'r') as f: @@ -108,7 +103,6 @@ def fix_ssh_config(config_path): fixed = False for line in lines: if line.strip().startswith("PermitRootLogin") and "yes" in line: - # Comment out and add disabled setting new_lines.append(f"# {line.strip()} (Disabled by Auto-Fix)\n") new_lines.append("PermitRootLogin no\n") fixed = True @@ -120,88 +114,89 @@ def fix_ssh_config(config_path): f.writelines(new_lines) print(" -> āœ… Success: sshd_config updated.") - # Attempt to restart SSH service print(" -> Restarting sshd service...") - subprocess.run(["sudo", "systemctl", "restart", "ssh"], check=False) + res = subprocess.run( + ["sudo", "systemctl", "restart", "ssh"], + capture_output=True, text=True, timeout=30 + ) + if res.returncode != 0: + print(f" -> āš ļø SSH restart failed: {res.stderr}") + return True return True else: print(" -> No changes needed.") return True - except PermissionError: - print(" -> āŒ Failed to write config (Permission denied). Need sudo?") - return False except Exception as e: print(f" -> āŒ Error during fix: {e}") return False -def verify_security_logic(): - print("=== Ubuntu Security Logic Verification ===") - - # --------------------------------------------------------- - # 1. Firewall (UFW) Check Logic - # --------------------------------------------------------- +def _check_firewall_status(): + """Helper to check firewall status.""" print("\n[1] Checking Firewall (UFW)...") - ufw_active = False - ufw_needs_fix = False try: print(" Running: systemctl is-active ufw") res = subprocess.run( ["systemctl", "is-active", "ufw"], - capture_output=True, text=True + capture_output=True, text=True, timeout=10 ) output = res.stdout.strip() print(f" Output: '{output}'") if res.returncode == 0 and output == "active": - ufw_active = True print(" -> JUDGEMENT: Firewall is ACTIVE (Score: 100)") + return True else: print(" -> JUDGEMENT: Firewall is INACTIVE (Score: 0)") - ufw_needs_fix = True + return False except FileNotFoundError: print(" -> ERROR: 'systemctl' command not found.") except Exception as e: print(f" -> ERROR: {e}") + return False - # --------------------------------------------------------- - # 2. SSH Root Login Check Logic - # --------------------------------------------------------- +def _check_ssh_status(ssh_config): + """Helper to check SSH status.""" print("\n[2] Checking SSH Configuration...") - ssh_config = "/etc/ssh/sshd_config" score_penalty = 0 - ssh_needs_fix = False + needs_fix = False if os.path.exists(ssh_config): print(f" File found: {ssh_config}") try: with open(ssh_config, 'r') as f: - found_risky_setting = False for line in f: - if line.strip().startswith("PermitRootLogin") and "yes" in line: + parts = line.split() + if len(parts) >= 2 and parts[0] == "PermitRootLogin" and parts[1] == "yes": print(f" -> FOUND RISKY LINE: {line.strip()}") score_penalty = 50 - found_risky_setting = True - ssh_needs_fix = True + needs_fix = True break - if not found_risky_setting: + if not needs_fix: print(" -> No 'PermitRootLogin yes' found (Safe)") except PermissionError: print(" -> ERROR: Permission denied. Try running with 'sudo'.") else: print(f" -> WARNING: {ssh_config} does not exist.") + + return score_penalty, needs_fix - # --------------------------------------------------------- - # Final Report & History - # --------------------------------------------------------- +def verify_security_logic(): + print("=== Ubuntu Security Logic Verification ===") + + ufw_active = _check_firewall_status() + ssh_config = "/etc/ssh/sshd_config" + ssh_penalty, ssh_needs_fix = _check_ssh_status(ssh_config) + + # Final Report print("\n=== Summary ===") final_score = 100 if not ufw_active: final_score = 0 - final_score -= score_penalty + final_score -= ssh_penalty final_score = max(0, final_score) status = "OK" @@ -211,18 +206,17 @@ def verify_security_logic(): print(f"Current Score: {final_score}") print(f"Status: {status}") - # --- Trend Tracking --- + # History print("\n... Saving history ...") details = [] + ufw_needs_fix = not ufw_active if ufw_needs_fix: details.append("Firewall Inactive") if ssh_needs_fix: details.append("Root SSH Allowed") history = save_history(final_score, status, ", ".join(details)) show_trend(history) - # --------------------------------------------------------- - # Automated Fixes (Interactive) - # --------------------------------------------------------- + # Automated Fixes if ufw_needs_fix or ssh_needs_fix: print("\n=== šŸ› ļø Automated Fixes Available ===") print("Issues detected that can be automatically fixed.") @@ -233,13 +227,11 @@ def verify_security_logic(): fix_firewall() if ssh_needs_fix: fix_ssh_config(ssh_config) - print("\nāœ… Fixes attempt complete. Please re-run script to verify.") else: print("Skipping fixes.") if __name__ == "__main__": - # Warn that sudo might be required for execution if os.geteuid() != 0: print("NOTE: This script works best with 'sudo' for fixing issues.") verify_security_logic() \ No newline at end of file From 2e950b1c2cd637ef5487303e6b576b12489aa223 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Fri, 12 Dec 2025 00:19:09 +0900 Subject: [PATCH 07/18] docs: Add missing docstrings to HealthMonitor public APIs --- cortex/health/monitor.py | 59 ++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/cortex/health/monitor.py b/cortex/health/monitor.py index c85b6246..7ba95d06 100644 --- a/cortex/health/monitor.py +++ b/cortex/health/monitor.py @@ -1,7 +1,7 @@ import json import time from abc import ABC, abstractmethod -from dataclasses import dataclass, field +from dataclasses import dataclass from pathlib import Path from typing import List, Dict, Optional from rich.console import Console @@ -10,7 +10,7 @@ @dataclass class CheckResult: - """Data class to hold the result of each check""" + """Data class to hold the result of each check.""" name: str # Item name (e.g. "Disk Space") category: str # Category (security, updates, performance, disk) score: int # Score 0-100 @@ -20,40 +20,59 @@ class CheckResult: weight: float = 1.0 # Weight for weighted average class HealthCheck(ABC): - """Base class inherited by all health check modules""" + """Base class inherited by all health check modules.""" + @abstractmethod def run(self) -> CheckResult: + """Execute the check and return a result.""" pass class HealthMonitor: """ Main engine for system health monitoring. + + Manages registration of health checks, execution, score aggregation, + and history persistence. """ def __init__(self): + """Initialize the health monitor and register default checks.""" self.history_file = Path.home() / ".cortex" / "health_history.json" self.history_file.parent.mkdir(exist_ok=True) self.checks: List[HealthCheck] = [] - + # Register each check here # (Import here to prevent circular references) from .checks.security import SecurityCheck from .checks.updates import UpdateCheck from .checks.performance import PerformanceCheck from .checks.disk import DiskCheck - + self.register_check(SecurityCheck()) self.register_check(UpdateCheck()) self.register_check(PerformanceCheck()) self.register_check(DiskCheck()) + + def register_check(self, check: HealthCheck) -> None: + """ + Register a health check instance to be run as part of the monitor. - def register_check(self, check: HealthCheck): + Args: + check (HealthCheck): The check instance to register. + """ self.checks.append(check) def run_all(self) -> Dict: + """ + Run all registered checks and return an aggregated health report. + + Returns: + Dict: A report containing the timestamp, total weighted score, + and a list of individual check results. + """ results = [] total_weighted_score = 0 total_weight = 0 - + for check in self.checks: try: result = check.run() @@ -62,11 +81,11 @@ def run_all(self) -> Dict: total_weight += result.weight except Exception as e: console.print(f"[red]Error running check {check.__class__.__name__}: {e}[/red]") - + final_score = 0 if total_weight > 0: final_score = int(total_weighted_score / total_weight) - + report = { "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"), "total_score": final_score, @@ -82,11 +101,17 @@ def run_all(self) -> Dict: for r in results ] } - + self._save_history(report) return report - def _save_history(self, report: Dict): + def _save_history(self, report: Dict) -> None: + """ + Save the current health report to the history JSON file. + + Args: + report (Dict): The health report to save. + """ history = [] if self.history_file.exists(): try: @@ -96,11 +121,11 @@ def _save_history(self, report: Dict): pass history.append(report) + # Keep only the last 100 records history = history[-100:] - with open(self.history_file, 'w') as f: - json.dump(history, f, indent=4) - -if __name__ == "__main__": - # For testing execution - print("HealthMonitor initialized.") \ No newline at end of file + try: + with open(self.history_file, 'w') as f: + json.dump(history, f, indent=4) + except Exception as e: + console.print(f"[yellow]Warning: Could not save health history: {e}[/yellow]") \ No newline at end of file From 8240944db60b3b82c238c467d65fd1c84ff65449 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Fri, 12 Dec 2025 00:22:44 +0900 Subject: [PATCH 08/18] fix: Address SonarCloud and CodeRabbit feedback (redundant exceptions, error handling) --- cortex/health/checks/disk.py | 17 ++++- cortex/health/checks/security.py | 109 ++++++++++++------------------- cortex/health/checks/updates.py | 13 +++- 3 files changed, 65 insertions(+), 74 deletions(-) diff --git a/cortex/health/checks/disk.py b/cortex/health/checks/disk.py index 631e9742..4b066594 100644 --- a/cortex/health/checks/disk.py +++ b/cortex/health/checks/disk.py @@ -11,9 +11,20 @@ def run(self) -> CheckResult: Returns: CheckResult based on usage thresholds. """ - # Use _ for unused variable (free space) - total, used, _ = shutil.disk_usage("/") - usage_percent = (used / total) * 100 + try: + # Use _ for unused variable (free space) + total, used, _ = shutil.disk_usage("/") + usage_percent = (used / total) * 100 + except Exception as e: + return CheckResult( + name="Disk Usage", + category="disk", + score=0, + status="CRITICAL", + details=f"Check failed: {e}", + recommendation="Check disk mounts and permissions", + weight=0.20 + ) # Explicit early returns to avoid static analysis confusion if usage_percent > 90: diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index 64d594c7..c7313197 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -3,93 +3,64 @@ from ..monitor import HealthCheck, CheckResult class SecurityCheck(HealthCheck): - """ - Checks system security posture including firewall status and SSH configuration. - - Evaluates UFW firewall activity and SSH root login permissions, - returning a weighted score and actionable recommendations. - """ - def run(self) -> CheckResult: - """ - Execute security checks and return aggregated results. - - Returns: - CheckResult: Security assessment with score (0-100), status, - detected issues, and recommendations. - """ score = 100 issues = [] recommendations = [] # 1. Firewall (UFW) Check - fw_score_delta, fw_issues, fw_recs = self._check_firewall() - - if fw_score_delta == -100: - score = 0 - - issues.extend(fw_issues) - recommendations.extend(fw_recs) - - # 2. SSH Root Login Check - ssh_score_delta, ssh_issues, ssh_recs = self._check_ssh_root_login() - score += ssh_score_delta - issues.extend(ssh_issues) - recommendations.extend(ssh_recs) - - status = "OK" - if score < 50: status = "CRITICAL" - elif score < 100: status = "WARNING" - - return CheckResult( - name="Security Posture", - category="security", - score=max(0, score), - status=status, - details=", ".join(issues) if issues else "Secure", - recommendation=", ".join(recommendations) if recommendations else None, - weight=0.35 - ) - - def _check_firewall(self) -> tuple[int, list[str], list[str]]: - """ - Check if UFW is active. - - Returns: - tuple: (score_delta, issues_list, recommendations_list) - """ + ufw_active = False try: + # Add timeout to prevent hanging (Fixes Reliability Issue) res = subprocess.run( ["systemctl", "is-active", "ufw"], capture_output=True, text=True, - timeout=10 + timeout=5 ) + # Fix: Use exact match to avoid matching "inactive" which contains "active" if res.returncode == 0 and res.stdout.strip() == "active": - return 0, [], [] + ufw_active = True + except subprocess.TimeoutExpired: + pass # Command timed out, treat as inactive or unavailable + except FileNotFoundError: + pass # Environment without systemctl (e.g., Docker or non-systemd) except Exception: - # Catch-all is intentional here for robustness against missing systemctl etc. - pass - - return -100, ["Firewall Inactive"], ["Enable UFW Firewall"] + pass # Generic error protection - def _check_ssh_root_login(self) -> tuple[int, list[str], list[str]]: - """ - Check for PermitRootLogin yes in sshd_config. - - Returns: - tuple: (score_delta, issues_list, recommendations_list) - """ + if not ufw_active: + score = 0 # Spec: 0 points if Firewall is inactive + issues.append("Firewall Inactive") + recommendations.append("Enable UFW Firewall") + + # 2. SSH Root Login Check try: ssh_config = "/etc/ssh/sshd_config" if os.path.exists(ssh_config): with open(ssh_config, 'r') as f: for line in f: - parts = line.split() - if len(parts) >= 2 and parts[0] == "PermitRootLogin" and parts[1] == "yes": - return -50, ["Root SSH Allowed"], ["Disable SSH Root Login in sshd_config"] + line = line.strip() + # Check for uncommented PermitRootLogin yes + if line.startswith("PermitRootLogin") and "yes" in line.split(): + score -= 50 + issues.append("Root SSH Allowed") + recommendations.append("Disable SSH Root Login in sshd_config") + break + except PermissionError: + pass # Cannot read config, skip check except Exception: - # Catch-all is intentional here for file permission issues etc. - pass - - return 0, [], [] \ No newline at end of file + pass # Generic error protection + + status = "OK" + if score < 50: status = "CRITICAL" + elif score < 100: status = "WARNING" + + return CheckResult( + name="Security Posture", + category="security", + score=max(0, score), + status=status, + details=", ".join(issues) if issues else "Secure", + recommendation=", ".join(recommendations) if recommendations else None, + weight=0.35 + ) \ No newline at end of file diff --git a/cortex/health/checks/updates.py b/cortex/health/checks/updates.py index d40b2519..a38a464c 100644 --- a/cortex/health/checks/updates.py +++ b/cortex/health/checks/updates.py @@ -37,8 +37,17 @@ def run(self) -> CheckResult: score -= (pkg_count * 2) score -= (sec_count * 10) - except Exception: - pass + except Exception as e: + # CodeRabbit Suggestion: Return failure state instead of ignoring errors + return CheckResult( + name="System Updates", + category="updates", + score=0, + status="CRITICAL", + details=f"Check failed: {e}", + recommendation="Verify package manager configuration", + weight=0.25 + ) status = "OK" if score < 50: status = "CRITICAL" From db3bf193dc3d1bf2ca7968db314d4977f0fc63a0 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 17:29:35 +0900 Subject: [PATCH 09/18] fix: improve CodeQL compliance in health checks - Add command constants for security (avoids PATH manipulation) - Use specific exception types instead of bare Exception - Add comprehensive docstrings for classes and methods - Extract SSH check logic to helper method for better maintainability - Apply consistent code formatting --- cortex/health/checks/security.py | 92 +++++++++++++++++++++++--------- cortex/health/checks/updates.py | 57 +++++++++++++++----- 2 files changed, 112 insertions(+), 37 deletions(-) diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index c7313197..59704670 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -2,8 +2,35 @@ import os from ..monitor import HealthCheck, CheckResult +# Command constants (full paths for security - avoids PATH manipulation attacks) +SYSTEMCTL_CMD = "/usr/bin/systemctl" +UFW_SERVICE = "ufw" +SSH_CONFIG_PATH = "/etc/ssh/sshd_config" + + class SecurityCheck(HealthCheck): + """Check system security posture including firewall and SSH configuration. + + This check evaluates: + - UFW firewall status (0 points if inactive) + - SSH root login configuration (-50 points if enabled) + + Attributes: + None + + Example: + >>> check = SecurityCheck() + >>> result = check.run() + >>> print(result.score) + 100 + """ + def run(self) -> CheckResult: + """Execute security checks and return aggregated result. + + Returns: + CheckResult: Security posture score and recommendations. + """ score = 100 issues = [] recommendations = [] @@ -13,8 +40,8 @@ def run(self) -> CheckResult: try: # Add timeout to prevent hanging (Fixes Reliability Issue) res = subprocess.run( - ["systemctl", "is-active", "ufw"], - capture_output=True, + [SYSTEMCTL_CMD, "is-active", UFW_SERVICE], + capture_output=True, text=True, timeout=5 ) @@ -22,34 +49,21 @@ def run(self) -> CheckResult: if res.returncode == 0 and res.stdout.strip() == "active": ufw_active = True except subprocess.TimeoutExpired: - pass # Command timed out, treat as inactive or unavailable + pass # Command timed out, treat as inactive or unavailable except FileNotFoundError: - pass # Environment without systemctl (e.g., Docker or non-systemd) - except Exception: - pass # Generic error protection + pass # Environment without systemctl (e.g., Docker or non-systemd) + except OSError: + pass # Other OS-level errors if not ufw_active: - score = 0 # Spec: 0 points if Firewall is inactive + score = 0 # Spec: 0 points if Firewall is inactive issues.append("Firewall Inactive") recommendations.append("Enable UFW Firewall") # 2. SSH Root Login Check - try: - ssh_config = "/etc/ssh/sshd_config" - if os.path.exists(ssh_config): - with open(ssh_config, 'r') as f: - for line in f: - line = line.strip() - # Check for uncommented PermitRootLogin yes - if line.startswith("PermitRootLogin") and "yes" in line.split(): - score -= 50 - issues.append("Root SSH Allowed") - recommendations.append("Disable SSH Root Login in sshd_config") - break - except PermissionError: - pass # Cannot read config, skip check - except Exception: - pass # Generic error protection + self._check_ssh_root_login(issues, recommendations) + if "Root SSH Allowed" in issues: + score -= 50 status = "OK" if score < 50: status = "CRITICAL" @@ -63,4 +77,34 @@ def run(self) -> CheckResult: details=", ".join(issues) if issues else "Secure", recommendation=", ".join(recommendations) if recommendations else None, weight=0.35 - ) \ No newline at end of file + ) + + def _check_ssh_root_login( + self, issues: list[str], recommendations: list[str] + ) -> None: + """Check if SSH root login is enabled. + + Args: + issues: List to append issue descriptions to. + recommendations: List to append recommendations to. + """ + try: + if not os.path.exists(SSH_CONFIG_PATH): + return + + with open(SSH_CONFIG_PATH, 'r') as f: + for line in f: + stripped = line.strip() + # Check for uncommented PermitRootLogin yes + if stripped.startswith("PermitRootLogin"): + parts = stripped.split() + if len(parts) >= 2 and parts[1] == "yes": + issues.append("Root SSH Allowed") + recommendations.append( + "Disable SSH Root Login in sshd_config" + ) + return + except PermissionError: + pass # Cannot read config, skip check + except OSError: + pass # Other file system errors \ No newline at end of file diff --git a/cortex/health/checks/updates.py b/cortex/health/checks/updates.py index a38a464c..9faa901b 100644 --- a/cortex/health/checks/updates.py +++ b/cortex/health/checks/updates.py @@ -1,30 +1,42 @@ import subprocess from ..monitor import HealthCheck, CheckResult +# Command constants (full paths for security) +APT_CMD = "/usr/bin/apt" + + class UpdateCheck(HealthCheck): - """Check for pending system updates and security patches.""" + """Check for pending system updates and security patches. + + This check evaluates the number of available package updates + and applies score penalties accordingly: + - Regular packages: -2 points each + - Security updates: -10 points each + + Attributes: + None + """ def run(self) -> CheckResult: - """ - Check for available updates using apt. - + """Check for available updates using apt. + Returns: CheckResult with score based on pending updates. """ score = 100 pkg_count = 0 sec_count = 0 - + try: # Add timeout to prevent hangs res = subprocess.run( - ["apt", "list", "--upgradable"], - capture_output=True, + [APT_CMD, "list", "--upgradable"], + capture_output=True, text=True, timeout=30 ) lines = res.stdout.splitlines() - + # apt list output header usually takes first line for line in lines[1:]: if line.strip(): @@ -32,13 +44,32 @@ def run(self) -> CheckResult: sec_count += 1 else: pkg_count += 1 - + # Scoring - score -= (pkg_count * 2) - score -= (sec_count * 10) + score -= (pkg_count * 2) + score -= (sec_count * 10) - except Exception as e: - # CodeRabbit Suggestion: Return failure state instead of ignoring errors + except subprocess.TimeoutExpired as e: + return CheckResult( + name="System Updates", + category="updates", + score=0, + status="CRITICAL", + details=f"Check timed out: {e}", + recommendation="Verify package manager configuration", + weight=0.25 + ) + except FileNotFoundError: + return CheckResult( + name="System Updates", + category="updates", + score=0, + status="CRITICAL", + details="apt command not found", + recommendation="This check requires apt package manager", + weight=0.25 + ) + except OSError as e: return CheckResult( name="System Updates", category="updates", From 6f2d8a3c0f36c937d0b1cdbe6dc9293b77d82069 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 17:46:45 +0900 Subject: [PATCH 10/18] style: fix ruff linter errors in health module - I001: Sort and organize imports - UP035/UP006: Modernize type hints (List/Dict -> list/dict) - UP045: Use X | None syntax for optional types - UP015: Remove unnecessary mode arguments from open() - W293: Remove whitespace from blank lines - W292: Add newline at end of files - F811: Remove duplicate imports --- cortex/cli.py | 6 ++- cortex/health/checks/disk.py | 13 ++++--- cortex/health/checks/performance.py | 35 +++++++++++------- cortex/health/checks/security.py | 15 +++++--- cortex/health/checks/updates.py | 11 ++++-- cortex/health/monitor.py | 57 +++++++++++++++-------------- tests/test_health_monitor.py | 41 ++++++++++++--------- 7 files changed, 104 insertions(+), 74 deletions(-) diff --git a/cortex/cli.py b/cortex/cli.py index e5d5a3db..81f77ce2 100644 --- a/cortex/cli.py +++ b/cortex/cli.py @@ -14,7 +14,11 @@ from cortex.branding import VERSION, console, cx_header, cx_print, show_banner from cortex.coordinator import InstallationCoordinator, StepStatus -from cortex.installation_history import InstallationHistory, InstallationStatus, InstallationType +from cortex.installation_history import ( + InstallationHistory, + InstallationStatus, + InstallationType, +) from cortex.llm.interpreter import CommandInterpreter from cortex.notification_manager import NotificationManager from cortex.stack_manager import StackManager diff --git a/cortex/health/checks/disk.py b/cortex/health/checks/disk.py index 4b066594..42834f59 100644 --- a/cortex/health/checks/disk.py +++ b/cortex/health/checks/disk.py @@ -1,13 +1,14 @@ import shutil -from ..monitor import HealthCheck, CheckResult + +from ..monitor import CheckResult, HealthCheck + class DiskCheck(HealthCheck): """Check root filesystem disk usage.""" def run(self) -> CheckResult: - """ - Calculate disk usage percentage. - + """Calculate disk usage percentage. + Returns: CheckResult based on usage thresholds. """ @@ -25,7 +26,7 @@ def run(self) -> CheckResult: recommendation="Check disk mounts and permissions", weight=0.20 ) - + # Explicit early returns to avoid static analysis confusion if usage_percent > 90: return CheckResult( @@ -37,7 +38,7 @@ def run(self) -> CheckResult: recommendation="Clean up disk space immediately", weight=0.20 ) - + if usage_percent > 80: return CheckResult( name="Disk Usage", diff --git a/cortex/health/checks/performance.py b/cortex/health/checks/performance.py index 9e5e66f1..cf46913b 100644 --- a/cortex/health/checks/performance.py +++ b/cortex/health/checks/performance.py @@ -1,47 +1,56 @@ -import os import multiprocessing -from ..monitor import HealthCheck, CheckResult +import os + +from ..monitor import CheckResult, HealthCheck + class PerformanceCheck(HealthCheck): + """Check system performance metrics including CPU load and memory usage.""" + def run(self) -> CheckResult: + """Check system load and memory usage. + + Returns: + CheckResult with performance score. + """ score = 100 issues = [] rec = None - + # 1. Load Average (1min) try: load1, _, _ = os.getloadavg() cores = multiprocessing.cpu_count() # Load ratio against core count load_ratio = load1 / cores - + if load_ratio > 1.0: score -= 50 issues.append(f"High Load ({load1:.2f})") rec = "Check top processes" - except Exception: - pass # Skip on Windows etc. + except OSError: + pass # Skip on Windows etc. # 2. Memory Usage (Linux /proc/meminfo) try: - with open('/proc/meminfo', 'r') as f: + with open('/proc/meminfo') as f: meminfo = {} for line in f: parts = line.split(':') if len(parts) == 2: meminfo[parts[0].strip()] = int(parts[1].strip().split()[0]) - + if 'MemTotal' in meminfo and 'MemAvailable' in meminfo: total = meminfo['MemTotal'] avail = meminfo['MemAvailable'] used_percent = ((total - avail) / total) * 100 - + if used_percent > 80: penalty = int(used_percent - 80) score -= penalty issues.append(f"High Memory ({used_percent:.0f}%)") except FileNotFoundError: - pass # Non-Linux systems + pass # Non-Linux systems # Summary of results status = "OK" @@ -49,9 +58,9 @@ def run(self) -> CheckResult: status = "CRITICAL" elif score < 90: status = "WARNING" - + details = ", ".join(issues) if issues else "Optimal" - + return CheckResult( name="System Load", category="performance", @@ -59,5 +68,5 @@ def run(self) -> CheckResult: status=status, details=details, recommendation=rec, - weight=0.20 # 20% + weight=0.20 # 20% ) \ No newline at end of file diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index 59704670..9bf7b84b 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -1,6 +1,7 @@ -import subprocess import os -from ..monitor import HealthCheck, CheckResult +import subprocess + +from ..monitor import CheckResult, HealthCheck # Command constants (full paths for security - avoids PATH manipulation attacks) SYSTEMCTL_CMD = "/usr/bin/systemctl" @@ -34,7 +35,7 @@ def run(self) -> CheckResult: score = 100 issues = [] recommendations = [] - + # 1. Firewall (UFW) Check ufw_active = False try: @@ -66,8 +67,10 @@ def run(self) -> CheckResult: score -= 50 status = "OK" - if score < 50: status = "CRITICAL" - elif score < 100: status = "WARNING" + if score < 50: + status = "CRITICAL" + elif score < 100: + status = "WARNING" return CheckResult( name="Security Posture", @@ -92,7 +95,7 @@ def _check_ssh_root_login( if not os.path.exists(SSH_CONFIG_PATH): return - with open(SSH_CONFIG_PATH, 'r') as f: + with open(SSH_CONFIG_PATH) as f: for line in f: stripped = line.strip() # Check for uncommented PermitRootLogin yes diff --git a/cortex/health/checks/updates.py b/cortex/health/checks/updates.py index 9faa901b..633f9676 100644 --- a/cortex/health/checks/updates.py +++ b/cortex/health/checks/updates.py @@ -1,5 +1,6 @@ import subprocess -from ..monitor import HealthCheck, CheckResult + +from ..monitor import CheckResult, HealthCheck # Command constants (full paths for security) APT_CMD = "/usr/bin/apt" @@ -81,9 +82,11 @@ def run(self) -> CheckResult: ) status = "OK" - if score < 50: status = "CRITICAL" - elif score < 90: status = "WARNING" - + if score < 50: + status = "CRITICAL" + elif score < 90: + status = "WARNING" + details = f"{pkg_count} packages, {sec_count} security updates pending" if pkg_count == 0 and sec_count == 0: details = "System up to date" diff --git a/cortex/health/monitor.py b/cortex/health/monitor.py index 7ba95d06..deac2b4d 100644 --- a/cortex/health/monitor.py +++ b/cortex/health/monitor.py @@ -3,49 +3,53 @@ from abc import ABC, abstractmethod from dataclasses import dataclass from pathlib import Path -from typing import List, Dict, Optional + from rich.console import Console console = Console() + @dataclass class CheckResult: """Data class to hold the result of each check.""" + name: str # Item name (e.g. "Disk Space") category: str # Category (security, updates, performance, disk) score: int # Score 0-100 status: str # "OK", "WARNING", "CRITICAL" details: str # Detailed message - recommendation: Optional[str] = None # Recommended action (if any) + recommendation: str | None = None # Recommended action (if any) weight: float = 1.0 # Weight for weighted average + class HealthCheck(ABC): """Base class inherited by all health check modules.""" - + @abstractmethod def run(self) -> CheckResult: """Execute the check and return a result.""" pass + class HealthMonitor: - """ - Main engine for system health monitoring. - + """Main engine for system health monitoring. + Manages registration of health checks, execution, score aggregation, and history persistence. """ - def __init__(self): + + def __init__(self) -> None: """Initialize the health monitor and register default checks.""" self.history_file = Path.home() / ".cortex" / "health_history.json" self.history_file.parent.mkdir(exist_ok=True) - self.checks: List[HealthCheck] = [] + self.checks: list[HealthCheck] = [] # Register each check here # (Import here to prevent circular references) + from .checks.disk import DiskCheck + from .checks.performance import PerformanceCheck from .checks.security import SecurityCheck from .checks.updates import UpdateCheck - from .checks.performance import PerformanceCheck - from .checks.disk import DiskCheck self.register_check(SecurityCheck()) self.register_check(UpdateCheck()) @@ -53,20 +57,18 @@ def __init__(self): self.register_check(DiskCheck()) def register_check(self, check: HealthCheck) -> None: - """ - Register a health check instance to be run as part of the monitor. - + """Register a health check instance to be run as part of the monitor. + Args: check (HealthCheck): The check instance to register. """ self.checks.append(check) - def run_all(self) -> Dict: - """ - Run all registered checks and return an aggregated health report. - + def run_all(self) -> dict: + """Run all registered checks and return an aggregated health report. + Returns: - Dict: A report containing the timestamp, total weighted score, + dict: A report containing the timestamp, total weighted score, and a list of individual check results. """ results = [] @@ -80,7 +82,9 @@ def run_all(self) -> Dict: total_weighted_score += result.score * result.weight total_weight += result.weight except Exception as e: - console.print(f"[red]Error running check {check.__class__.__name__}: {e}[/red]") + console.print( + f"[red]Error running check {check.__class__.__name__}: {e}[/red]" + ) final_score = 0 if total_weight > 0: @@ -105,25 +109,24 @@ def run_all(self) -> Dict: self._save_history(report) return report - def _save_history(self, report: Dict) -> None: - """ - Save the current health report to the history JSON file. - + def _save_history(self, report: dict) -> None: + """Save the current health report to the history JSON file. + Args: - report (Dict): The health report to save. + report (dict): The health report to save. """ history = [] if self.history_file.exists(): try: - with open(self.history_file, 'r') as f: + with open(self.history_file) as f: history = json.load(f) except json.JSONDecodeError: pass - + history.append(report) # Keep only the last 100 records history = history[-100:] - + try: with open(self.history_file, 'w') as f: json.dump(history, f, indent=4) diff --git a/tests/test_health_monitor.py b/tests/test_health_monitor.py index d352f0d4..00a2a965 100644 --- a/tests/test_health_monitor.py +++ b/tests/test_health_monitor.py @@ -1,10 +1,12 @@ import unittest -from unittest.mock import patch, MagicMock, mock_open -from cortex.health.monitor import HealthMonitor, CheckResult +from unittest.mock import MagicMock, mock_open, patch + from cortex.health.checks.disk import DiskCheck from cortex.health.checks.performance import PerformanceCheck from cortex.health.checks.security import SecurityCheck from cortex.health.checks.updates import UpdateCheck +from cortex.health.monitor import CheckResult, HealthMonitor + class TestDiskCheck(unittest.TestCase): @patch('shutil.disk_usage') @@ -29,6 +31,7 @@ def test_disk_usage_scoring(self, mock_usage): self.assertEqual(result.score, 0) self.assertEqual(result.status, "CRITICAL") + class TestPerformanceCheck(unittest.TestCase): @patch('os.getloadavg') @patch('multiprocessing.cpu_count') @@ -36,13 +39,13 @@ def test_load_average(self, mock_cpu, mock_load): # Case 1: Load OK (Load 2.0 / 4 Cores = 0.5 ratio) mock_cpu.return_value = 4 mock_load.return_value = (2.0, 2.0, 2.0) - + # Mock reading /proc/meminfo (Normal case) mem_data = "MemTotal: 1000 kB\nMemAvailable: 500 kB\n" with patch('builtins.open', mock_open(read_data=mem_data)): check = PerformanceCheck() result = check.run() - self.assertEqual(result.score, 100) # No penalty + self.assertEqual(result.score, 100) # No penalty @patch('os.getloadavg') @patch('multiprocessing.cpu_count') @@ -50,13 +53,14 @@ def test_high_load_penalty(self, mock_cpu, mock_load): # Case 2: High Load (Load 5.0 / 4 Cores = 1.25 ratio) -> -50 pts mock_cpu.return_value = 4 mock_load.return_value = (5.0, 5.0, 5.0) - + # Assume memory is normal mem_data = "MemTotal: 1000 kB\nMemAvailable: 500 kB\n" with patch('builtins.open', mock_open(read_data=mem_data)): check = PerformanceCheck() result = check.run() - self.assertEqual(result.score, 50) # 100 - 50 = 50 + self.assertEqual(result.score, 50) # 100 - 50 = 50 + class TestSecurityCheck(unittest.TestCase): @patch('subprocess.run') @@ -64,7 +68,7 @@ def test_ufw_status(self, mock_run): # Case 1: UFW Inactive -> 0 pts mock_run.return_value.stdout = "inactive" mock_run.return_value.returncode = 0 - + check = SecurityCheck() result = check.run() self.assertEqual(result.score, 0) @@ -75,13 +79,14 @@ def test_ufw_active(self, mock_run): # Case 2: UFW Active -> 100 pts (SSH config is safe by default mock) mock_run.return_value.stdout = "active" mock_run.return_value.returncode = 0 - + # Test error handling when sshd_config does not exist with patch('os.path.exists', return_value=False): check = SecurityCheck() result = check.run() self.assertEqual(result.score, 100) + class TestUpdateCheck(unittest.TestCase): @patch('subprocess.run') def test_apt_updates(self, mock_run): @@ -94,44 +99,46 @@ def test_apt_updates(self, mock_run): """ mock_run.return_value.stdout = apt_output mock_run.return_value.returncode = 0 - + check = UpdateCheck() result = check.run() - - # Calculation: + + # Calculation: # Total packages: 3 # Security packages: 1 (line containing "security") # Penalty: (3 * 2) + (1 * 10) = 6 + 10 = 16 pts # Expected score: 100 - 16 = 84 pts - + self.assertEqual(result.score, 84) self.assertIn("3 pending", result.details) + class TestHealthMonitor(unittest.TestCase): def test_monitor_aggregation(self): monitor = HealthMonitor() # Register mock checks instead of real check classes - + mock_check1 = MagicMock() mock_check1.run.return_value = CheckResult( name="Check1", category="test", score=100, status="OK", details="", weight=0.5 ) - + mock_check2 = MagicMock() mock_check2.run.return_value = CheckResult( name="Check2", category="test", score=0, status="CRITICAL", details="", weight=0.5 ) - + monitor.checks = [mock_check1, mock_check2] - + # Mock history saving to prevent file write with patch.object(monitor, '_save_history'): report = monitor.run_all() - + # Weighted average calculation: # (100 * 0.5) + (0 * 0.5) = 50 / (0.5 + 0.5) = 50 pts self.assertEqual(report['total_score'], 50) self.assertEqual(len(report['results']), 2) + if __name__ == '__main__': unittest.main() \ No newline at end of file From fa3b020415ec7fdcacfc04b14debb9c959852f81 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 17:55:30 +0900 Subject: [PATCH 11/18] style: fix W292 and W293 ruff errors - Add missing newlines at end of files - Remove whitespace from blank lines --- cortex/health/checks/disk.py | 2 +- cortex/health/checks/performance.py | 2 +- cortex/health/checks/security.py | 2 +- cortex/health/checks/updates.py | 2 +- cortex/health/monitor.py | 2 +- tests/test_health_monitor.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cortex/health/checks/disk.py b/cortex/health/checks/disk.py index 42834f59..eece7bde 100644 --- a/cortex/health/checks/disk.py +++ b/cortex/health/checks/disk.py @@ -58,4 +58,4 @@ def run(self) -> CheckResult: details=f"{usage_percent:.1f}% used", recommendation=None, weight=0.20 - ) \ No newline at end of file + ) diff --git a/cortex/health/checks/performance.py b/cortex/health/checks/performance.py index cf46913b..92ba365e 100644 --- a/cortex/health/checks/performance.py +++ b/cortex/health/checks/performance.py @@ -69,4 +69,4 @@ def run(self) -> CheckResult: details=details, recommendation=rec, weight=0.20 # 20% - ) \ No newline at end of file + ) diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index 9bf7b84b..2cc015c6 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -110,4 +110,4 @@ def _check_ssh_root_login( except PermissionError: pass # Cannot read config, skip check except OSError: - pass # Other file system errors \ No newline at end of file + pass # Other file system errors diff --git a/cortex/health/checks/updates.py b/cortex/health/checks/updates.py index 633f9676..0a5abc34 100644 --- a/cortex/health/checks/updates.py +++ b/cortex/health/checks/updates.py @@ -99,4 +99,4 @@ def run(self) -> CheckResult: details=details, recommendation="Run 'apt upgrade'" if score < 100 else None, weight=0.25 - ) \ No newline at end of file + ) diff --git a/cortex/health/monitor.py b/cortex/health/monitor.py index deac2b4d..4d45ac81 100644 --- a/cortex/health/monitor.py +++ b/cortex/health/monitor.py @@ -131,4 +131,4 @@ def _save_history(self, report: dict) -> None: with open(self.history_file, 'w') as f: json.dump(history, f, indent=4) except Exception as e: - console.print(f"[yellow]Warning: Could not save health history: {e}[/yellow]") \ No newline at end of file + console.print(f"[yellow]Warning: Could not save health history: {e}[/yellow]") diff --git a/tests/test_health_monitor.py b/tests/test_health_monitor.py index 00a2a965..35605e93 100644 --- a/tests/test_health_monitor.py +++ b/tests/test_health_monitor.py @@ -141,4 +141,4 @@ def test_monitor_aggregation(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() From 14b5158778fa4417f988bc9187685f31b2638add Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 18:04:27 +0900 Subject: [PATCH 12/18] fix: secure verify_ubuntu_compatibility.py and fix ruff errors - Add sudo -n for non-interactive execution - Use atomic write pattern for sshd_config - Use full paths for system commands to prevent PATH attacks - Fix ruff linter errors (imports, whitespace, mode args) --- scripts/verify_ubuntu_compatibility.py | 159 +++++++++++++++++-------- 1 file changed, 108 insertions(+), 51 deletions(-) diff --git a/scripts/verify_ubuntu_compatibility.py b/scripts/verify_ubuntu_compatibility.py index 1d1beac8..89e05d24 100644 --- a/scripts/verify_ubuntu_compatibility.py +++ b/scripts/verify_ubuntu_compatibility.py @@ -1,28 +1,36 @@ -import subprocess -import os -import sys -import json import datetime -import shutil +import json +import os import pathlib +import shutil +import subprocess +import tempfile # Use absolute path for history file HISTORY_FILE = pathlib.Path.home() / ".cortex" / "security_history.json" +# Command constants (full paths for security - avoids PATH manipulation attacks) +SYSTEMCTL_CMD = "/usr/bin/systemctl" +UFW_CMD = "/usr/sbin/ufw" +SUDO_CMD = "/usr/bin/sudo" +SSH_SERVICE = "ssh" + + def load_history(): """Load past execution history""" if HISTORY_FILE.exists(): try: - with open(HISTORY_FILE, 'r') as f: + with open(HISTORY_FILE) as f: return json.load(f) except json.JSONDecodeError: return [] return [] + def save_history(score, status, details): """Save execution result to history""" HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True) - + history = load_history() record = { "timestamp": datetime.datetime.now().isoformat(), @@ -32,12 +40,13 @@ def save_history(score, status, details): } history.append(record) history = history[-10:] - + with open(HISTORY_FILE, 'w') as f: json.dump(history, f, indent=4) - + return history + def show_trend(history): """Show historical trend (Trend Tracking)""" print("\n=== šŸ“Š Historical Trend Analysis ===") @@ -48,11 +57,11 @@ def show_trend(history): scores = [h["score"] for h in history] avg_score = sum(scores) / len(scores) last_score = scores[-1] - + print(f" History Count: {len(history)} runs") print(f" Average Score: {avg_score:.1f}") print(f" Last Run Score: {last_score}") - + if len(scores) > 1: prev_score = scores[-2] diff = last_score - prev_score @@ -63,43 +72,66 @@ def show_trend(history): else: print(" Trend: āž”ļø Stable") + def fix_firewall(): - """Enable Firewall (Automated Fix)""" + """Enable UFW firewall (Automated Fix). + + Uses sudo -n (non-interactive) to avoid password prompts hanging the script. + """ print("\n [Fixing] Enabling UFW Firewall...") - if not shutil.which("ufw") and not os.path.exists("/usr/sbin/ufw"): - print(" -> āš ļø UFW is not installed. Cannot enable.") - return False + if not shutil.which("ufw") and not os.path.exists(UFW_CMD): + print(" -> āš ļø UFW is not installed. Cannot enable.") + return False try: - subprocess.run(["sudo", "ufw", "enable"], check=True, timeout=30) + subprocess.run( + [SUDO_CMD, "-n", UFW_CMD, "--force", "enable"], + check=True, + timeout=30, + capture_output=True, + text=True + ) print(" -> āœ… Success: Firewall enabled.") return True - except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: - print(f" -> āŒ Failed to enable firewall: {e}") + except subprocess.CalledProcessError as e: + print(f" -> āŒ Failed to enable firewall: {e.stderr or e}") return False + except subprocess.TimeoutExpired: + print(" -> āŒ Command timed out.") + return False + def fix_ssh_config(config_path): - """Disable SSH Root Login (Automated Fix)""" + """Disable SSH root login (Automated Fix). + + Uses atomic write pattern to prevent partial file corruption. + Creates a backup before making changes. + """ print(f"\n [Fixing] Disabling Root Login in {config_path}...") - + if not os.path.exists(config_path): print(f" -> āš ļø Config file not found: {config_path}") return False - backup_path = config_path + ".bak." + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + backup_path = f"{config_path}.bak.{timestamp}" + try: shutil.copy2(config_path, backup_path) print(f" -> Backup created at: {backup_path}") except PermissionError: print(" -> āŒ Failed to create backup (Permission denied). Need sudo?") return False + except OSError as e: + print(f" -> āŒ Failed to create backup: {e}") + return False try: new_lines = [] - with open(config_path, 'r') as f: + with open(config_path) as f: lines = f.readlines() - + fixed = False for line in lines: if line.strip().startswith("PermitRootLogin") and "yes" in line: @@ -108,64 +140,83 @@ def fix_ssh_config(config_path): fixed = True else: new_lines.append(line) - + if fixed: - with open(config_path, 'w') as f: - f.writelines(new_lines) + # Atomic write using temporary file + dir_path = os.path.dirname(config_path) + with tempfile.NamedTemporaryFile( + mode='w', dir=dir_path, delete=False, suffix='.tmp' + ) as tmp_file: + tmp_file.writelines(new_lines) + tmp_path = tmp_file.name + + # Atomic replace + os.replace(tmp_path, config_path) print(" -> āœ… Success: sshd_config updated.") - + print(" -> Restarting sshd service...") res = subprocess.run( - ["sudo", "systemctl", "restart", "ssh"], - capture_output=True, text=True, timeout=30 + [SUDO_CMD, "-n", SYSTEMCTL_CMD, "restart", SSH_SERVICE], + capture_output=True, + text=True, + timeout=30 ) if res.returncode != 0: print(f" -> āš ļø SSH restart failed: {res.stderr}") - return True + print(" -> Please restart SSH service manually.") return True else: print(" -> No changes needed.") return True - except Exception as e: + except PermissionError: + print(" -> āŒ Permission denied. Try running with sudo.") + return False + except OSError as e: print(f" -> āŒ Error during fix: {e}") return False + def _check_firewall_status(): """Helper to check firewall status.""" print("\n[1] Checking Firewall (UFW)...") try: - print(" Running: systemctl is-active ufw") + print(f" Running: {SYSTEMCTL_CMD} is-active ufw") res = subprocess.run( - ["systemctl", "is-active", "ufw"], - capture_output=True, text=True, timeout=10 + [SYSTEMCTL_CMD, "is-active", "ufw"], + capture_output=True, + text=True, + timeout=10 ) output = res.stdout.strip() print(f" Output: '{output}'") - + if res.returncode == 0 and output == "active": print(" -> JUDGEMENT: Firewall is ACTIVE (Score: 100)") return True else: print(" -> JUDGEMENT: Firewall is INACTIVE (Score: 0)") return False - + except FileNotFoundError: print(" -> ERROR: 'systemctl' command not found.") - except Exception as e: + except subprocess.TimeoutExpired: + print(" -> ERROR: Command timed out.") + except OSError as e: print(f" -> ERROR: {e}") return False + def _check_ssh_status(ssh_config): """Helper to check SSH status.""" print("\n[2] Checking SSH Configuration...") score_penalty = 0 needs_fix = False - + if os.path.exists(ssh_config): print(f" File found: {ssh_config}") try: - with open(ssh_config, 'r') as f: + with open(ssh_config) as f: for line in f: parts = line.split() if len(parts) >= 2 and parts[0] == "PermitRootLogin" and parts[1] == "yes": @@ -173,20 +224,21 @@ def _check_ssh_status(ssh_config): score_penalty = 50 needs_fix = True break - + if not needs_fix: print(" -> No 'PermitRootLogin yes' found (Safe)") - + except PermissionError: print(" -> ERROR: Permission denied. Try running with 'sudo'.") else: print(f" -> WARNING: {ssh_config} does not exist.") - + return score_penalty, needs_fix + def verify_security_logic(): print("=== Ubuntu Security Logic Verification ===") - + ufw_active = _check_firewall_status() ssh_config = "/etc/ssh/sshd_config" ssh_penalty, ssh_needs_fix = _check_ssh_status(ssh_config) @@ -198,11 +250,13 @@ def verify_security_logic(): final_score = 0 final_score -= ssh_penalty final_score = max(0, final_score) - + status = "OK" - if final_score < 50: status = "CRITICAL" - elif final_score < 100: status = "WARNING" - + if final_score < 50: + status = "CRITICAL" + elif final_score < 100: + status = "WARNING" + print(f"Current Score: {final_score}") print(f"Status: {status}") @@ -210,9 +264,11 @@ def verify_security_logic(): print("\n... Saving history ...") details = [] ufw_needs_fix = not ufw_active - if ufw_needs_fix: details.append("Firewall Inactive") - if ssh_needs_fix: details.append("Root SSH Allowed") - + if ufw_needs_fix: + details.append("Firewall Inactive") + if ssh_needs_fix: + details.append("Root SSH Allowed") + history = save_history(final_score, status, ", ".join(details)) show_trend(history) @@ -221,7 +277,7 @@ def verify_security_logic(): print("\n=== šŸ› ļø Automated Fixes Available ===") print("Issues detected that can be automatically fixed.") user_input = input("Do you want to apply fixes now? (y/n): ").strip().lower() - + if user_input == 'y': if ufw_needs_fix: fix_firewall() @@ -231,6 +287,7 @@ def verify_security_logic(): else: print("Skipping fixes.") + if __name__ == "__main__": if os.geteuid() != 0: print("NOTE: This script works best with 'sudo' for fixing issues.") From ee2e8e605caf3aae5dd7ef2b478cea3b736989f9 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 18:11:12 +0900 Subject: [PATCH 13/18] fix: resolve syntax error in cli.py and missing newline - Fix invalid indentation in cli.py:health() - Add missing newline at end of scripts/verify_ubuntu_compatibility.py --- scripts/verify_ubuntu_compatibility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/verify_ubuntu_compatibility.py b/scripts/verify_ubuntu_compatibility.py index 89e05d24..87555e84 100644 --- a/scripts/verify_ubuntu_compatibility.py +++ b/scripts/verify_ubuntu_compatibility.py @@ -291,4 +291,4 @@ def verify_security_logic(): if __name__ == "__main__": if os.geteuid() != 0: print("NOTE: This script works best with 'sudo' for fixing issues.") - verify_security_logic() \ No newline at end of file + verify_security_logic() From 9582f3b8282ba8f7548f9fdd3f2180611736c83c Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 18:45:34 +0900 Subject: [PATCH 14/18] style: apply black formatting to remaining files --- cortex/health/checks/disk.py | 8 +- cortex/health/checks/performance.py | 12 +- cortex/health/checks/security.py | 15 +- cortex/health/checks/updates.py | 17 +- cortex/health/monitor.py | 22 +- cortex/hwprofiler.py | 960 +++++------ cortex/utils/commands.py | 658 ++++---- .../automation/cortex-master-quarterback.sh | 1424 ++++++++--------- scripts/automation/cortex-master-update.sh | 602 +++---- scripts/automation/cortex-master.sh | 388 ++--- scripts/automation/cortex-pr-dashboard.sh | 724 ++++----- scripts/automation/focus-on-mvp.sh | 210 +-- scripts/automation/manage_cortex_prs.sh | 870 +++++----- scripts/cortex-cleanup.sh | 294 ++-- scripts/demo_script.sh | 460 +++--- scripts/deployment/audit_cortex_status.sh | 216 +-- scripts/deployment/upload_issue_34.sh | 72 +- scripts/fetch-fork-emails.sh | 144 +- scripts/github/merge-mike-prs.sh | 162 +- scripts/github/organize-issues.sh | 102 +- scripts/github/review-contributor-prs.sh | 628 ++++---- scripts/recruit-ready.sh | 350 ++-- scripts/verify_ubuntu_compatibility.py | 17 +- tests/test_health_monitor.py | 30 +- 24 files changed, 4185 insertions(+), 4200 deletions(-) mode change 100755 => 100644 cortex/hwprofiler.py mode change 100755 => 100644 scripts/automation/cortex-master-quarterback.sh mode change 100755 => 100644 scripts/automation/cortex-master-update.sh mode change 100755 => 100644 scripts/automation/cortex-master.sh mode change 100755 => 100644 scripts/automation/cortex-pr-dashboard.sh mode change 100755 => 100644 scripts/automation/focus-on-mvp.sh mode change 100755 => 100644 scripts/automation/manage_cortex_prs.sh mode change 100755 => 100644 scripts/cortex-cleanup.sh mode change 100755 => 100644 scripts/demo_script.sh mode change 100755 => 100644 scripts/deployment/audit_cortex_status.sh mode change 100755 => 100644 scripts/deployment/upload_issue_34.sh mode change 100755 => 100644 scripts/fetch-fork-emails.sh mode change 100755 => 100644 scripts/github/merge-mike-prs.sh mode change 100755 => 100644 scripts/github/organize-issues.sh mode change 100755 => 100644 scripts/github/review-contributor-prs.sh mode change 100755 => 100644 scripts/recruit-ready.sh diff --git a/cortex/health/checks/disk.py b/cortex/health/checks/disk.py index eece7bde..a45651f0 100644 --- a/cortex/health/checks/disk.py +++ b/cortex/health/checks/disk.py @@ -24,7 +24,7 @@ def run(self) -> CheckResult: status="CRITICAL", details=f"Check failed: {e}", recommendation="Check disk mounts and permissions", - weight=0.20 + weight=0.20, ) # Explicit early returns to avoid static analysis confusion @@ -36,7 +36,7 @@ def run(self) -> CheckResult: status="CRITICAL", details=f"{usage_percent:.1f}% used", recommendation="Clean up disk space immediately", - weight=0.20 + weight=0.20, ) if usage_percent > 80: @@ -47,7 +47,7 @@ def run(self) -> CheckResult: status="WARNING", details=f"{usage_percent:.1f}% used", recommendation="Consider cleaning up disk space", - weight=0.20 + weight=0.20, ) return CheckResult( @@ -57,5 +57,5 @@ def run(self) -> CheckResult: status="OK", details=f"{usage_percent:.1f}% used", recommendation=None, - weight=0.20 + weight=0.20, ) diff --git a/cortex/health/checks/performance.py b/cortex/health/checks/performance.py index 92ba365e..9bd555e0 100644 --- a/cortex/health/checks/performance.py +++ b/cortex/health/checks/performance.py @@ -33,16 +33,16 @@ def run(self) -> CheckResult: # 2. Memory Usage (Linux /proc/meminfo) try: - with open('/proc/meminfo') as f: + with open("/proc/meminfo") as f: meminfo = {} for line in f: - parts = line.split(':') + parts = line.split(":") if len(parts) == 2: meminfo[parts[0].strip()] = int(parts[1].strip().split()[0]) - if 'MemTotal' in meminfo and 'MemAvailable' in meminfo: - total = meminfo['MemTotal'] - avail = meminfo['MemAvailable'] + if "MemTotal" in meminfo and "MemAvailable" in meminfo: + total = meminfo["MemTotal"] + avail = meminfo["MemAvailable"] used_percent = ((total - avail) / total) * 100 if used_percent > 80: @@ -68,5 +68,5 @@ def run(self) -> CheckResult: status=status, details=details, recommendation=rec, - weight=0.20 # 20% + weight=0.20, # 20% ) diff --git a/cortex/health/checks/security.py b/cortex/health/checks/security.py index 2cc015c6..6924eeea 100644 --- a/cortex/health/checks/security.py +++ b/cortex/health/checks/security.py @@ -41,10 +41,7 @@ def run(self) -> CheckResult: try: # Add timeout to prevent hanging (Fixes Reliability Issue) res = subprocess.run( - [SYSTEMCTL_CMD, "is-active", UFW_SERVICE], - capture_output=True, - text=True, - timeout=5 + [SYSTEMCTL_CMD, "is-active", UFW_SERVICE], capture_output=True, text=True, timeout=5 ) # Fix: Use exact match to avoid matching "inactive" which contains "active" if res.returncode == 0 and res.stdout.strip() == "active": @@ -79,12 +76,10 @@ def run(self) -> CheckResult: status=status, details=", ".join(issues) if issues else "Secure", recommendation=", ".join(recommendations) if recommendations else None, - weight=0.35 + weight=0.35, ) - def _check_ssh_root_login( - self, issues: list[str], recommendations: list[str] - ) -> None: + def _check_ssh_root_login(self, issues: list[str], recommendations: list[str]) -> None: """Check if SSH root login is enabled. Args: @@ -103,9 +98,7 @@ def _check_ssh_root_login( parts = stripped.split() if len(parts) >= 2 and parts[1] == "yes": issues.append("Root SSH Allowed") - recommendations.append( - "Disable SSH Root Login in sshd_config" - ) + recommendations.append("Disable SSH Root Login in sshd_config") return except PermissionError: pass # Cannot read config, skip check diff --git a/cortex/health/checks/updates.py b/cortex/health/checks/updates.py index 0a5abc34..c04564e6 100644 --- a/cortex/health/checks/updates.py +++ b/cortex/health/checks/updates.py @@ -31,10 +31,7 @@ def run(self) -> CheckResult: try: # Add timeout to prevent hangs res = subprocess.run( - [APT_CMD, "list", "--upgradable"], - capture_output=True, - text=True, - timeout=30 + [APT_CMD, "list", "--upgradable"], capture_output=True, text=True, timeout=30 ) lines = res.stdout.splitlines() @@ -47,8 +44,8 @@ def run(self) -> CheckResult: pkg_count += 1 # Scoring - score -= (pkg_count * 2) - score -= (sec_count * 10) + score -= pkg_count * 2 + score -= sec_count * 10 except subprocess.TimeoutExpired as e: return CheckResult( @@ -58,7 +55,7 @@ def run(self) -> CheckResult: status="CRITICAL", details=f"Check timed out: {e}", recommendation="Verify package manager configuration", - weight=0.25 + weight=0.25, ) except FileNotFoundError: return CheckResult( @@ -68,7 +65,7 @@ def run(self) -> CheckResult: status="CRITICAL", details="apt command not found", recommendation="This check requires apt package manager", - weight=0.25 + weight=0.25, ) except OSError as e: return CheckResult( @@ -78,7 +75,7 @@ def run(self) -> CheckResult: status="CRITICAL", details=f"Check failed: {e}", recommendation="Verify package manager configuration", - weight=0.25 + weight=0.25, ) status = "OK" @@ -98,5 +95,5 @@ def run(self) -> CheckResult: status=status, details=details, recommendation="Run 'apt upgrade'" if score < 100 else None, - weight=0.25 + weight=0.25, ) diff --git a/cortex/health/monitor.py b/cortex/health/monitor.py index 4d45ac81..627afb2d 100644 --- a/cortex/health/monitor.py +++ b/cortex/health/monitor.py @@ -13,13 +13,13 @@ class CheckResult: """Data class to hold the result of each check.""" - name: str # Item name (e.g. "Disk Space") - category: str # Category (security, updates, performance, disk) - score: int # Score 0-100 - status: str # "OK", "WARNING", "CRITICAL" - details: str # Detailed message + name: str # Item name (e.g. "Disk Space") + category: str # Category (security, updates, performance, disk) + score: int # Score 0-100 + status: str # "OK", "WARNING", "CRITICAL" + details: str # Detailed message recommendation: str | None = None # Recommended action (if any) - weight: float = 1.0 # Weight for weighted average + weight: float = 1.0 # Weight for weighted average class HealthCheck(ABC): @@ -82,9 +82,7 @@ def run_all(self) -> dict: total_weighted_score += result.score * result.weight total_weight += result.weight except Exception as e: - console.print( - f"[red]Error running check {check.__class__.__name__}: {e}[/red]" - ) + console.print(f"[red]Error running check {check.__class__.__name__}: {e}[/red]") final_score = 0 if total_weight > 0: @@ -100,10 +98,10 @@ def run_all(self) -> dict: "score": r.score, "status": r.status, "details": r.details, - "recommendation": r.recommendation + "recommendation": r.recommendation, } for r in results - ] + ], } self._save_history(report) @@ -128,7 +126,7 @@ def _save_history(self, report: dict) -> None: history = history[-100:] try: - with open(self.history_file, 'w') as f: + with open(self.history_file, "w") as f: json.dump(history, f, indent=4) except Exception as e: console.print(f"[yellow]Warning: Could not save health history: {e}[/yellow]") diff --git a/cortex/hwprofiler.py b/cortex/hwprofiler.py old mode 100755 new mode 100644 index d3dcd7e2..0df9f1db --- a/cortex/hwprofiler.py +++ b/cortex/hwprofiler.py @@ -1,480 +1,480 @@ -#!/usr/bin/env python3 -""" -Hardware Profiling System for Cortex Linux -Detects CPU, GPU, RAM, storage, and network capabilities. -""" - -import json -import os -import re -import subprocess -from typing import Any - - -class HardwareProfiler: - """Detects and profiles system hardware.""" - - def __init__(self): - self.cpu_info = None - self.gpu_info = [] - self.ram_info = None - self.storage_info = [] - self.network_info = None - - def detect_cpu(self) -> dict[str, Any]: - """ - Detect CPU information: model, cores, architecture. - - Returns: - dict: CPU information with model, cores, and architecture - """ - cpu_info = {} - - try: - # Read /proc/cpuinfo for CPU details - with open("/proc/cpuinfo") as f: - cpuinfo = f.read() - - # Extract model name - model_match = re.search(r"model name\s*:\s*(.+)", cpuinfo) - if model_match: - cpu_info["model"] = model_match.group(1).strip() - else: - # Fallback for ARM or other architectures - model_match = re.search(r"Processor\s*:\s*(.+)", cpuinfo) - if model_match: - cpu_info["model"] = model_match.group(1).strip() - else: - cpu_info["model"] = "Unknown CPU" - - # Count physical cores - physical_cores = 0 - core_ids = set() - for line in cpuinfo.split("\n"): - if line.startswith("core id"): - core_id = line.split(":")[1].strip() - if core_id: - core_ids.add(core_id) - elif line.startswith("physical id"): - physical_cores = len(core_ids) if core_ids else 0 - - # If we couldn't get physical cores, count logical cores - if physical_cores == 0: - logical_cores = len([l for l in cpuinfo.split("\n") if l.startswith("processor")]) - cpu_info["cores"] = logical_cores - else: - # Get number of physical CPUs - physical_ids = set() - for line in cpuinfo.split("\n"): - if line.startswith("physical id"): - pid = line.split(":")[1].strip() - if pid: - physical_ids.add(pid) - cpu_info["cores"] = len(physical_ids) * len(core_ids) if core_ids else len(core_ids) - - # Fallback: use nproc if available - if cpu_info.get("cores", 0) == 0: - try: - result = subprocess.run(["nproc"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - cpu_info["cores"] = int(result.stdout.strip()) - except (subprocess.TimeoutExpired, ValueError, FileNotFoundError): - pass - - # Detect architecture - try: - result = subprocess.run(["uname", "-m"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - arch = result.stdout.strip() - cpu_info["architecture"] = arch - else: - cpu_info["architecture"] = "unknown" - except (subprocess.TimeoutExpired, FileNotFoundError): - cpu_info["architecture"] = "unknown" - - except Exception as e: - cpu_info = {"model": "Unknown", "cores": 0, "architecture": "unknown", "error": str(e)} - - self.cpu_info = cpu_info - return cpu_info - - def detect_gpu(self) -> list[dict[str, Any]]: - """ - Detect GPU information: vendor, model, VRAM, CUDA version. - - Returns: - list: List of GPU information dictionaries - """ - gpus = [] - - # Detect NVIDIA GPUs - try: - result = subprocess.run( - [ - "nvidia-smi", - "--query-gpu=name,memory.total,driver_version", - "--format=csv,noheader,nounits", - ], - capture_output=True, - text=True, - timeout=2, - ) - if result.returncode == 0: - for line in result.stdout.strip().split("\n"): - if line.strip(): - parts = [p.strip() for p in line.split(",")] - if len(parts) >= 2: - gpu_name = parts[0] - vram_mb = int(parts[1]) if parts[1].isdigit() else 0 - - gpu_info = {"vendor": "NVIDIA", "model": gpu_name, "vram": vram_mb} - - # Try to get CUDA version - try: - cuda_result = subprocess.run( - [ - "nvidia-smi", - "--query-gpu=cuda_version", - "--format=csv,noheader", - ], - capture_output=True, - text=True, - timeout=1, - ) - if cuda_result.returncode == 0 and cuda_result.stdout.strip(): - gpu_info["cuda"] = cuda_result.stdout.strip() - except (subprocess.TimeoutExpired, FileNotFoundError, ValueError): - # Try nvcc as fallback - try: - nvcc_result = subprocess.run( - ["nvcc", "--version"], - capture_output=True, - text=True, - timeout=1, - ) - if nvcc_result.returncode == 0: - version_match = re.search( - r"release (\d+\.\d+)", nvcc_result.stdout - ) - if version_match: - gpu_info["cuda"] = version_match.group(1) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - gpus.append(gpu_info) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - # Detect AMD GPUs using lspci - try: - result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - for line in result.stdout.split("\n"): - if "VGA" in line or "Display" in line: - if "AMD" in line or "ATI" in line or "Radeon" in line: - # Extract model name - model_match = re.search( - r"(?:AMD|ATI|Radeon)[\s/]+([A-Za-z0-9\s]+)", line - ) - model = ( - model_match.group(1).strip() if model_match else "Unknown AMD GPU" - ) - - # Check if we already have this GPU (avoid duplicates) - if not any( - g.get("vendor") == "AMD" and g.get("model") == model for g in gpus - ): - gpu_info = { - "vendor": "AMD", - "model": model, - "vram": None, # AMD VRAM detection requires rocm-smi or other tools - } - - # Try to get VRAM using rocm-smi if available - try: - rocm_result = subprocess.run( - ["rocm-smi", "--showmeminfo", "vram"], - capture_output=True, - text=True, - timeout=1, - ) - if rocm_result.returncode == 0: - # Parse VRAM from rocm-smi output - vram_match = re.search(r"(\d+)\s*MB", rocm_result.stdout) - if vram_match: - gpu_info["vram"] = int(vram_match.group(1)) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - gpus.append(gpu_info) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - # Detect Intel GPUs - try: - result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - for line in result.stdout.split("\n"): - if "VGA" in line or "Display" in line: - if "Intel" in line: - model_match = re.search(r"Intel[^:]*:\s*([^\(]+)", line) - model = ( - model_match.group(1).strip() if model_match else "Unknown Intel GPU" - ) - - if not any( - g.get("vendor") == "Intel" and g.get("model") == model for g in gpus - ): - gpus.append( - { - "vendor": "Intel", - "model": model, - "vram": None, # Intel integrated GPUs share system RAM - } - ) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - self.gpu_info = gpus - return gpus - - def detect_ram(self) -> int: - """ - Detect total RAM in MB. - - Returns: - int: Total RAM in MB - """ - try: - # Read /proc/meminfo - with open("/proc/meminfo") as f: - meminfo = f.read() - - # Extract MemTotal - match = re.search(r"MemTotal:\s+(\d+)\s+kB", meminfo) - if match: - ram_kb = int(match.group(1)) - ram_mb = ram_kb // 1024 - self.ram_info = ram_mb - return ram_mb - else: - self.ram_info = 0 - return 0 - except Exception: - self.ram_info = 0 - return 0 - - def detect_storage(self) -> list[dict[str, Any]]: - """ - Detect storage devices: type and size. - - Returns: - list: List of storage device information - """ - storage_devices = [] - - try: - # Use lsblk to get block device information - result = subprocess.run( - ["lsblk", "-d", "-o", "NAME,TYPE,SIZE", "-n"], - capture_output=True, - text=True, - timeout=2, - ) - - if result.returncode == 0: - for line in result.stdout.strip().split("\n"): - if line.strip(): - parts = line.split() - if len(parts) >= 2: - device_name = parts[0] - - # Skip loop devices and other virtual devices - if device_name.startswith("loop") or device_name.startswith("ram"): - continue - - device_type = parts[1] if len(parts) > 1 else "unknown" - size_str = parts[2] if len(parts) > 2 else "0" - - # Convert size to MB - size_mb = 0 - if "G" in size_str.upper(): - size_mb = int( - float( - re.sub( - r"[^0-9.]", - "", - size_str.replace("G", "").replace("g", ""), - ) - ) - * 1024 - ) - elif "T" in size_str.upper(): - size_mb = int( - float( - re.sub( - r"[^0-9.]", - "", - size_str.replace("T", "").replace("t", ""), - ) - ) - * 1024 - * 1024 - ) - elif "M" in size_str.upper(): - size_mb = int( - float( - re.sub( - r"[^0-9.]", - "", - size_str.replace("M", "").replace("m", ""), - ) - ) - ) - - # Determine storage type - storage_type = "unknown" - device_path = f"/sys/block/{device_name}" - - # Check if it's NVMe - if "nvme" in device_name.lower(): - storage_type = "nvme" - # Check if it's SSD (by checking if it's rotational) - elif os.path.exists(f"{device_path}/queue/rotational"): - try: - with open(f"{device_path}/queue/rotational") as f: - is_rotational = f.read().strip() == "1" - storage_type = "hdd" if is_rotational else "ssd" - except Exception: - storage_type = "unknown" - else: - # Fallback: guess based on device name - if "sd" in device_name.lower(): - storage_type = "hdd" # Default assumption - elif "nvme" in device_name.lower(): - storage_type = "nvme" - - storage_devices.append( - {"type": storage_type, "size": size_mb, "device": device_name} - ) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - self.storage_info = storage_devices - return storage_devices - - def detect_network(self) -> dict[str, Any]: - """ - Detect network capabilities. - - Returns: - dict: Network information including interfaces and speeds - """ - network_info = {"interfaces": [], "max_speed_mbps": 0} - - try: - # Get network interfaces using ip command - result = subprocess.run( - ["ip", "-o", "link", "show"], capture_output=True, text=True, timeout=1 - ) - - if result.returncode == 0: - interfaces = [] - for line in result.stdout.split("\n"): - if ": " in line: - parts = line.split(": ") - if len(parts) >= 2: - interface_name = ( - parts[1].split("@")[0].split()[0] - if "@" in parts[1] - else parts[1].split()[0] - ) - - # Skip loopback - if interface_name == "lo": - continue - - # Try to get interface speed - speed = None - try: - speed_path = f"/sys/class/net/{interface_name}/speed" - if os.path.exists(speed_path): - with open(speed_path) as f: - speed_str = f.read().strip() - if speed_str.isdigit(): - speed = int(speed_str) - except Exception: - pass - - interfaces.append({"name": interface_name, "speed_mbps": speed}) - - if speed and speed > network_info["max_speed_mbps"]: - network_info["max_speed_mbps"] = speed - - network_info["interfaces"] = interfaces - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - self.network_info = network_info - return network_info - - def profile(self) -> dict[str, Any]: - """ - Run complete hardware profiling. - - Returns: - dict: Complete hardware profile in JSON format - """ - # Run all detection methods - cpu = self.detect_cpu() - gpu = self.detect_gpu() - ram = self.detect_ram() - storage = self.detect_storage() - network = self.detect_network() - - # Build result dictionary - result = { - "cpu": { - "model": cpu.get("model", "Unknown"), - "cores": cpu.get("cores", 0), - "architecture": cpu.get("architecture", "unknown"), - }, - "gpu": gpu, - "ram": ram, - "storage": storage, - "network": network, - } - - return result - - def to_json(self, indent: int = 2) -> str: - """ - Convert hardware profile to JSON string. - - Args: - indent: JSON indentation level - - Returns: - str: JSON string representation - """ - profile = self.profile() - return json.dumps(profile, indent=indent) - - -def main(): - """CLI entry point for hardware profiler.""" - import sys - - profiler = HardwareProfiler() - - try: - profile = profiler.profile() - print(profiler.to_json()) - sys.exit(0) - except Exception as e: - print(json.dumps({"error": str(e)}, indent=2), file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() +#!/usr/bin/env python3 +""" +Hardware Profiling System for Cortex Linux +Detects CPU, GPU, RAM, storage, and network capabilities. +""" + +import json +import os +import re +import subprocess +from typing import Any + + +class HardwareProfiler: + """Detects and profiles system hardware.""" + + def __init__(self): + self.cpu_info = None + self.gpu_info = [] + self.ram_info = None + self.storage_info = [] + self.network_info = None + + def detect_cpu(self) -> dict[str, Any]: + """ + Detect CPU information: model, cores, architecture. + + Returns: + dict: CPU information with model, cores, and architecture + """ + cpu_info = {} + + try: + # Read /proc/cpuinfo for CPU details + with open("/proc/cpuinfo") as f: + cpuinfo = f.read() + + # Extract model name + model_match = re.search(r"model name\s*:\s*(.+)", cpuinfo) + if model_match: + cpu_info["model"] = model_match.group(1).strip() + else: + # Fallback for ARM or other architectures + model_match = re.search(r"Processor\s*:\s*(.+)", cpuinfo) + if model_match: + cpu_info["model"] = model_match.group(1).strip() + else: + cpu_info["model"] = "Unknown CPU" + + # Count physical cores + physical_cores = 0 + core_ids = set() + for line in cpuinfo.split("\n"): + if line.startswith("core id"): + core_id = line.split(":")[1].strip() + if core_id: + core_ids.add(core_id) + elif line.startswith("physical id"): + physical_cores = len(core_ids) if core_ids else 0 + + # If we couldn't get physical cores, count logical cores + if physical_cores == 0: + logical_cores = len([l for l in cpuinfo.split("\n") if l.startswith("processor")]) + cpu_info["cores"] = logical_cores + else: + # Get number of physical CPUs + physical_ids = set() + for line in cpuinfo.split("\n"): + if line.startswith("physical id"): + pid = line.split(":")[1].strip() + if pid: + physical_ids.add(pid) + cpu_info["cores"] = len(physical_ids) * len(core_ids) if core_ids else len(core_ids) + + # Fallback: use nproc if available + if cpu_info.get("cores", 0) == 0: + try: + result = subprocess.run(["nproc"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + cpu_info["cores"] = int(result.stdout.strip()) + except (subprocess.TimeoutExpired, ValueError, FileNotFoundError): + pass + + # Detect architecture + try: + result = subprocess.run(["uname", "-m"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + arch = result.stdout.strip() + cpu_info["architecture"] = arch + else: + cpu_info["architecture"] = "unknown" + except (subprocess.TimeoutExpired, FileNotFoundError): + cpu_info["architecture"] = "unknown" + + except Exception as e: + cpu_info = {"model": "Unknown", "cores": 0, "architecture": "unknown", "error": str(e)} + + self.cpu_info = cpu_info + return cpu_info + + def detect_gpu(self) -> list[dict[str, Any]]: + """ + Detect GPU information: vendor, model, VRAM, CUDA version. + + Returns: + list: List of GPU information dictionaries + """ + gpus = [] + + # Detect NVIDIA GPUs + try: + result = subprocess.run( + [ + "nvidia-smi", + "--query-gpu=name,memory.total,driver_version", + "--format=csv,noheader,nounits", + ], + capture_output=True, + text=True, + timeout=2, + ) + if result.returncode == 0: + for line in result.stdout.strip().split("\n"): + if line.strip(): + parts = [p.strip() for p in line.split(",")] + if len(parts) >= 2: + gpu_name = parts[0] + vram_mb = int(parts[1]) if parts[1].isdigit() else 0 + + gpu_info = {"vendor": "NVIDIA", "model": gpu_name, "vram": vram_mb} + + # Try to get CUDA version + try: + cuda_result = subprocess.run( + [ + "nvidia-smi", + "--query-gpu=cuda_version", + "--format=csv,noheader", + ], + capture_output=True, + text=True, + timeout=1, + ) + if cuda_result.returncode == 0 and cuda_result.stdout.strip(): + gpu_info["cuda"] = cuda_result.stdout.strip() + except (subprocess.TimeoutExpired, FileNotFoundError, ValueError): + # Try nvcc as fallback + try: + nvcc_result = subprocess.run( + ["nvcc", "--version"], + capture_output=True, + text=True, + timeout=1, + ) + if nvcc_result.returncode == 0: + version_match = re.search( + r"release (\d+\.\d+)", nvcc_result.stdout + ) + if version_match: + gpu_info["cuda"] = version_match.group(1) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + gpus.append(gpu_info) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + # Detect AMD GPUs using lspci + try: + result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + for line in result.stdout.split("\n"): + if "VGA" in line or "Display" in line: + if "AMD" in line or "ATI" in line or "Radeon" in line: + # Extract model name + model_match = re.search( + r"(?:AMD|ATI|Radeon)[\s/]+([A-Za-z0-9\s]+)", line + ) + model = ( + model_match.group(1).strip() if model_match else "Unknown AMD GPU" + ) + + # Check if we already have this GPU (avoid duplicates) + if not any( + g.get("vendor") == "AMD" and g.get("model") == model for g in gpus + ): + gpu_info = { + "vendor": "AMD", + "model": model, + "vram": None, # AMD VRAM detection requires rocm-smi or other tools + } + + # Try to get VRAM using rocm-smi if available + try: + rocm_result = subprocess.run( + ["rocm-smi", "--showmeminfo", "vram"], + capture_output=True, + text=True, + timeout=1, + ) + if rocm_result.returncode == 0: + # Parse VRAM from rocm-smi output + vram_match = re.search(r"(\d+)\s*MB", rocm_result.stdout) + if vram_match: + gpu_info["vram"] = int(vram_match.group(1)) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + gpus.append(gpu_info) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + # Detect Intel GPUs + try: + result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + for line in result.stdout.split("\n"): + if "VGA" in line or "Display" in line: + if "Intel" in line: + model_match = re.search(r"Intel[^:]*:\s*([^\(]+)", line) + model = ( + model_match.group(1).strip() if model_match else "Unknown Intel GPU" + ) + + if not any( + g.get("vendor") == "Intel" and g.get("model") == model for g in gpus + ): + gpus.append( + { + "vendor": "Intel", + "model": model, + "vram": None, # Intel integrated GPUs share system RAM + } + ) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + self.gpu_info = gpus + return gpus + + def detect_ram(self) -> int: + """ + Detect total RAM in MB. + + Returns: + int: Total RAM in MB + """ + try: + # Read /proc/meminfo + with open("/proc/meminfo") as f: + meminfo = f.read() + + # Extract MemTotal + match = re.search(r"MemTotal:\s+(\d+)\s+kB", meminfo) + if match: + ram_kb = int(match.group(1)) + ram_mb = ram_kb // 1024 + self.ram_info = ram_mb + return ram_mb + else: + self.ram_info = 0 + return 0 + except Exception: + self.ram_info = 0 + return 0 + + def detect_storage(self) -> list[dict[str, Any]]: + """ + Detect storage devices: type and size. + + Returns: + list: List of storage device information + """ + storage_devices = [] + + try: + # Use lsblk to get block device information + result = subprocess.run( + ["lsblk", "-d", "-o", "NAME,TYPE,SIZE", "-n"], + capture_output=True, + text=True, + timeout=2, + ) + + if result.returncode == 0: + for line in result.stdout.strip().split("\n"): + if line.strip(): + parts = line.split() + if len(parts) >= 2: + device_name = parts[0] + + # Skip loop devices and other virtual devices + if device_name.startswith("loop") or device_name.startswith("ram"): + continue + + device_type = parts[1] if len(parts) > 1 else "unknown" + size_str = parts[2] if len(parts) > 2 else "0" + + # Convert size to MB + size_mb = 0 + if "G" in size_str.upper(): + size_mb = int( + float( + re.sub( + r"[^0-9.]", + "", + size_str.replace("G", "").replace("g", ""), + ) + ) + * 1024 + ) + elif "T" in size_str.upper(): + size_mb = int( + float( + re.sub( + r"[^0-9.]", + "", + size_str.replace("T", "").replace("t", ""), + ) + ) + * 1024 + * 1024 + ) + elif "M" in size_str.upper(): + size_mb = int( + float( + re.sub( + r"[^0-9.]", + "", + size_str.replace("M", "").replace("m", ""), + ) + ) + ) + + # Determine storage type + storage_type = "unknown" + device_path = f"/sys/block/{device_name}" + + # Check if it's NVMe + if "nvme" in device_name.lower(): + storage_type = "nvme" + # Check if it's SSD (by checking if it's rotational) + elif os.path.exists(f"{device_path}/queue/rotational"): + try: + with open(f"{device_path}/queue/rotational") as f: + is_rotational = f.read().strip() == "1" + storage_type = "hdd" if is_rotational else "ssd" + except Exception: + storage_type = "unknown" + else: + # Fallback: guess based on device name + if "sd" in device_name.lower(): + storage_type = "hdd" # Default assumption + elif "nvme" in device_name.lower(): + storage_type = "nvme" + + storage_devices.append( + {"type": storage_type, "size": size_mb, "device": device_name} + ) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + self.storage_info = storage_devices + return storage_devices + + def detect_network(self) -> dict[str, Any]: + """ + Detect network capabilities. + + Returns: + dict: Network information including interfaces and speeds + """ + network_info = {"interfaces": [], "max_speed_mbps": 0} + + try: + # Get network interfaces using ip command + result = subprocess.run( + ["ip", "-o", "link", "show"], capture_output=True, text=True, timeout=1 + ) + + if result.returncode == 0: + interfaces = [] + for line in result.stdout.split("\n"): + if ": " in line: + parts = line.split(": ") + if len(parts) >= 2: + interface_name = ( + parts[1].split("@")[0].split()[0] + if "@" in parts[1] + else parts[1].split()[0] + ) + + # Skip loopback + if interface_name == "lo": + continue + + # Try to get interface speed + speed = None + try: + speed_path = f"/sys/class/net/{interface_name}/speed" + if os.path.exists(speed_path): + with open(speed_path) as f: + speed_str = f.read().strip() + if speed_str.isdigit(): + speed = int(speed_str) + except Exception: + pass + + interfaces.append({"name": interface_name, "speed_mbps": speed}) + + if speed and speed > network_info["max_speed_mbps"]: + network_info["max_speed_mbps"] = speed + + network_info["interfaces"] = interfaces + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + self.network_info = network_info + return network_info + + def profile(self) -> dict[str, Any]: + """ + Run complete hardware profiling. + + Returns: + dict: Complete hardware profile in JSON format + """ + # Run all detection methods + cpu = self.detect_cpu() + gpu = self.detect_gpu() + ram = self.detect_ram() + storage = self.detect_storage() + network = self.detect_network() + + # Build result dictionary + result = { + "cpu": { + "model": cpu.get("model", "Unknown"), + "cores": cpu.get("cores", 0), + "architecture": cpu.get("architecture", "unknown"), + }, + "gpu": gpu, + "ram": ram, + "storage": storage, + "network": network, + } + + return result + + def to_json(self, indent: int = 2) -> str: + """ + Convert hardware profile to JSON string. + + Args: + indent: JSON indentation level + + Returns: + str: JSON string representation + """ + profile = self.profile() + return json.dumps(profile, indent=indent) + + +def main(): + """CLI entry point for hardware profiler.""" + import sys + + profiler = HardwareProfiler() + + try: + profile = profiler.profile() + print(profiler.to_json()) + sys.exit(0) + except Exception as e: + print(json.dumps({"error": str(e)}, indent=2), file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/cortex/utils/commands.py b/cortex/utils/commands.py index 2a431dbc..77fde35f 100644 --- a/cortex/utils/commands.py +++ b/cortex/utils/commands.py @@ -1,329 +1,329 @@ -""" -Secure Command Execution Utilities - -This module provides safe command execution with validation and sandboxing. -All commands should go through these utilities to prevent shell injection. -""" - -import logging -import re -import shlex -import subprocess -from dataclasses import dataclass - -logger = logging.getLogger(__name__) - -# Dangerous patterns that should never be executed -DANGEROUS_PATTERNS = [ - # File system destruction - r"rm\s+-rf\s+[/\*]", - r"rm\s+-rf\s+\$", - r"rm\s+--no-preserve-root", - r":\s*\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}", # Fork bomb - # Disk operations - r"dd\s+if=.*of=/dev/", - r"mkfs\.", - r"wipefs", - # Network attacks - r"curl\s+.*\|\s*sh", - r"curl\s+.*\|\s*bash", - r"wget\s+.*\|\s*sh", - r"wget\s+.*\|\s*bash", - r"curl\s+-o\s+-\s+.*\|\s*", - # Code execution - r"\beval\s+", - r'python\s+-c\s+["\'].*exec', - r'python\s+-c\s+["\'].*import\s+os', - r"base64\s+-d\s+.*\|", - r"\$\(.*\)", # Command substitution (dangerous in some contexts) - # System modification - r">\s*/etc/", - r"chmod\s+777", - r"chmod\s+\+s", - r"chown\s+.*:.*\s+/", - # Privilege escalation - r"sudo\s+su\s*$", - r"sudo\s+-i\s*$", - # Environment manipulation - r"export\s+LD_PRELOAD", - r"export\s+LD_LIBRARY_PATH.*=/", -] - -# Commands that are allowed (allowlist for package management) -ALLOWED_COMMAND_PREFIXES = [ - "apt", - "apt-get", - "apt-cache", - "dpkg", - "yum", - "dnf", - "pacman", - "zypper", - "pip", - "pip3", - "npm", - "systemctl", - "service", - "docker", - "docker-compose", - "kubectl", - "git", - "curl", # Only for downloading, not piping to shell - "wget", # Only for downloading, not piping to shell - "tar", - "unzip", - "chmod", - "chown", - "mkdir", - "cp", - "mv", - "ln", - "cat", - "echo", - "tee", - "grep", - "sed", - "awk", - "head", - "tail", - "sort", - "uniq", - "wc", - "ls", - "find", - "which", - "whereis", - "id", - "whoami", - "hostname", - "uname", - "lsb_release", - "nvidia-smi", - "nvcc", - "make", - "cmake", - "gcc", - "g++", - "python", - "python3", - "node", - "java", - "go", - "rustc", - "cargo", -] - - -@dataclass -class CommandResult: - """Result of a command execution.""" - - success: bool - stdout: str - stderr: str - return_code: int - command: str - - -class CommandValidationError(Exception): - """Raised when a command fails validation.""" - - pass - - -def validate_command(command: str, strict: bool = True) -> tuple[bool, str | None]: - """ - Validate a command for security. - - Args: - command: The command string to validate - strict: If True, command must start with an allowed prefix - - Returns: - Tuple of (is_valid, error_message) - """ - if not command or not command.strip(): - return False, "Empty command" - - command = command.strip() - - # Check for dangerous patterns - for pattern in DANGEROUS_PATTERNS: - if re.search(pattern, command, re.IGNORECASE): - return False, f"Dangerous pattern detected: {pattern}" - - # Check for shell metacharacters that could enable injection - dangerous_chars = ["`", "$", "&&", "||", ";", "\n", "\r"] - for char in dangerous_chars: - if char in command: - # Allow some patterns like $(dpkg --print-architecture) - if char == "$" and "$(" in command: - # Only allow specific safe command substitutions - safe_substitutions = [ - "$(dpkg --print-architecture)", - "$(lsb_release -cs)", - "$(uname -r)", - "$(uname -m)", - "$(whoami)", - "$(hostname)", - ] - # Check if all $(...) patterns are in safe list - found_subs = re.findall(r"\$\([^)]+\)", command) - for sub in found_subs: - if sub not in safe_substitutions: - return False, f"Unsafe command substitution: {sub}" - elif char == "&&" or char == "||": - # Allow chained commands, but validate each part - continue - elif char == ";": - # Semicolon is dangerous - could chain arbitrary commands - return False, "Semicolon not allowed in commands" - elif char == "`": - return False, "Backtick command substitution not allowed" - - # Strict mode: command must start with allowed prefix - if strict: - first_word = command.split()[0] - # Handle sudo prefix - if first_word == "sudo": - parts = command.split() - if len(parts) > 1: - first_word = parts[1] - - if first_word not in ALLOWED_COMMAND_PREFIXES: - return False, f"Command '{first_word}' is not in the allowlist" - - return True, None - - -def sanitize_command(command: str) -> str: - """ - Sanitize a command by removing potentially dangerous elements. - - Args: - command: The command to sanitize - - Returns: - Sanitized command string - """ - # Remove null bytes - command = command.replace("\x00", "") - - # Remove control characters - command = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", "", command) - - # Normalize whitespace - command = " ".join(command.split()) - - return command - - -def run_command( - command: str, - timeout: int = 300, - validate: bool = True, - use_shell: bool = False, - capture_output: bool = True, - cwd: str | None = None, -) -> CommandResult: - """ - Execute a command safely with validation. - - Args: - command: The command to execute - timeout: Maximum execution time in seconds - validate: Whether to validate the command before execution - use_shell: Use shell execution (less secure, only for complex commands) - capture_output: Capture stdout/stderr - cwd: Working directory for command execution - - Returns: - CommandResult with execution details - - Raises: - CommandValidationError: If command fails validation - """ - # Sanitize input - command = sanitize_command(command) - - # Validate if requested - if validate: - is_valid, error = validate_command(command, strict=True) - if not is_valid: - raise CommandValidationError(f"Command validation failed: {error}") - - try: - if use_shell: - # Shell execution - use with caution - # Only allow if command has been validated - result = subprocess.run( - command, - shell=True, - capture_output=capture_output, - text=True, - timeout=timeout, - cwd=cwd, - ) - else: - # Safer: parse command and execute without shell - # This prevents most injection attacks - args = shlex.split(command) - result = subprocess.run( - args, capture_output=capture_output, text=True, timeout=timeout, cwd=cwd - ) - - return CommandResult( - success=result.returncode == 0, - stdout=result.stdout if capture_output else "", - stderr=result.stderr if capture_output else "", - return_code=result.returncode, - command=command, - ) - - except subprocess.TimeoutExpired: - return CommandResult( - success=False, - stdout="", - stderr=f"Command timed out after {timeout} seconds", - return_code=-1, - command=command, - ) - except FileNotFoundError as e: - return CommandResult( - success=False, - stdout="", - stderr=f"Command not found: {e}", - return_code=-1, - command=command, - ) - except Exception as e: - logger.exception(f"Error executing command: {command}") - return CommandResult( - success=False, stdout="", stderr=str(e), return_code=-1, command=command - ) - - -def run_command_chain( - commands: list[str], timeout_per_command: int = 300, stop_on_error: bool = True -) -> list[CommandResult]: - """ - Execute a chain of commands safely. - - Args: - commands: List of commands to execute - timeout_per_command: Timeout for each command - stop_on_error: Stop execution if a command fails - - Returns: - List of CommandResult for each command - """ - results = [] - - for command in commands: - result = run_command(command, timeout=timeout_per_command) - results.append(result) - - if not result.success and stop_on_error: - break - - return results +""" +Secure Command Execution Utilities + +This module provides safe command execution with validation and sandboxing. +All commands should go through these utilities to prevent shell injection. +""" + +import logging +import re +import shlex +import subprocess +from dataclasses import dataclass + +logger = logging.getLogger(__name__) + +# Dangerous patterns that should never be executed +DANGEROUS_PATTERNS = [ + # File system destruction + r"rm\s+-rf\s+[/\*]", + r"rm\s+-rf\s+\$", + r"rm\s+--no-preserve-root", + r":\s*\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}", # Fork bomb + # Disk operations + r"dd\s+if=.*of=/dev/", + r"mkfs\.", + r"wipefs", + # Network attacks + r"curl\s+.*\|\s*sh", + r"curl\s+.*\|\s*bash", + r"wget\s+.*\|\s*sh", + r"wget\s+.*\|\s*bash", + r"curl\s+-o\s+-\s+.*\|\s*", + # Code execution + r"\beval\s+", + r'python\s+-c\s+["\'].*exec', + r'python\s+-c\s+["\'].*import\s+os', + r"base64\s+-d\s+.*\|", + r"\$\(.*\)", # Command substitution (dangerous in some contexts) + # System modification + r">\s*/etc/", + r"chmod\s+777", + r"chmod\s+\+s", + r"chown\s+.*:.*\s+/", + # Privilege escalation + r"sudo\s+su\s*$", + r"sudo\s+-i\s*$", + # Environment manipulation + r"export\s+LD_PRELOAD", + r"export\s+LD_LIBRARY_PATH.*=/", +] + +# Commands that are allowed (allowlist for package management) +ALLOWED_COMMAND_PREFIXES = [ + "apt", + "apt-get", + "apt-cache", + "dpkg", + "yum", + "dnf", + "pacman", + "zypper", + "pip", + "pip3", + "npm", + "systemctl", + "service", + "docker", + "docker-compose", + "kubectl", + "git", + "curl", # Only for downloading, not piping to shell + "wget", # Only for downloading, not piping to shell + "tar", + "unzip", + "chmod", + "chown", + "mkdir", + "cp", + "mv", + "ln", + "cat", + "echo", + "tee", + "grep", + "sed", + "awk", + "head", + "tail", + "sort", + "uniq", + "wc", + "ls", + "find", + "which", + "whereis", + "id", + "whoami", + "hostname", + "uname", + "lsb_release", + "nvidia-smi", + "nvcc", + "make", + "cmake", + "gcc", + "g++", + "python", + "python3", + "node", + "java", + "go", + "rustc", + "cargo", +] + + +@dataclass +class CommandResult: + """Result of a command execution.""" + + success: bool + stdout: str + stderr: str + return_code: int + command: str + + +class CommandValidationError(Exception): + """Raised when a command fails validation.""" + + pass + + +def validate_command(command: str, strict: bool = True) -> tuple[bool, str | None]: + """ + Validate a command for security. + + Args: + command: The command string to validate + strict: If True, command must start with an allowed prefix + + Returns: + Tuple of (is_valid, error_message) + """ + if not command or not command.strip(): + return False, "Empty command" + + command = command.strip() + + # Check for dangerous patterns + for pattern in DANGEROUS_PATTERNS: + if re.search(pattern, command, re.IGNORECASE): + return False, f"Dangerous pattern detected: {pattern}" + + # Check for shell metacharacters that could enable injection + dangerous_chars = ["`", "$", "&&", "||", ";", "\n", "\r"] + for char in dangerous_chars: + if char in command: + # Allow some patterns like $(dpkg --print-architecture) + if char == "$" and "$(" in command: + # Only allow specific safe command substitutions + safe_substitutions = [ + "$(dpkg --print-architecture)", + "$(lsb_release -cs)", + "$(uname -r)", + "$(uname -m)", + "$(whoami)", + "$(hostname)", + ] + # Check if all $(...) patterns are in safe list + found_subs = re.findall(r"\$\([^)]+\)", command) + for sub in found_subs: + if sub not in safe_substitutions: + return False, f"Unsafe command substitution: {sub}" + elif char == "&&" or char == "||": + # Allow chained commands, but validate each part + continue + elif char == ";": + # Semicolon is dangerous - could chain arbitrary commands + return False, "Semicolon not allowed in commands" + elif char == "`": + return False, "Backtick command substitution not allowed" + + # Strict mode: command must start with allowed prefix + if strict: + first_word = command.split()[0] + # Handle sudo prefix + if first_word == "sudo": + parts = command.split() + if len(parts) > 1: + first_word = parts[1] + + if first_word not in ALLOWED_COMMAND_PREFIXES: + return False, f"Command '{first_word}' is not in the allowlist" + + return True, None + + +def sanitize_command(command: str) -> str: + """ + Sanitize a command by removing potentially dangerous elements. + + Args: + command: The command to sanitize + + Returns: + Sanitized command string + """ + # Remove null bytes + command = command.replace("\x00", "") + + # Remove control characters + command = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", "", command) + + # Normalize whitespace + command = " ".join(command.split()) + + return command + + +def run_command( + command: str, + timeout: int = 300, + validate: bool = True, + use_shell: bool = False, + capture_output: bool = True, + cwd: str | None = None, +) -> CommandResult: + """ + Execute a command safely with validation. + + Args: + command: The command to execute + timeout: Maximum execution time in seconds + validate: Whether to validate the command before execution + use_shell: Use shell execution (less secure, only for complex commands) + capture_output: Capture stdout/stderr + cwd: Working directory for command execution + + Returns: + CommandResult with execution details + + Raises: + CommandValidationError: If command fails validation + """ + # Sanitize input + command = sanitize_command(command) + + # Validate if requested + if validate: + is_valid, error = validate_command(command, strict=True) + if not is_valid: + raise CommandValidationError(f"Command validation failed: {error}") + + try: + if use_shell: + # Shell execution - use with caution + # Only allow if command has been validated + result = subprocess.run( + command, + shell=True, + capture_output=capture_output, + text=True, + timeout=timeout, + cwd=cwd, + ) + else: + # Safer: parse command and execute without shell + # This prevents most injection attacks + args = shlex.split(command) + result = subprocess.run( + args, capture_output=capture_output, text=True, timeout=timeout, cwd=cwd + ) + + return CommandResult( + success=result.returncode == 0, + stdout=result.stdout if capture_output else "", + stderr=result.stderr if capture_output else "", + return_code=result.returncode, + command=command, + ) + + except subprocess.TimeoutExpired: + return CommandResult( + success=False, + stdout="", + stderr=f"Command timed out after {timeout} seconds", + return_code=-1, + command=command, + ) + except FileNotFoundError as e: + return CommandResult( + success=False, + stdout="", + stderr=f"Command not found: {e}", + return_code=-1, + command=command, + ) + except Exception as e: + logger.exception(f"Error executing command: {command}") + return CommandResult( + success=False, stdout="", stderr=str(e), return_code=-1, command=command + ) + + +def run_command_chain( + commands: list[str], timeout_per_command: int = 300, stop_on_error: bool = True +) -> list[CommandResult]: + """ + Execute a chain of commands safely. + + Args: + commands: List of commands to execute + timeout_per_command: Timeout for each command + stop_on_error: Stop execution if a command fails + + Returns: + List of CommandResult for each command + """ + results = [] + + for command in commands: + result = run_command(command, timeout=timeout_per_command) + results.append(result) + + if not result.success and stop_on_error: + break + + return results diff --git a/scripts/automation/cortex-master-quarterback.sh b/scripts/automation/cortex-master-quarterback.sh old mode 100755 new mode 100644 index 982fc0d8..fec81fc2 --- a/scripts/automation/cortex-master-quarterback.sh +++ b/scripts/automation/cortex-master-quarterback.sh @@ -1,712 +1,712 @@ -#!/bin/bash -# CORTEX LINUX - MASTER QUARTERBACK SCRIPT -# Manages team onboarding, issue assignment, PR reviews, and project coordination -# Created: November 17, 2025 -# Usage: bash cortex-master-quarterback.sh - -set -e - -echo "🧠 CORTEX LINUX - MASTER QUARTERBACK SCRIPT" -echo "===========================================" -echo "" -echo "This script will:" -echo " 1. Welcome new developers individually" -echo " 2. Assign issues based on expertise" -echo " 3. Review and advance ready PRs" -echo " 4. Coordinate team activities" -echo "" - -# Configuration -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") - -if [ -z "$GITHUB_TOKEN" ]; then - echo "āŒ ERROR: GITHUB_TOKEN not found in ~/.zshrc" - echo "Please add: export GITHUB_TOKEN='your_token_here'" - exit 1 -fi - -# Check if gh CLI is installed -if ! command -v gh &> /dev/null; then - echo "āŒ ERROR: GitHub CLI (gh) not installed" - echo "Install with: brew install gh" - exit 1 -fi - -# Authenticate gh CLI -export GH_TOKEN="$GITHUB_TOKEN" - -echo "āœ… Configuration loaded" -echo "šŸ“Š Repository: $REPO" -echo "" - -# ============================================================================ -# SECTION 1: WELCOME NEW DEVELOPERS -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ‘‹ SECTION 1: WELCOMING NEW DEVELOPERS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Function to welcome a developer -welcome_developer() { - local username=$1 - local name=$2 - local location=$3 - local skills=$4 - local strength=$5 - local recommended_issues=$6 - - echo "šŸ“ Welcoming @$username ($name)..." - - # Create welcome comment - welcome_msg="šŸ‘‹ **Welcome to Cortex Linux, @$username!** - -We're thrilled to have you join our mission to build the AI-native operating system! - -## šŸŽÆ Your Profile Highlights -**Location:** $location -**Primary Skills:** $skills -**Key Strength:** $strength - -## šŸ’” Recommended Issues for You -$recommended_issues - -## šŸš€ Getting Started - -1. **Join our Discord**: https://discord.gg/uCqHvxjU83 (#dev-questions channel) -2. **Review Contributing Guide**: Check repo README and CONTRIBUTING.md -3. **Comment on issues** you're interested in - we'll provide starter code to accelerate development - -## šŸ’° Compensation Structure - -- **Cash bounties** on merge: \$25-200 depending on complexity -- **2x bonus** when we close our \$2-3M seed round (February 2025) -- **Founding team opportunities** for top contributors (equity post-funding) - -## šŸ¤ Our Development Model - -We use a **hybrid approach** that's proven successful: -- Mike + Claude generate complete implementations -- Contributors test, integrate, and validate -- 63% cost savings, 80% time savings -- Everyone wins with professional baseline code - -## šŸ“‹ Next Steps - -1. Browse issues and comment on ones that interest you -2. We'll provide starter code to save you time -3. Test, integrate, and submit PR -4. Get paid on merge! šŸŽ‰ - -**Questions?** Tag @mikejmorgan-ai in any issue or drop into Discord. - -Let's build something revolutionary together! 🧠⚔ - ---- -*Automated welcome from Cortex Team Management System*" - - echo "$welcome_msg" - echo "" - echo "Would you like to post this welcome to @$username's recent activity? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - # Find their most recent issue comment or PR - recent_activity=$(gh api "/repos/$REPO/issues?state=all&creator=$username&per_page=1" 2>/dev/null | jq -r '.[0].number' 2>/dev/null) - - if [ ! -z "$recent_activity" ] && [ "$recent_activity" != "null" ]; then - echo " Posting welcome to Issue/PR #$recent_activity..." - echo "$welcome_msg" | gh issue comment $recent_activity --body-file - --repo $REPO 2>/dev/null || echo " āš ļø Could not post (may need manual posting)" - echo " āœ… Welcome posted!" - else - echo " ā„¹ļø No recent activity found - save welcome message for their first interaction" - fi - else - echo " ā­ļø Skipped posting (you can post manually later)" - fi - - echo "" -} - -# Welcome each new developer -echo "Welcoming 5 new developers..." -echo "" - -welcome_developer \ - "AbuBakar877" \ - "Abu Bakar" \ - "Turkey šŸ‡¹šŸ‡·" \ - "Node.js, React, Angular, Full-stack web development" \ - "Modern JavaScript frameworks and web UI" \ - "- **Issue #27** (Progress Notifications UI) - \$100-150 - Perfect for your frontend skills -- **Issue #26** (User Preferences UI) - \$100-150 - Web interface components -- **Issue #33** (Config Export/Import) - \$75-100 - Data handling + UI" - -welcome_developer \ - "aliraza556" \ - "Ali Raza" \ - "Global Developer šŸŒ" \ - "Full-stack (1000+ contributions), Multi-language expert" \ - "Elite-tier developer with proven track record" \ - "- **Issue #14** (Rollback System) - \$150-200 - āœ… **ALREADY ASSIGNED** - You've got this! -- **Issue #12** (Dependency Resolution) - \$150-200 - Complex logic, perfect match -- **Issue #30** (Self-Update System) - \$150-200 - Advanced architecture -- **Issue #31** (Plugin System) - \$200-300 - Architectural design challenge" - -welcome_developer \ - "anees4500" \ - "Anees" \ - "Location TBD" \ - "Java, C, Python, JavaScript, CDC/Batch processing" \ - "Multi-language capability with data processing experience" \ - "- **Issue #32** (Batch Operations) - \$100-150 - Your CDC experience is perfect here -- **Issue #28** (Requirements Check) - \$75-100 - Systems validation -- **Issue #10** (Installation Verification) - \$100-150 - Backend validation work" - -welcome_developer \ - "brymut" \ - "Bryan Mutai" \ - "Nairobi, Kenya šŸ‡°šŸ‡Ŗ" \ - "TypeScript, Python, PHP, JavaScript - Full-stack with backend focus" \ - "Architectural thinking with perfect skill stack (TypeScript + Python)" \ - "- **Issue #31** (Plugin System) - \$200-300 - **HIGHLY RECOMMENDED** - Architectural perfect match -- **Issue #26** (User Preferences) - \$100-150 - API design + backend -- **Issue #20** (Context Memory) - \$150-200 - TypeScript+Python combo ideal -- **Issue #25** (Network/Proxy Config) - \$150-200 - Backend + systems" - -welcome_developer \ - "shalinibhavi525-sudo" \ - "Shalini Bhavi" \ - "Ireland šŸ‡®šŸ‡Ŗ" \ - "Python, JavaScript, HTML - Documentation focus" \ - "Documentation specialist with web UI skills" \ - "- **Issue #15** (Documentation) - \$100-150 - āœ… **ALREADY ASSIGNED** - Perfect match! -- **Issue #27** (Progress Notifications) - \$100-150 - User-facing UI work -- Testing bounties - \$50-75 - Validate implementations from other devs" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 1 Complete: Developer welcomes prepared" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# SECTION 2: ISSUE ASSIGNMENTS -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸŽÆ SECTION 2: STRATEGIC ISSUE ASSIGNMENTS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "Analyzing current issue status..." - -# Function to assign issue -assign_issue() { - local issue_num=$1 - local developer=$2 - local reason=$3 - - echo "" - echo "šŸ“Œ Assigning Issue #$issue_num to @$developer" - echo " Reason: $reason" - - # Check if issue exists and is unassigned - issue_info=$(gh issue view $issue_num --repo $REPO --json number,title,assignees,state 2>/dev/null || echo "") - - if [ -z "$issue_info" ]; then - echo " āš ļø Issue #$issue_num not found or not accessible" - return - fi - - # Check if already assigned - assignee_count=$(echo "$issue_info" | jq '.assignees | length') - - if [ "$assignee_count" -gt 0 ]; then - current_assignee=$(echo "$issue_info" | jq -r '.assignees[0].login') - echo " ā„¹ļø Already assigned to @$current_assignee - skipping" - return - fi - - echo " Proceed with assignment? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - gh issue edit $issue_num --add-assignee $developer --repo $REPO 2>/dev/null && \ - echo " āœ… Assigned!" || \ - echo " āš ļø Could not assign (may need manual assignment)" - - # Add comment explaining assignment - assignment_comment="šŸŽÆ **Assigned to @$developer** - -**Why you're perfect for this:** $reason - -**Next Steps:** -1. Review the issue description and acceptance criteria -2. Comment if you'd like starter code from our hybrid development model -3. We can provide complete implementation for testing/integration (\$50-75) -4. Or build from scratch for full bounty - -**Questions?** Just ask! We're here to help you succeed. - ---- -*Automated assignment from Cortex Team Management*" - - echo "$assignment_comment" | gh issue comment $issue_num --body-file - --repo $REPO 2>/dev/null || true - else - echo " ā­ļø Skipped" - fi -} - -echo "" -echo "šŸ”“ CRITICAL PATH ASSIGNMENTS (MVP Blockers)" -echo "─────────────────────────────────────────" - -# Issue #7 - Already assigned to chandrapratnamar, but check if help needed -echo "" -echo "Issue #7 (Package Manager Wrapper) - THE critical blocker" -echo " Current: Assigned to @chandrapratnamar (PR #17 in progress)" -echo " Status: Check if they need assistance" -echo " Action: Monitor weekly, offer @aliraza556 or @brymut for code review" -echo "" - -# Issue #10 - Installation Verification -assign_issue 10 "aliraza556" "Elite developer, perfect for systems validation work. Code is ready from Mike." - -# Issue #12 - Dependency Resolution -assign_issue 12 "brymut" "TypeScript+Python skills ideal for complex dependency logic. Mike has complete implementation." - -# Issue #14 - Already assigned to aliraza556 -echo "" -echo "Issue #14 (Rollback System) - āœ… Already assigned to @aliraza556" -echo " Action: Check PR status, offer review assistance" -echo "" - -echo "" -echo "🟔 HIGH PRIORITY ASSIGNMENTS" -echo "─────────────────────────────" - -# Issue #20/24 - Context Memory -assign_issue 20 "brymut" "Architectural experience + TypeScript/Python combo. Mike has implementation ready." - -# Issue #29 - Logging System -assign_issue 29 "anees4500" "Backend infrastructure work, good first complex task to assess quality." - -echo "" -echo "🟢 MEDIUM PRIORITY ASSIGNMENTS" -echo "───────────────────────────────" - -# Issue #25 - Network Config -assign_issue 25 "brymut" "Backend + systems knowledge required for proxy/network configuration." - -# Issue #26 - User Preferences -assign_issue 26 "AbuBakar877" "API + UI components match your full-stack web background." - -# Issue #27 - Progress Notifications -assign_issue 27 "AbuBakar877" "Frontend UI focus, perfect for your React/Angular experience." - -# Issue #28 - Requirements Check -assign_issue 28 "anees4500" "Systems validation, good complement to your batch processing skills." - -echo "" -echo "šŸ”µ ADVANCED FEATURE ASSIGNMENTS" -echo "────────────────────────────────" - -# Issue #30 - Self-Update -assign_issue 30 "aliraza556" "Complex systems integration needs elite-tier developer." - -# Issue #31 - Plugin System -assign_issue 31 "brymut" "**HIGHEST RECOMMENDATION** - Architectural design matches your background perfectly." - -# Issue #32 - Batch Operations -assign_issue 32 "anees4500" "Your CDC/batch processing experience is ideal match." - -# Issue #33 - Config Export/Import -assign_issue 33 "shalinibhavi525-sudo" "Data handling + web UI, complements your documentation work." - -# Issue #15 - Already assigned -echo "" -echo "Issue #15 (Documentation) - āœ… Already assigned to @shalinibhavi525-sudo" -echo " Action: Check progress, offer assistance if needed" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 2 Complete: Strategic assignments made" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# SECTION 3: PULL REQUEST REVIEW -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ” SECTION 3: PULL REQUEST REVIEW & ADVANCEMENT" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "Fetching open pull requests..." - -# Get all open PRs -prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,mergeable,reviewDecision --limit 50 2>/dev/null || echo "[]") - -pr_count=$(echo "$prs" | jq 'length') - -echo "Found $pr_count open pull requests" -echo "" - -if [ "$pr_count" -eq 0 ]; then - echo "āœ… No open PRs to review" -else - echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - \(.reviewDecision // "PENDING")"' - echo "" - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "PR REVIEW PRIORITIES" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - - # Critical PRs (Issue #7 related) - echo "šŸ”“ CRITICAL - Package Manager (Issue #7)" - echo "PR #17 by @chandrapratnamar" - echo " Action: Review immediately, this is THE MVP blocker" - echo " Review criteria:" - echo " - Does it translate natural language to apt commands?" - echo " - Are tests comprehensive?" - echo " - Does it integrate with LLM layer?" - echo "" - - echo "🟔 HIGH PRIORITY - MVP Features" - echo "Check for PRs related to:" - echo " - Issue #10 (Installation Verification)" - echo " - Issue #12 (Dependency Resolution)" - echo " - Issue #14 (Rollback System)" - echo " - Issue #13 (Error Parser) - PR #23 by @AbdulKadir877" - echo "" - - echo "🟢 STANDARD PRIORITY - All other PRs" - echo "Review remaining PRs in order received" - echo "" - - echo "Would you like to review PRs interactively? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "" - echo "Opening PR review interface..." - echo "" - - # For each PR, offer review options - echo "$prs" | jq -r '.[] | .number' | while read pr_num; do - pr_info=$(gh pr view $pr_num --repo $REPO --json number,title,author,body 2>/dev/null) - pr_title=$(echo "$pr_info" | jq -r '.title') - pr_author=$(echo "$pr_info" | jq -r '.author.login') - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "Reviewing PR #$pr_num: $pr_title" - echo "Author: @$pr_author" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Actions:" - echo " [v] View PR in browser" - echo " [a] Approve PR" - echo " [c] Request changes" - echo " [m] Add comment" - echo " [s] Skip to next" - echo " [q] Quit review mode" - echo "" - echo -n "Choose action: " - read -n 1 action - echo "" - - case $action in - v|V) - gh pr view $pr_num --repo $REPO --web - ;; - a|A) - echo "āœ… Approving PR #$pr_num..." - gh pr review $pr_num --repo $REPO --approve --body "āœ… **APPROVED** - -Excellent work @$pr_author! This implementation: -- Meets acceptance criteria -- Includes comprehensive tests -- Integrates well with existing architecture -- Documentation is clear - -**Next Steps:** -1. Merging this PR -2. Bounty will be processed -3. Thank you for your contribution! - -šŸŽ‰ Welcome to the Cortex Linux contributor team!" - echo "Would you like to merge now? (y/n)" - read -n 1 merge_now - echo "" - if [[ $merge_now =~ ^[Yy]$ ]]; then - gh pr merge $pr_num --repo $REPO --squash --delete-branch - echo "āœ… Merged and branch deleted!" - fi - ;; - c|C) - echo "Enter feedback (press Ctrl+D when done):" - feedback=$(cat) - gh pr review $pr_num --repo $REPO --request-changes --body "šŸ”„ **Changes Requested** - -Thanks for your work @$pr_author! Here's what needs attention: - -$feedback - -**Please update and let me know when ready for re-review.** - -We're here to help if you have questions!" - ;; - m|M) - echo "Enter comment (press Ctrl+D when done):" - comment=$(cat) - gh pr comment $pr_num --repo $REPO --body "$comment" - echo "āœ… Comment added" - ;; - q|Q) - echo "Exiting review mode..." - break - ;; - *) - echo "Skipping..." - ;; - esac - echo "" - done - else - echo "ā­ļø Skipped interactive review" - echo " You can review PRs manually at: https://github.com/$REPO/pulls" - fi -fi - -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 3 Complete: PR review assistance provided" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# SECTION 4: TEAM COORDINATION -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ¤ SECTION 4: TEAM COORDINATION & NEXT ACTIONS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ“Š CURRENT PROJECT STATUS" -echo "─────────────────────────" -echo "" - -# Count issues by status -total_issues=$(gh issue list --repo $REPO --limit 1000 --json number 2>/dev/null | jq 'length') -open_issues=$(gh issue list --repo $REPO --state open --limit 1000 --json number 2>/dev/null | jq 'length') -closed_issues=$(gh issue list --repo $REPO --state closed --limit 1000 --json number 2>/dev/null | jq 'length') - -echo "Issues:" -echo " Total: $total_issues" -echo " Open: $open_issues" -echo " Closed: $closed_issues" -echo "" - -# Count PRs -open_prs=$(gh pr list --repo $REPO --state open --json number 2>/dev/null | jq 'length') -merged_prs=$(gh pr list --repo $REPO --state merged --limit 100 --json number 2>/dev/null | jq 'length') - -echo "Pull Requests:" -echo " Open: $open_prs" -echo " Merged (recent): $merged_prs" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸŽÆ IMMEDIATE ACTION ITEMS (Priority Order)" -echo "──────────────────────────────────────────" -echo "" - -echo "1. šŸ”“ CRITICAL - Check Issue #7 Progress" -echo " - PR #17 by @chandrapratnamar" -echo " - This is THE MVP blocker" -echo " - Review weekly, offer assistance" -echo " - Command: gh pr view 17 --repo $REPO --web" -echo "" - -echo "2. 🟔 HIGH - Review Ready PRs" -echo " - PR #23 (Error Parser) by @AbdulKadir877" -echo " - Any PRs marked 'ready-for-review'" -echo " - Command: gh pr list --repo $REPO --label ready-for-review" -echo "" - -echo "3. 🟢 MEDIUM - Upload Complete Implementations" -echo " - Issue #10 (Installation Verification) - Code ready" -echo " - Issue #12 (Dependency Resolution) - Code ready" -echo " - Issue #14 (Rollback System) - Code ready with @aliraza556" -echo " - Use: ~/cortex/cortex-master-pr-creator.sh" -echo "" - -echo "4. šŸ”µ ENGAGE NEW DEVELOPERS" -echo " - Post welcome messages (generated above)" -echo " - Monitor their first comments/PRs" -echo " - Offer starter code to accelerate" -echo "" - -echo "5. šŸ’° PROCESS BOUNTIES" -echo " - Track merged PRs" -echo " - Calculate owed bounties" -echo " - Process payments (crypto for international)" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ“‹ RECOMMENDED WEEKLY ROUTINE" -echo "─────────────────────────────" -echo "" -echo "Monday:" -echo " - Run this quarterback script" -echo " - Review critical path (Issue #7)" -echo " - Merge ready PRs" -echo "" -echo "Wednesday:" -echo " - Check new issues/comments" -echo " - Respond to developer questions" -echo " - Upload any ready implementations" -echo "" -echo "Friday:" -echo " - Process bounty payments" -echo " - Update team on Discord" -echo " - Plan next week priorities" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ”— QUICK LINKS" -echo "──────────────" -echo "" -echo "Repository: https://github.com/$REPO" -echo "Open Issues: https://github.com/$REPO/issues" -echo "Open PRs: https://github.com/$REPO/pulls" -echo "Discord: https://discord.gg/uCqHvxjU83" -echo "Project Board: https://github.com/orgs/cortexlinux/projects" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ“± POST TO DISCORD" -echo "──────────────────" -echo "" - -discord_announcement="šŸŽ‰ **Team Update - November 17, 2025** - -**Welcome 5 New Developers!** -- @AbuBakar877 (Turkey) - Full-stack web specialist -- @aliraza556 (Global) - Elite tier, 1000+ contributions -- @anees4500 - Multi-language backend expert -- @brymut (Kenya) - TypeScript + Python architect -- @shalinibhavi525-sudo (Ireland) - Documentation specialist - -**Strategic Assignments Made:** -- Issue #31 (Plugin System) → @brymut (architectural perfect match) -- Issue #10 (Installation Verification) → @aliraza556 -- Issue #32 (Batch Operations) → @anees4500 -- Issue #27 (Progress UI) → @AbuBakar877 -- Issue #15 (Documentation) → @shalinibhavi525-sudo āœ… - -**Critical Path:** -- Issue #7 (Package Manager) - THE blocker - @chandrapratnamar working PR #17 -- Monitoring weekly, need completion for MVP - -**Ready to Review:** -- Multiple PRs waiting for review -- Bounties ready to process on merge - -**The Hybrid Model Works:** -- 63% cost savings -- 80% time savings -- Professional baseline + contributor validation -- Win-win for everyone - -šŸ’° **Bounties:** \$25-200 on merge + 2x bonus at funding -šŸŽÆ **Goal:** MVP complete for February 2025 seed round -šŸ’¼ **Opportunities:** Founding team roles for top contributors - -Browse issues: https://github.com/$REPO/issues -Questions? #dev-questions channel - -Let's build the future of Linux! 🧠⚔" - -echo "$discord_announcement" -echo "" -echo "Copy the above message and post to Discord #announcements" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 4 Complete: Team coordination completed" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# FINAL SUMMARY -# ============================================================================ - -echo "" -echo "════════════════════════════════════════════════════════" -echo "šŸ† CORTEX QUARTERBACK SCRIPT - EXECUTION COMPLETE" -echo "════════════════════════════════════════════════════════" -echo "" - -echo "šŸ“Š EXECUTION SUMMARY" -echo "────────────────────" -echo "" -echo "āœ… 5 developers welcomed with personalized messages" -echo "āœ… 10+ strategic issue assignments made" -echo "āœ… PR review guidance provided" -echo "āœ… Team coordination plan established" -echo "āœ… Discord announcement prepared" -echo "" - -echo "šŸŽÆ YOUR NEXT STEPS (Priority Order)" -echo "────────────────────────────────────" -echo "" -echo "1. Post Discord announcement (message above)" -echo "2. Review PR #17 (Issue #7 - THE BLOCKER)" -echo "3. Check for new developer comments" -echo "4. Upload ready implementations (Issues #10, #12, #14)" -echo "5. Process any merged PR bounties" -echo "" - -echo "šŸ’” STRATEGIC RECOMMENDATIONS" -echo "─────────────────────────────" -echo "" -echo "āœ… aliraza556 - Elite tier, consider for senior role/CTO discussion" -echo "āœ… brymut - Perfect skills for Plugin System (#31), high potential" -echo "āš ļø anees4500 - New, monitor first contribution quality" -echo "āœ… AbuBakar877 - Keep on web UI work, avoid core systems" -echo "āœ… shalinibhavi525-sudo - Perfect for docs, complement with testing" -echo "" - -echo "šŸ”„ CRITICAL PATH REMINDER" -echo "──────────────────────────" -echo "" -echo "Issue #7 (Package Manager Wrapper) is THE BLOCKER for MVP." -echo "Everything else can proceed in parallel, but #7 must complete." -echo "Check PR #17 weekly, offer assistance to @chandrapratnamar." -echo "" - -echo "════════════════════════════════════════════════════════" -echo "āœ… Ready for next session!" -echo "════════════════════════════════════════════════════════" -echo "" - -echo "Run this script weekly to quarterback your growing team." -echo "The Cortex Linux revolution is accelerating! 🧠⚔" -echo "" +#!/bin/bash +# CORTEX LINUX - MASTER QUARTERBACK SCRIPT +# Manages team onboarding, issue assignment, PR reviews, and project coordination +# Created: November 17, 2025 +# Usage: bash cortex-master-quarterback.sh + +set -e + +echo "🧠 CORTEX LINUX - MASTER QUARTERBACK SCRIPT" +echo "===========================================" +echo "" +echo "This script will:" +echo " 1. Welcome new developers individually" +echo " 2. Assign issues based on expertise" +echo " 3. Review and advance ready PRs" +echo " 4. Coordinate team activities" +echo "" + +# Configuration +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") + +if [ -z "$GITHUB_TOKEN" ]; then + echo "āŒ ERROR: GITHUB_TOKEN not found in ~/.zshrc" + echo "Please add: export GITHUB_TOKEN='your_token_here'" + exit 1 +fi + +# Check if gh CLI is installed +if ! command -v gh &> /dev/null; then + echo "āŒ ERROR: GitHub CLI (gh) not installed" + echo "Install with: brew install gh" + exit 1 +fi + +# Authenticate gh CLI +export GH_TOKEN="$GITHUB_TOKEN" + +echo "āœ… Configuration loaded" +echo "šŸ“Š Repository: $REPO" +echo "" + +# ============================================================================ +# SECTION 1: WELCOME NEW DEVELOPERS +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ‘‹ SECTION 1: WELCOMING NEW DEVELOPERS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Function to welcome a developer +welcome_developer() { + local username=$1 + local name=$2 + local location=$3 + local skills=$4 + local strength=$5 + local recommended_issues=$6 + + echo "šŸ“ Welcoming @$username ($name)..." + + # Create welcome comment + welcome_msg="šŸ‘‹ **Welcome to Cortex Linux, @$username!** + +We're thrilled to have you join our mission to build the AI-native operating system! + +## šŸŽÆ Your Profile Highlights +**Location:** $location +**Primary Skills:** $skills +**Key Strength:** $strength + +## šŸ’” Recommended Issues for You +$recommended_issues + +## šŸš€ Getting Started + +1. **Join our Discord**: https://discord.gg/uCqHvxjU83 (#dev-questions channel) +2. **Review Contributing Guide**: Check repo README and CONTRIBUTING.md +3. **Comment on issues** you're interested in - we'll provide starter code to accelerate development + +## šŸ’° Compensation Structure + +- **Cash bounties** on merge: \$25-200 depending on complexity +- **2x bonus** when we close our \$2-3M seed round (February 2025) +- **Founding team opportunities** for top contributors (equity post-funding) + +## šŸ¤ Our Development Model + +We use a **hybrid approach** that's proven successful: +- Mike + Claude generate complete implementations +- Contributors test, integrate, and validate +- 63% cost savings, 80% time savings +- Everyone wins with professional baseline code + +## šŸ“‹ Next Steps + +1. Browse issues and comment on ones that interest you +2. We'll provide starter code to save you time +3. Test, integrate, and submit PR +4. Get paid on merge! šŸŽ‰ + +**Questions?** Tag @mikejmorgan-ai in any issue or drop into Discord. + +Let's build something revolutionary together! 🧠⚔ + +--- +*Automated welcome from Cortex Team Management System*" + + echo "$welcome_msg" + echo "" + echo "Would you like to post this welcome to @$username's recent activity? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + # Find their most recent issue comment or PR + recent_activity=$(gh api "/repos/$REPO/issues?state=all&creator=$username&per_page=1" 2>/dev/null | jq -r '.[0].number' 2>/dev/null) + + if [ ! -z "$recent_activity" ] && [ "$recent_activity" != "null" ]; then + echo " Posting welcome to Issue/PR #$recent_activity..." + echo "$welcome_msg" | gh issue comment $recent_activity --body-file - --repo $REPO 2>/dev/null || echo " āš ļø Could not post (may need manual posting)" + echo " āœ… Welcome posted!" + else + echo " ā„¹ļø No recent activity found - save welcome message for their first interaction" + fi + else + echo " ā­ļø Skipped posting (you can post manually later)" + fi + + echo "" +} + +# Welcome each new developer +echo "Welcoming 5 new developers..." +echo "" + +welcome_developer \ + "AbuBakar877" \ + "Abu Bakar" \ + "Turkey šŸ‡¹šŸ‡·" \ + "Node.js, React, Angular, Full-stack web development" \ + "Modern JavaScript frameworks and web UI" \ + "- **Issue #27** (Progress Notifications UI) - \$100-150 - Perfect for your frontend skills +- **Issue #26** (User Preferences UI) - \$100-150 - Web interface components +- **Issue #33** (Config Export/Import) - \$75-100 - Data handling + UI" + +welcome_developer \ + "aliraza556" \ + "Ali Raza" \ + "Global Developer šŸŒ" \ + "Full-stack (1000+ contributions), Multi-language expert" \ + "Elite-tier developer with proven track record" \ + "- **Issue #14** (Rollback System) - \$150-200 - āœ… **ALREADY ASSIGNED** - You've got this! +- **Issue #12** (Dependency Resolution) - \$150-200 - Complex logic, perfect match +- **Issue #30** (Self-Update System) - \$150-200 - Advanced architecture +- **Issue #31** (Plugin System) - \$200-300 - Architectural design challenge" + +welcome_developer \ + "anees4500" \ + "Anees" \ + "Location TBD" \ + "Java, C, Python, JavaScript, CDC/Batch processing" \ + "Multi-language capability with data processing experience" \ + "- **Issue #32** (Batch Operations) - \$100-150 - Your CDC experience is perfect here +- **Issue #28** (Requirements Check) - \$75-100 - Systems validation +- **Issue #10** (Installation Verification) - \$100-150 - Backend validation work" + +welcome_developer \ + "brymut" \ + "Bryan Mutai" \ + "Nairobi, Kenya šŸ‡°šŸ‡Ŗ" \ + "TypeScript, Python, PHP, JavaScript - Full-stack with backend focus" \ + "Architectural thinking with perfect skill stack (TypeScript + Python)" \ + "- **Issue #31** (Plugin System) - \$200-300 - **HIGHLY RECOMMENDED** - Architectural perfect match +- **Issue #26** (User Preferences) - \$100-150 - API design + backend +- **Issue #20** (Context Memory) - \$150-200 - TypeScript+Python combo ideal +- **Issue #25** (Network/Proxy Config) - \$150-200 - Backend + systems" + +welcome_developer \ + "shalinibhavi525-sudo" \ + "Shalini Bhavi" \ + "Ireland šŸ‡®šŸ‡Ŗ" \ + "Python, JavaScript, HTML - Documentation focus" \ + "Documentation specialist with web UI skills" \ + "- **Issue #15** (Documentation) - \$100-150 - āœ… **ALREADY ASSIGNED** - Perfect match! +- **Issue #27** (Progress Notifications) - \$100-150 - User-facing UI work +- Testing bounties - \$50-75 - Validate implementations from other devs" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 1 Complete: Developer welcomes prepared" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# SECTION 2: ISSUE ASSIGNMENTS +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸŽÆ SECTION 2: STRATEGIC ISSUE ASSIGNMENTS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "Analyzing current issue status..." + +# Function to assign issue +assign_issue() { + local issue_num=$1 + local developer=$2 + local reason=$3 + + echo "" + echo "šŸ“Œ Assigning Issue #$issue_num to @$developer" + echo " Reason: $reason" + + # Check if issue exists and is unassigned + issue_info=$(gh issue view $issue_num --repo $REPO --json number,title,assignees,state 2>/dev/null || echo "") + + if [ -z "$issue_info" ]; then + echo " āš ļø Issue #$issue_num not found or not accessible" + return + fi + + # Check if already assigned + assignee_count=$(echo "$issue_info" | jq '.assignees | length') + + if [ "$assignee_count" -gt 0 ]; then + current_assignee=$(echo "$issue_info" | jq -r '.assignees[0].login') + echo " ā„¹ļø Already assigned to @$current_assignee - skipping" + return + fi + + echo " Proceed with assignment? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + gh issue edit $issue_num --add-assignee $developer --repo $REPO 2>/dev/null && \ + echo " āœ… Assigned!" || \ + echo " āš ļø Could not assign (may need manual assignment)" + + # Add comment explaining assignment + assignment_comment="šŸŽÆ **Assigned to @$developer** + +**Why you're perfect for this:** $reason + +**Next Steps:** +1. Review the issue description and acceptance criteria +2. Comment if you'd like starter code from our hybrid development model +3. We can provide complete implementation for testing/integration (\$50-75) +4. Or build from scratch for full bounty + +**Questions?** Just ask! We're here to help you succeed. + +--- +*Automated assignment from Cortex Team Management*" + + echo "$assignment_comment" | gh issue comment $issue_num --body-file - --repo $REPO 2>/dev/null || true + else + echo " ā­ļø Skipped" + fi +} + +echo "" +echo "šŸ”“ CRITICAL PATH ASSIGNMENTS (MVP Blockers)" +echo "─────────────────────────────────────────" + +# Issue #7 - Already assigned to chandrapratnamar, but check if help needed +echo "" +echo "Issue #7 (Package Manager Wrapper) - THE critical blocker" +echo " Current: Assigned to @chandrapratnamar (PR #17 in progress)" +echo " Status: Check if they need assistance" +echo " Action: Monitor weekly, offer @aliraza556 or @brymut for code review" +echo "" + +# Issue #10 - Installation Verification +assign_issue 10 "aliraza556" "Elite developer, perfect for systems validation work. Code is ready from Mike." + +# Issue #12 - Dependency Resolution +assign_issue 12 "brymut" "TypeScript+Python skills ideal for complex dependency logic. Mike has complete implementation." + +# Issue #14 - Already assigned to aliraza556 +echo "" +echo "Issue #14 (Rollback System) - āœ… Already assigned to @aliraza556" +echo " Action: Check PR status, offer review assistance" +echo "" + +echo "" +echo "🟔 HIGH PRIORITY ASSIGNMENTS" +echo "─────────────────────────────" + +# Issue #20/24 - Context Memory +assign_issue 20 "brymut" "Architectural experience + TypeScript/Python combo. Mike has implementation ready." + +# Issue #29 - Logging System +assign_issue 29 "anees4500" "Backend infrastructure work, good first complex task to assess quality." + +echo "" +echo "🟢 MEDIUM PRIORITY ASSIGNMENTS" +echo "───────────────────────────────" + +# Issue #25 - Network Config +assign_issue 25 "brymut" "Backend + systems knowledge required for proxy/network configuration." + +# Issue #26 - User Preferences +assign_issue 26 "AbuBakar877" "API + UI components match your full-stack web background." + +# Issue #27 - Progress Notifications +assign_issue 27 "AbuBakar877" "Frontend UI focus, perfect for your React/Angular experience." + +# Issue #28 - Requirements Check +assign_issue 28 "anees4500" "Systems validation, good complement to your batch processing skills." + +echo "" +echo "šŸ”µ ADVANCED FEATURE ASSIGNMENTS" +echo "────────────────────────────────" + +# Issue #30 - Self-Update +assign_issue 30 "aliraza556" "Complex systems integration needs elite-tier developer." + +# Issue #31 - Plugin System +assign_issue 31 "brymut" "**HIGHEST RECOMMENDATION** - Architectural design matches your background perfectly." + +# Issue #32 - Batch Operations +assign_issue 32 "anees4500" "Your CDC/batch processing experience is ideal match." + +# Issue #33 - Config Export/Import +assign_issue 33 "shalinibhavi525-sudo" "Data handling + web UI, complements your documentation work." + +# Issue #15 - Already assigned +echo "" +echo "Issue #15 (Documentation) - āœ… Already assigned to @shalinibhavi525-sudo" +echo " Action: Check progress, offer assistance if needed" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 2 Complete: Strategic assignments made" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# SECTION 3: PULL REQUEST REVIEW +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ” SECTION 3: PULL REQUEST REVIEW & ADVANCEMENT" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "Fetching open pull requests..." + +# Get all open PRs +prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,mergeable,reviewDecision --limit 50 2>/dev/null || echo "[]") + +pr_count=$(echo "$prs" | jq 'length') + +echo "Found $pr_count open pull requests" +echo "" + +if [ "$pr_count" -eq 0 ]; then + echo "āœ… No open PRs to review" +else + echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - \(.reviewDecision // "PENDING")"' + echo "" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "PR REVIEW PRIORITIES" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + # Critical PRs (Issue #7 related) + echo "šŸ”“ CRITICAL - Package Manager (Issue #7)" + echo "PR #17 by @chandrapratnamar" + echo " Action: Review immediately, this is THE MVP blocker" + echo " Review criteria:" + echo " - Does it translate natural language to apt commands?" + echo " - Are tests comprehensive?" + echo " - Does it integrate with LLM layer?" + echo "" + + echo "🟔 HIGH PRIORITY - MVP Features" + echo "Check for PRs related to:" + echo " - Issue #10 (Installation Verification)" + echo " - Issue #12 (Dependency Resolution)" + echo " - Issue #14 (Rollback System)" + echo " - Issue #13 (Error Parser) - PR #23 by @AbdulKadir877" + echo "" + + echo "🟢 STANDARD PRIORITY - All other PRs" + echo "Review remaining PRs in order received" + echo "" + + echo "Would you like to review PRs interactively? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "" + echo "Opening PR review interface..." + echo "" + + # For each PR, offer review options + echo "$prs" | jq -r '.[] | .number' | while read pr_num; do + pr_info=$(gh pr view $pr_num --repo $REPO --json number,title,author,body 2>/dev/null) + pr_title=$(echo "$pr_info" | jq -r '.title') + pr_author=$(echo "$pr_info" | jq -r '.author.login') + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Reviewing PR #$pr_num: $pr_title" + echo "Author: @$pr_author" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Actions:" + echo " [v] View PR in browser" + echo " [a] Approve PR" + echo " [c] Request changes" + echo " [m] Add comment" + echo " [s] Skip to next" + echo " [q] Quit review mode" + echo "" + echo -n "Choose action: " + read -n 1 action + echo "" + + case $action in + v|V) + gh pr view $pr_num --repo $REPO --web + ;; + a|A) + echo "āœ… Approving PR #$pr_num..." + gh pr review $pr_num --repo $REPO --approve --body "āœ… **APPROVED** + +Excellent work @$pr_author! This implementation: +- Meets acceptance criteria +- Includes comprehensive tests +- Integrates well with existing architecture +- Documentation is clear + +**Next Steps:** +1. Merging this PR +2. Bounty will be processed +3. Thank you for your contribution! + +šŸŽ‰ Welcome to the Cortex Linux contributor team!" + echo "Would you like to merge now? (y/n)" + read -n 1 merge_now + echo "" + if [[ $merge_now =~ ^[Yy]$ ]]; then + gh pr merge $pr_num --repo $REPO --squash --delete-branch + echo "āœ… Merged and branch deleted!" + fi + ;; + c|C) + echo "Enter feedback (press Ctrl+D when done):" + feedback=$(cat) + gh pr review $pr_num --repo $REPO --request-changes --body "šŸ”„ **Changes Requested** + +Thanks for your work @$pr_author! Here's what needs attention: + +$feedback + +**Please update and let me know when ready for re-review.** + +We're here to help if you have questions!" + ;; + m|M) + echo "Enter comment (press Ctrl+D when done):" + comment=$(cat) + gh pr comment $pr_num --repo $REPO --body "$comment" + echo "āœ… Comment added" + ;; + q|Q) + echo "Exiting review mode..." + break + ;; + *) + echo "Skipping..." + ;; + esac + echo "" + done + else + echo "ā­ļø Skipped interactive review" + echo " You can review PRs manually at: https://github.com/$REPO/pulls" + fi +fi + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 3 Complete: PR review assistance provided" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# SECTION 4: TEAM COORDINATION +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ¤ SECTION 4: TEAM COORDINATION & NEXT ACTIONS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ“Š CURRENT PROJECT STATUS" +echo "─────────────────────────" +echo "" + +# Count issues by status +total_issues=$(gh issue list --repo $REPO --limit 1000 --json number 2>/dev/null | jq 'length') +open_issues=$(gh issue list --repo $REPO --state open --limit 1000 --json number 2>/dev/null | jq 'length') +closed_issues=$(gh issue list --repo $REPO --state closed --limit 1000 --json number 2>/dev/null | jq 'length') + +echo "Issues:" +echo " Total: $total_issues" +echo " Open: $open_issues" +echo " Closed: $closed_issues" +echo "" + +# Count PRs +open_prs=$(gh pr list --repo $REPO --state open --json number 2>/dev/null | jq 'length') +merged_prs=$(gh pr list --repo $REPO --state merged --limit 100 --json number 2>/dev/null | jq 'length') + +echo "Pull Requests:" +echo " Open: $open_prs" +echo " Merged (recent): $merged_prs" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸŽÆ IMMEDIATE ACTION ITEMS (Priority Order)" +echo "──────────────────────────────────────────" +echo "" + +echo "1. šŸ”“ CRITICAL - Check Issue #7 Progress" +echo " - PR #17 by @chandrapratnamar" +echo " - This is THE MVP blocker" +echo " - Review weekly, offer assistance" +echo " - Command: gh pr view 17 --repo $REPO --web" +echo "" + +echo "2. 🟔 HIGH - Review Ready PRs" +echo " - PR #23 (Error Parser) by @AbdulKadir877" +echo " - Any PRs marked 'ready-for-review'" +echo " - Command: gh pr list --repo $REPO --label ready-for-review" +echo "" + +echo "3. 🟢 MEDIUM - Upload Complete Implementations" +echo " - Issue #10 (Installation Verification) - Code ready" +echo " - Issue #12 (Dependency Resolution) - Code ready" +echo " - Issue #14 (Rollback System) - Code ready with @aliraza556" +echo " - Use: ~/cortex/cortex-master-pr-creator.sh" +echo "" + +echo "4. šŸ”µ ENGAGE NEW DEVELOPERS" +echo " - Post welcome messages (generated above)" +echo " - Monitor their first comments/PRs" +echo " - Offer starter code to accelerate" +echo "" + +echo "5. šŸ’° PROCESS BOUNTIES" +echo " - Track merged PRs" +echo " - Calculate owed bounties" +echo " - Process payments (crypto for international)" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ“‹ RECOMMENDED WEEKLY ROUTINE" +echo "─────────────────────────────" +echo "" +echo "Monday:" +echo " - Run this quarterback script" +echo " - Review critical path (Issue #7)" +echo " - Merge ready PRs" +echo "" +echo "Wednesday:" +echo " - Check new issues/comments" +echo " - Respond to developer questions" +echo " - Upload any ready implementations" +echo "" +echo "Friday:" +echo " - Process bounty payments" +echo " - Update team on Discord" +echo " - Plan next week priorities" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ”— QUICK LINKS" +echo "──────────────" +echo "" +echo "Repository: https://github.com/$REPO" +echo "Open Issues: https://github.com/$REPO/issues" +echo "Open PRs: https://github.com/$REPO/pulls" +echo "Discord: https://discord.gg/uCqHvxjU83" +echo "Project Board: https://github.com/orgs/cortexlinux/projects" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ“± POST TO DISCORD" +echo "──────────────────" +echo "" + +discord_announcement="šŸŽ‰ **Team Update - November 17, 2025** + +**Welcome 5 New Developers!** +- @AbuBakar877 (Turkey) - Full-stack web specialist +- @aliraza556 (Global) - Elite tier, 1000+ contributions +- @anees4500 - Multi-language backend expert +- @brymut (Kenya) - TypeScript + Python architect +- @shalinibhavi525-sudo (Ireland) - Documentation specialist + +**Strategic Assignments Made:** +- Issue #31 (Plugin System) → @brymut (architectural perfect match) +- Issue #10 (Installation Verification) → @aliraza556 +- Issue #32 (Batch Operations) → @anees4500 +- Issue #27 (Progress UI) → @AbuBakar877 +- Issue #15 (Documentation) → @shalinibhavi525-sudo āœ… + +**Critical Path:** +- Issue #7 (Package Manager) - THE blocker - @chandrapratnamar working PR #17 +- Monitoring weekly, need completion for MVP + +**Ready to Review:** +- Multiple PRs waiting for review +- Bounties ready to process on merge + +**The Hybrid Model Works:** +- 63% cost savings +- 80% time savings +- Professional baseline + contributor validation +- Win-win for everyone + +šŸ’° **Bounties:** \$25-200 on merge + 2x bonus at funding +šŸŽÆ **Goal:** MVP complete for February 2025 seed round +šŸ’¼ **Opportunities:** Founding team roles for top contributors + +Browse issues: https://github.com/$REPO/issues +Questions? #dev-questions channel + +Let's build the future of Linux! 🧠⚔" + +echo "$discord_announcement" +echo "" +echo "Copy the above message and post to Discord #announcements" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 4 Complete: Team coordination completed" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# FINAL SUMMARY +# ============================================================================ + +echo "" +echo "════════════════════════════════════════════════════════" +echo "šŸ† CORTEX QUARTERBACK SCRIPT - EXECUTION COMPLETE" +echo "════════════════════════════════════════════════════════" +echo "" + +echo "šŸ“Š EXECUTION SUMMARY" +echo "────────────────────" +echo "" +echo "āœ… 5 developers welcomed with personalized messages" +echo "āœ… 10+ strategic issue assignments made" +echo "āœ… PR review guidance provided" +echo "āœ… Team coordination plan established" +echo "āœ… Discord announcement prepared" +echo "" + +echo "šŸŽÆ YOUR NEXT STEPS (Priority Order)" +echo "────────────────────────────────────" +echo "" +echo "1. Post Discord announcement (message above)" +echo "2. Review PR #17 (Issue #7 - THE BLOCKER)" +echo "3. Check for new developer comments" +echo "4. Upload ready implementations (Issues #10, #12, #14)" +echo "5. Process any merged PR bounties" +echo "" + +echo "šŸ’” STRATEGIC RECOMMENDATIONS" +echo "─────────────────────────────" +echo "" +echo "āœ… aliraza556 - Elite tier, consider for senior role/CTO discussion" +echo "āœ… brymut - Perfect skills for Plugin System (#31), high potential" +echo "āš ļø anees4500 - New, monitor first contribution quality" +echo "āœ… AbuBakar877 - Keep on web UI work, avoid core systems" +echo "āœ… shalinibhavi525-sudo - Perfect for docs, complement with testing" +echo "" + +echo "šŸ”„ CRITICAL PATH REMINDER" +echo "──────────────────────────" +echo "" +echo "Issue #7 (Package Manager Wrapper) is THE BLOCKER for MVP." +echo "Everything else can proceed in parallel, but #7 must complete." +echo "Check PR #17 weekly, offer assistance to @chandrapratnamar." +echo "" + +echo "════════════════════════════════════════════════════════" +echo "āœ… Ready for next session!" +echo "════════════════════════════════════════════════════════" +echo "" + +echo "Run this script weekly to quarterback your growing team." +echo "The Cortex Linux revolution is accelerating! 🧠⚔" +echo "" diff --git a/scripts/automation/cortex-master-update.sh b/scripts/automation/cortex-master-update.sh old mode 100755 new mode 100644 index f5afb060..44252b3e --- a/scripts/automation/cortex-master-update.sh +++ b/scripts/automation/cortex-master-update.sh @@ -1,301 +1,301 @@ -#!/bin/bash -# CORTEX LINUX - MASTER REPOSITORY UPDATE SCRIPT -# Analyzes PRs, merges ready ones, assigns issues, tracks bounties - -set -e - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") -export GH_TOKEN="$GITHUB_TOKEN" - -echo "🧠 CORTEX LINUX - MASTER UPDATE" -echo "================================" -echo "" - -# ============================================================================ -# STEP 1: MERGE READY PRS -# ============================================================================ - -echo "šŸ“Š STEP 1: REVIEWING & MERGING READY PRS" -echo "─────────────────────────────────────────" -echo "" - -# PR #195: Package Manager (dhvll) - REPLACES PR #17 -echo "šŸ”“ PR #195: Package Manager Wrapper (@dhvll)" -echo " Status: MERGEABLE āœ…" -echo " Action: MERGE NOW - This is THE MVP blocker" -echo "" - -gh pr review 195 --repo $REPO --approve --body "āœ… APPROVED - Excellent package manager implementation! This replaces PR #17 and unblocks the entire MVP. Outstanding work @dhvll!" - -gh pr merge 195 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #195 MERGED - MVP BLOCKER CLEARED!" - echo "" - - # Close Issue #7 - gh issue close 7 --repo $REPO --comment "āœ… Completed in PR #195 by @dhvll. Package manager wrapper is live and working!" - - # Close old PR #17 - gh pr close 17 --repo $REPO --comment "Closing in favor of PR #195 which has a cleaner implementation. Thank you @chandrapratamar for the original work - you'll still receive the $100 bounty for your contribution." - - echo "āœ… Issue #7 closed" - echo "āœ… PR #17 closed (superseded)" - echo "" -} || { - echo "āš ļø PR #195 merge failed - check manually" - echo "" -} - -# PR #198: Rollback System (aliraza556) -echo "🟢 PR #198: Installation History & Rollback (@aliraza556)" -echo " Status: MERGEABLE āœ…" -echo " Bounty: $150" -echo "" - -gh pr review 198 --repo $REPO --approve --body "āœ… APPROVED - Comprehensive rollback system! $150 bounty within 48 hours. Outstanding work @aliraza556!" - -gh pr merge 198 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #198 MERGED" - gh issue close 14 --repo $REPO --comment "āœ… Completed in PR #198 by @aliraza556. Rollback system is live!" - echo " šŸ’° Bounty owed: $150 to @aliraza556" - echo "" -} || { - echo "āš ļø PR #198 merge failed" - echo "" -} - -# PR #197: Cleanup (mikejmorgan-ai) -echo "🟢 PR #197: Remove Duplicate Workflow" -echo " Status: MERGEABLE āœ…" -echo "" - -gh pr merge 197 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #197 MERGED" - echo "" -} || { - echo "āš ļø PR #197 merge failed" - echo "" -} - -# PR #21: Config Templates (aliraza556) -echo "🟔 PR #21: Configuration Templates (@aliraza556)" -echo " Status: MERGEABLE āœ…" -echo " Bounty: $150" -echo "" - -gh pr review 21 --repo $REPO --approve --body "āœ… APPROVED - Production-ready config templates! $150 bounty within 48 hours." - -gh pr merge 21 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #21 MERGED" - gh issue close 9 --repo $REPO --comment "āœ… Completed in PR #21. Config templates are live!" - echo " šŸ’° Bounty owed: $150 to @aliraza556" - echo "" -} || { - echo "āš ļø PR #21 merge failed" - echo "" -} - -# PR #38: Requirements Check (AlexanderLuzDH) - HAS CONFLICTS -echo "ā­ļø PR #38: Requirements Checker (@AlexanderLuzDH)" -echo " Status: CONFLICTING āŒ" -echo " Action: Skip - needs contributor to fix conflicts" -echo " Bounty: $100 pending" -echo "" - -# PR #18: CLI Interface (Sahilbhatane) - DRAFT -echo "ā­ļø PR #18: CLI Interface (@Sahilbhatane)" -echo " Status: DRAFT - not ready yet" -echo " Action: Skip" -echo "" - -# ============================================================================ -# STEP 2: ASSIGN UNASSIGNED MVP ISSUES -# ============================================================================ - -echo "" -echo "šŸ“‹ STEP 2: ASSIGNING UNASSIGNED MVP ISSUES" -echo "───────────────────────────────────────────" -echo "" - -# High-value issues that need assignment -MVP_ISSUES=(144 135 131 128 126 125 119 117 112 103 44 25) - -echo "Unassigned MVP issues ready for contributors:" -echo "" - -for issue in "${MVP_ISSUES[@]}"; do - issue_info=$(gh issue view $issue --repo $REPO --json title,assignees,labels 2>/dev/null) - issue_title=$(echo "$issue_info" | jq -r '.title') - assignee_count=$(echo "$issue_info" | jq '.assignees | length') - - if [ "$assignee_count" -eq 0 ]; then - echo " #$issue: $issue_title" - fi -done - -echo "" -echo "These issues are ready for contributors to claim." -echo "Post to Discord: 'MVP issues available - claim in comments!'" -echo "" - -# ============================================================================ -# STEP 3: BOUNTY TRACKING -# ============================================================================ - -echo "" -echo "šŸ’° STEP 3: BOUNTY TRACKING UPDATE" -echo "─────────────────────────────────" -echo "" - -BOUNTY_FILE="$HOME/cortex/bounties_owed.csv" - -if [ ! -f "$BOUNTY_FILE" ]; then - echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTY_FILE" -fi - -# Add new bounties from today's merges -echo "195,dhvll,Package Manager Wrapper,100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" -echo "198,aliraza556,Installation Rollback,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" -echo "21,aliraza556,Config Templates,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" -echo "17,chandrapratamar,Package Manager (original),100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" - -echo "Updated: $BOUNTY_FILE" -echo "" - -echo "BOUNTIES OWED:" -echo "──────────────" -tail -n +2 "$BOUNTY_FILE" | while IFS=',' read -r pr dev feature amount date status; do - if [ "$status" = "PENDING" ]; then - echo " PR #$pr - @$dev: \$$amount ($feature)" - fi -done - -echo "" - -# Calculate totals -total_owed=$(tail -n +2 "$BOUNTY_FILE" | awk -F',' '$6=="PENDING" {sum+=$4} END {print sum}') -echo " Total pending: \$$total_owed" -echo " At 2x bonus (funding): \$$(($total_owed * 2))" -echo "" - -# ============================================================================ -# STEP 4: GENERATE STATUS REPORT -# ============================================================================ - -echo "" -echo "šŸ“Š STEP 4: FINAL STATUS REPORT" -echo "───────────────────────────────" -echo "" - -echo "=== CORTEX REPOSITORY STATUS ===" -echo "" - -# Count current state -open_prs=$(gh pr list --repo $REPO --state open --json number | jq 'length') -open_issues=$(gh issue list --repo $REPO --state open --json number | jq 'length') - -echo "PRs:" -echo " Open: $open_prs" -echo " Merged today: 4 (PRs #195, #198, #197, #21)" -echo "" - -echo "Issues:" -echo " Open: $open_issues" -echo " Closed today: 2 (Issues #7, #14)" -echo "" - -echo "MVP Status:" -echo " āœ… Package Manager: COMPLETE (PR #195)" -echo " āœ… Rollback System: COMPLETE (PR #198)" -echo " āœ… Config Templates: COMPLETE (PR #21)" -echo " āœ… Hardware Detection: COMPLETE" -echo " āœ… Dependencies: COMPLETE" -echo " āœ… Verification: COMPLETE" -echo " āœ… Error Parsing: COMPLETE" -echo " āœ… Context Memory: COMPLETE" -echo " āœ… Logging: COMPLETE" -echo " āœ… Progress UI: COMPLETE" -echo " ā³ Requirements Check: Conflicts (PR #38)" -echo "" -echo " MVP COMPLETE: 95%" -echo "" - -echo "Bounties:" -echo " Owed: \$$total_owed" -echo " Contributors to pay: @dhvll, @aliraza556 (x2), @chandrapratamar" -echo "" - -# ============================================================================ -# STEP 5: DISCORD ANNOUNCEMENT -# ============================================================================ - -echo "" -echo "šŸ“± STEP 5: DISCORD ANNOUNCEMENT (COPY & POST)" -echo "─────────────────────────────────────────────" -echo "" - -cat << 'DISCORD' -šŸŽ‰ **MAJOR MVP MILESTONE - November 17, 2025** - -**BREAKTHROUGH: Package Manager MERGED! šŸš€** - -PR #195 by @dhvll just merged - THE critical MVP blocker is cleared! - -**Today's Merges:** -āœ… PR #195 - Package Manager Wrapper (@dhvll) -āœ… PR #198 - Installation Rollback (@aliraza556) -āœ… PR #21 - Config File Templates (@aliraza556) -āœ… PR #197 - Workflow Cleanup - -**Issues Closed:** -āœ… #7 - Package Manager (9 days → DONE!) -āœ… #14 - Rollback System - -**MVP Status: 95% COMPLETE** šŸŽÆ - -**What This Means:** -- Core "cortex install" functionality working -- Natural language → apt commands = LIVE -- Rollback safety net = LIVE -- Production-ready config templates = LIVE - -**Bounties Being Processed:** -- @dhvll: $100 -- @aliraza556: $300 ($150 x 2 PRs!) -- @chandrapratamar: $100 -Total: $500 (+ 2x at funding = $1000) - -**Available Issues:** -10+ MVP features ready to claim - check GitHub issues! - -**Next: Demo preparation for February 2025 funding round** - -We're making history! 🧠⚔ - -https://github.com/cortexlinux/cortex -DISCORD - -echo "" -echo "─────────────────────────────────────────────" -echo "" - -# ============================================================================ -# STEP 6: NEXT STEPS -# ============================================================================ - -echo "šŸŽÆ NEXT STEPS" -echo "─────────────" -echo "" -echo "1. Post Discord announcement above to #announcements" -echo "2. Coordinate payments with:" -echo " - @dhvll ($100)" -echo " - @aliraza556 ($300)" -echo " - @chandrapratamar ($100)" -echo "3. Wait for PR #38 conflict resolution" -echo "4. Create demo script: 'cortex install oracle-23-ai'" -echo "5. Prepare investor presentation materials" -echo "" - -echo "āœ… MASTER UPDATE COMPLETE" -echo "" -echo "Repository is MVP-ready for February 2025 funding!" +#!/bin/bash +# CORTEX LINUX - MASTER REPOSITORY UPDATE SCRIPT +# Analyzes PRs, merges ready ones, assigns issues, tracks bounties + +set -e + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") +export GH_TOKEN="$GITHUB_TOKEN" + +echo "🧠 CORTEX LINUX - MASTER UPDATE" +echo "================================" +echo "" + +# ============================================================================ +# STEP 1: MERGE READY PRS +# ============================================================================ + +echo "šŸ“Š STEP 1: REVIEWING & MERGING READY PRS" +echo "─────────────────────────────────────────" +echo "" + +# PR #195: Package Manager (dhvll) - REPLACES PR #17 +echo "šŸ”“ PR #195: Package Manager Wrapper (@dhvll)" +echo " Status: MERGEABLE āœ…" +echo " Action: MERGE NOW - This is THE MVP blocker" +echo "" + +gh pr review 195 --repo $REPO --approve --body "āœ… APPROVED - Excellent package manager implementation! This replaces PR #17 and unblocks the entire MVP. Outstanding work @dhvll!" + +gh pr merge 195 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #195 MERGED - MVP BLOCKER CLEARED!" + echo "" + + # Close Issue #7 + gh issue close 7 --repo $REPO --comment "āœ… Completed in PR #195 by @dhvll. Package manager wrapper is live and working!" + + # Close old PR #17 + gh pr close 17 --repo $REPO --comment "Closing in favor of PR #195 which has a cleaner implementation. Thank you @chandrapratamar for the original work - you'll still receive the $100 bounty for your contribution." + + echo "āœ… Issue #7 closed" + echo "āœ… PR #17 closed (superseded)" + echo "" +} || { + echo "āš ļø PR #195 merge failed - check manually" + echo "" +} + +# PR #198: Rollback System (aliraza556) +echo "🟢 PR #198: Installation History & Rollback (@aliraza556)" +echo " Status: MERGEABLE āœ…" +echo " Bounty: $150" +echo "" + +gh pr review 198 --repo $REPO --approve --body "āœ… APPROVED - Comprehensive rollback system! $150 bounty within 48 hours. Outstanding work @aliraza556!" + +gh pr merge 198 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #198 MERGED" + gh issue close 14 --repo $REPO --comment "āœ… Completed in PR #198 by @aliraza556. Rollback system is live!" + echo " šŸ’° Bounty owed: $150 to @aliraza556" + echo "" +} || { + echo "āš ļø PR #198 merge failed" + echo "" +} + +# PR #197: Cleanup (mikejmorgan-ai) +echo "🟢 PR #197: Remove Duplicate Workflow" +echo " Status: MERGEABLE āœ…" +echo "" + +gh pr merge 197 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #197 MERGED" + echo "" +} || { + echo "āš ļø PR #197 merge failed" + echo "" +} + +# PR #21: Config Templates (aliraza556) +echo "🟔 PR #21: Configuration Templates (@aliraza556)" +echo " Status: MERGEABLE āœ…" +echo " Bounty: $150" +echo "" + +gh pr review 21 --repo $REPO --approve --body "āœ… APPROVED - Production-ready config templates! $150 bounty within 48 hours." + +gh pr merge 21 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #21 MERGED" + gh issue close 9 --repo $REPO --comment "āœ… Completed in PR #21. Config templates are live!" + echo " šŸ’° Bounty owed: $150 to @aliraza556" + echo "" +} || { + echo "āš ļø PR #21 merge failed" + echo "" +} + +# PR #38: Requirements Check (AlexanderLuzDH) - HAS CONFLICTS +echo "ā­ļø PR #38: Requirements Checker (@AlexanderLuzDH)" +echo " Status: CONFLICTING āŒ" +echo " Action: Skip - needs contributor to fix conflicts" +echo " Bounty: $100 pending" +echo "" + +# PR #18: CLI Interface (Sahilbhatane) - DRAFT +echo "ā­ļø PR #18: CLI Interface (@Sahilbhatane)" +echo " Status: DRAFT - not ready yet" +echo " Action: Skip" +echo "" + +# ============================================================================ +# STEP 2: ASSIGN UNASSIGNED MVP ISSUES +# ============================================================================ + +echo "" +echo "šŸ“‹ STEP 2: ASSIGNING UNASSIGNED MVP ISSUES" +echo "───────────────────────────────────────────" +echo "" + +# High-value issues that need assignment +MVP_ISSUES=(144 135 131 128 126 125 119 117 112 103 44 25) + +echo "Unassigned MVP issues ready for contributors:" +echo "" + +for issue in "${MVP_ISSUES[@]}"; do + issue_info=$(gh issue view $issue --repo $REPO --json title,assignees,labels 2>/dev/null) + issue_title=$(echo "$issue_info" | jq -r '.title') + assignee_count=$(echo "$issue_info" | jq '.assignees | length') + + if [ "$assignee_count" -eq 0 ]; then + echo " #$issue: $issue_title" + fi +done + +echo "" +echo "These issues are ready for contributors to claim." +echo "Post to Discord: 'MVP issues available - claim in comments!'" +echo "" + +# ============================================================================ +# STEP 3: BOUNTY TRACKING +# ============================================================================ + +echo "" +echo "šŸ’° STEP 3: BOUNTY TRACKING UPDATE" +echo "─────────────────────────────────" +echo "" + +BOUNTY_FILE="$HOME/cortex/bounties_owed.csv" + +if [ ! -f "$BOUNTY_FILE" ]; then + echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTY_FILE" +fi + +# Add new bounties from today's merges +echo "195,dhvll,Package Manager Wrapper,100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" +echo "198,aliraza556,Installation Rollback,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" +echo "21,aliraza556,Config Templates,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" +echo "17,chandrapratamar,Package Manager (original),100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" + +echo "Updated: $BOUNTY_FILE" +echo "" + +echo "BOUNTIES OWED:" +echo "──────────────" +tail -n +2 "$BOUNTY_FILE" | while IFS=',' read -r pr dev feature amount date status; do + if [ "$status" = "PENDING" ]; then + echo " PR #$pr - @$dev: \$$amount ($feature)" + fi +done + +echo "" + +# Calculate totals +total_owed=$(tail -n +2 "$BOUNTY_FILE" | awk -F',' '$6=="PENDING" {sum+=$4} END {print sum}') +echo " Total pending: \$$total_owed" +echo " At 2x bonus (funding): \$$(($total_owed * 2))" +echo "" + +# ============================================================================ +# STEP 4: GENERATE STATUS REPORT +# ============================================================================ + +echo "" +echo "šŸ“Š STEP 4: FINAL STATUS REPORT" +echo "───────────────────────────────" +echo "" + +echo "=== CORTEX REPOSITORY STATUS ===" +echo "" + +# Count current state +open_prs=$(gh pr list --repo $REPO --state open --json number | jq 'length') +open_issues=$(gh issue list --repo $REPO --state open --json number | jq 'length') + +echo "PRs:" +echo " Open: $open_prs" +echo " Merged today: 4 (PRs #195, #198, #197, #21)" +echo "" + +echo "Issues:" +echo " Open: $open_issues" +echo " Closed today: 2 (Issues #7, #14)" +echo "" + +echo "MVP Status:" +echo " āœ… Package Manager: COMPLETE (PR #195)" +echo " āœ… Rollback System: COMPLETE (PR #198)" +echo " āœ… Config Templates: COMPLETE (PR #21)" +echo " āœ… Hardware Detection: COMPLETE" +echo " āœ… Dependencies: COMPLETE" +echo " āœ… Verification: COMPLETE" +echo " āœ… Error Parsing: COMPLETE" +echo " āœ… Context Memory: COMPLETE" +echo " āœ… Logging: COMPLETE" +echo " āœ… Progress UI: COMPLETE" +echo " ā³ Requirements Check: Conflicts (PR #38)" +echo "" +echo " MVP COMPLETE: 95%" +echo "" + +echo "Bounties:" +echo " Owed: \$$total_owed" +echo " Contributors to pay: @dhvll, @aliraza556 (x2), @chandrapratamar" +echo "" + +# ============================================================================ +# STEP 5: DISCORD ANNOUNCEMENT +# ============================================================================ + +echo "" +echo "šŸ“± STEP 5: DISCORD ANNOUNCEMENT (COPY & POST)" +echo "─────────────────────────────────────────────" +echo "" + +cat << 'DISCORD' +šŸŽ‰ **MAJOR MVP MILESTONE - November 17, 2025** + +**BREAKTHROUGH: Package Manager MERGED! šŸš€** + +PR #195 by @dhvll just merged - THE critical MVP blocker is cleared! + +**Today's Merges:** +āœ… PR #195 - Package Manager Wrapper (@dhvll) +āœ… PR #198 - Installation Rollback (@aliraza556) +āœ… PR #21 - Config File Templates (@aliraza556) +āœ… PR #197 - Workflow Cleanup + +**Issues Closed:** +āœ… #7 - Package Manager (9 days → DONE!) +āœ… #14 - Rollback System + +**MVP Status: 95% COMPLETE** šŸŽÆ + +**What This Means:** +- Core "cortex install" functionality working +- Natural language → apt commands = LIVE +- Rollback safety net = LIVE +- Production-ready config templates = LIVE + +**Bounties Being Processed:** +- @dhvll: $100 +- @aliraza556: $300 ($150 x 2 PRs!) +- @chandrapratamar: $100 +Total: $500 (+ 2x at funding = $1000) + +**Available Issues:** +10+ MVP features ready to claim - check GitHub issues! + +**Next: Demo preparation for February 2025 funding round** + +We're making history! 🧠⚔ + +https://github.com/cortexlinux/cortex +DISCORD + +echo "" +echo "─────────────────────────────────────────────" +echo "" + +# ============================================================================ +# STEP 6: NEXT STEPS +# ============================================================================ + +echo "šŸŽÆ NEXT STEPS" +echo "─────────────" +echo "" +echo "1. Post Discord announcement above to #announcements" +echo "2. Coordinate payments with:" +echo " - @dhvll ($100)" +echo " - @aliraza556 ($300)" +echo " - @chandrapratamar ($100)" +echo "3. Wait for PR #38 conflict resolution" +echo "4. Create demo script: 'cortex install oracle-23-ai'" +echo "5. Prepare investor presentation materials" +echo "" + +echo "āœ… MASTER UPDATE COMPLETE" +echo "" +echo "Repository is MVP-ready for February 2025 funding!" diff --git a/scripts/automation/cortex-master.sh b/scripts/automation/cortex-master.sh old mode 100755 new mode 100644 index 94e485b0..3b2d6ff7 --- a/scripts/automation/cortex-master.sh +++ b/scripts/automation/cortex-master.sh @@ -1,194 +1,194 @@ -#!/bin/bash -# Cortex Linux - Master MVP Automation System -# One script to rule them all - -set -e - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -REPO_DIR="$HOME/cortex" -WORK_DIR="$HOME/Downloads/cortex-work" -mkdir -p "$WORK_DIR" - -print_banner() { - echo -e "${BLUE}" - echo "╔════════════════════════════════════════════════╗" - echo "ā•‘ CORTEX LINUX - MVP MASTER AUTOMATION ā•‘" - echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" - echo -e "${NC}" -} - -show_menu() { - echo "" - echo -e "${GREEN}═══ MAIN MENU ═══${NC}" - echo "" - echo "1. Show MVP dashboard" - echo "2. List MVP-critical issues" - echo "3. Create PR for issue #10" - echo "4. Review pending PRs" - echo "5. Merge PR" - echo "6. List contributors" - echo "7. Assign issue to contributor" - echo "8. Process bounty payment" - echo "9. Generate weekly report" - echo "10. Full repository audit" - echo "" - echo "0. Exit" - echo "" - echo -n "Select: " -} - -show_dashboard() { - cd "$REPO_DIR" - echo -e "${BLUE}═══ CORTEX MVP DASHBOARD ═══${NC}" - echo "" - echo "šŸ“Š Issues:" - echo " Total: $(gh issue list --limit 1000 --json number | jq '. | length')" - echo " MVP Critical: $(gh issue list --label 'mvp-critical' --json number | jq '. | length')" - echo "" - echo "šŸ”€ Pull Requests:" - echo " Open: $(gh pr list --json number | jq '. | length')" - echo "" - echo "šŸ‘„ Recent activity:" - gh pr list --state all --limit 5 --json number,title,author | \ - jq -r '.[] | " PR #\(.number): \(.title) (@\(.author.login))"' -} - -list_mvp() { - cd "$REPO_DIR" - echo -e "${GREEN}šŸ“‹ MVP-Critical Issues:${NC}" - gh issue list --label "mvp-critical" --limit 20 --json number,title,assignees | \ - jq -r '.[] | " #\(.number): \(.title)"' -} - -create_pr_issue10() { - cd "$REPO_DIR" - git checkout feature/issue-10 2>/dev/null || { - echo "Branch feature/issue-10 not found" - return 1 - } - - gh pr create \ - --title "Add Installation Verification System - Fixes #10" \ - --body "Complete implementation: 918 lines (code+tests+docs). Ready for review." \ - --label "enhancement,ready-for-review,priority: critical" - - git checkout main - echo "āœ… PR created!" -} - -review_prs() { - cd "$REPO_DIR" - echo -e "${GREEN}šŸ“‹ Open Pull Requests:${NC}" - gh pr list --json number,title,author,createdAt | \ - jq -r '.[] | " PR #\(.number): \(.title)\n Author: @\(.author.login)\n Created: \(.createdAt)\n"' -} - -merge_pr() { - echo -n "PR number to merge: " - read pr_num - cd "$REPO_DIR" - gh pr merge $pr_num --squash --delete-branch - echo "āœ… Merged!" -} - -list_contributors() { - cd "$REPO_DIR" - echo -e "${GREEN}šŸ‘„ Active Contributors:${NC}" - gh pr list --state all --limit 50 --json author | \ - jq -r '.[].author.login' | sort | uniq -c | sort -rn | head -10 -} - -assign_issue() { - echo -n "Issue #: " - read issue - echo -n "Assign to (username): " - read user - cd "$REPO_DIR" - gh issue edit $issue --add-assignee "$user" - gh issue comment $issue --body "šŸ‘‹ @$user - This is assigned to you! Questions? Ask in Discord." - echo "āœ… Assigned!" -} - -process_bounty() { - echo -n "PR #: " - read pr - echo -n "Username: " - read user - echo -n "Amount $: " - read amount - - cd "$REPO_DIR" - gh pr comment $pr --body "šŸ’° **Bounty Approved: \$$amount** - -@$user - DM me your payment method. Payment Friday. Plus 2x bonus at funding! - -Thanks! šŸŽ‰" - - echo "āœ… Bounty processed!" -} - -weekly_report() { - cd "$REPO_DIR" - echo "# Cortex Linux - Weekly Report" - echo "Week of $(date +%Y-%m-%d)" - echo "" - echo "## PRs This Week" - gh pr list --state merged --limit 10 --json number,title | \ - jq -r '.[] | "- PR #\(.number): \(.title)"' - echo "" - echo "## Metrics" - echo "- Open Issues: $(gh issue list --json number | jq '. | length')" - echo "- Open PRs: $(gh pr list --json number | jq '. | length')" -} - -audit_repo() { - cd "$REPO_DIR" - echo "Repository: cortexlinux/cortex" - echo "Branch: $(git branch --show-current)" - echo "Last commit: $(git log -1 --oneline)" - echo "" - echo "Issues: $(gh issue list --json number | jq '. | length') open" - echo "PRs: $(gh pr list --json number | jq '. | length') open" - echo "" - echo "Recent activity:" - gh run list --limit 3 -} - -main() { - print_banner - - cd "$REPO_DIR" 2>/dev/null || { - echo "āŒ Repo not found at $REPO_DIR" - exit 1 - } - - while true; do - show_menu - read choice - - case $choice in - 1) show_dashboard ;; - 2) list_mvp ;; - 3) create_pr_issue10 ;; - 4) review_prs ;; - 5) merge_pr ;; - 6) list_contributors ;; - 7) assign_issue ;; - 8) process_bounty ;; - 9) weekly_report ;; - 10) audit_repo ;; - 0) echo "Goodbye!"; exit 0 ;; - *) echo "Invalid option" ;; - esac - - echo "" - read -p "Press Enter..." - done -} - -main +#!/bin/bash +# Cortex Linux - Master MVP Automation System +# One script to rule them all + +set -e + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +REPO_DIR="$HOME/cortex" +WORK_DIR="$HOME/Downloads/cortex-work" +mkdir -p "$WORK_DIR" + +print_banner() { + echo -e "${BLUE}" + echo "╔════════════════════════════════════════════════╗" + echo "ā•‘ CORTEX LINUX - MVP MASTER AUTOMATION ā•‘" + echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" + echo -e "${NC}" +} + +show_menu() { + echo "" + echo -e "${GREEN}═══ MAIN MENU ═══${NC}" + echo "" + echo "1. Show MVP dashboard" + echo "2. List MVP-critical issues" + echo "3. Create PR for issue #10" + echo "4. Review pending PRs" + echo "5. Merge PR" + echo "6. List contributors" + echo "7. Assign issue to contributor" + echo "8. Process bounty payment" + echo "9. Generate weekly report" + echo "10. Full repository audit" + echo "" + echo "0. Exit" + echo "" + echo -n "Select: " +} + +show_dashboard() { + cd "$REPO_DIR" + echo -e "${BLUE}═══ CORTEX MVP DASHBOARD ═══${NC}" + echo "" + echo "šŸ“Š Issues:" + echo " Total: $(gh issue list --limit 1000 --json number | jq '. | length')" + echo " MVP Critical: $(gh issue list --label 'mvp-critical' --json number | jq '. | length')" + echo "" + echo "šŸ”€ Pull Requests:" + echo " Open: $(gh pr list --json number | jq '. | length')" + echo "" + echo "šŸ‘„ Recent activity:" + gh pr list --state all --limit 5 --json number,title,author | \ + jq -r '.[] | " PR #\(.number): \(.title) (@\(.author.login))"' +} + +list_mvp() { + cd "$REPO_DIR" + echo -e "${GREEN}šŸ“‹ MVP-Critical Issues:${NC}" + gh issue list --label "mvp-critical" --limit 20 --json number,title,assignees | \ + jq -r '.[] | " #\(.number): \(.title)"' +} + +create_pr_issue10() { + cd "$REPO_DIR" + git checkout feature/issue-10 2>/dev/null || { + echo "Branch feature/issue-10 not found" + return 1 + } + + gh pr create \ + --title "Add Installation Verification System - Fixes #10" \ + --body "Complete implementation: 918 lines (code+tests+docs). Ready for review." \ + --label "enhancement,ready-for-review,priority: critical" + + git checkout main + echo "āœ… PR created!" +} + +review_prs() { + cd "$REPO_DIR" + echo -e "${GREEN}šŸ“‹ Open Pull Requests:${NC}" + gh pr list --json number,title,author,createdAt | \ + jq -r '.[] | " PR #\(.number): \(.title)\n Author: @\(.author.login)\n Created: \(.createdAt)\n"' +} + +merge_pr() { + echo -n "PR number to merge: " + read pr_num + cd "$REPO_DIR" + gh pr merge $pr_num --squash --delete-branch + echo "āœ… Merged!" +} + +list_contributors() { + cd "$REPO_DIR" + echo -e "${GREEN}šŸ‘„ Active Contributors:${NC}" + gh pr list --state all --limit 50 --json author | \ + jq -r '.[].author.login' | sort | uniq -c | sort -rn | head -10 +} + +assign_issue() { + echo -n "Issue #: " + read issue + echo -n "Assign to (username): " + read user + cd "$REPO_DIR" + gh issue edit $issue --add-assignee "$user" + gh issue comment $issue --body "šŸ‘‹ @$user - This is assigned to you! Questions? Ask in Discord." + echo "āœ… Assigned!" +} + +process_bounty() { + echo -n "PR #: " + read pr + echo -n "Username: " + read user + echo -n "Amount $: " + read amount + + cd "$REPO_DIR" + gh pr comment $pr --body "šŸ’° **Bounty Approved: \$$amount** + +@$user - DM me your payment method. Payment Friday. Plus 2x bonus at funding! + +Thanks! šŸŽ‰" + + echo "āœ… Bounty processed!" +} + +weekly_report() { + cd "$REPO_DIR" + echo "# Cortex Linux - Weekly Report" + echo "Week of $(date +%Y-%m-%d)" + echo "" + echo "## PRs This Week" + gh pr list --state merged --limit 10 --json number,title | \ + jq -r '.[] | "- PR #\(.number): \(.title)"' + echo "" + echo "## Metrics" + echo "- Open Issues: $(gh issue list --json number | jq '. | length')" + echo "- Open PRs: $(gh pr list --json number | jq '. | length')" +} + +audit_repo() { + cd "$REPO_DIR" + echo "Repository: cortexlinux/cortex" + echo "Branch: $(git branch --show-current)" + echo "Last commit: $(git log -1 --oneline)" + echo "" + echo "Issues: $(gh issue list --json number | jq '. | length') open" + echo "PRs: $(gh pr list --json number | jq '. | length') open" + echo "" + echo "Recent activity:" + gh run list --limit 3 +} + +main() { + print_banner + + cd "$REPO_DIR" 2>/dev/null || { + echo "āŒ Repo not found at $REPO_DIR" + exit 1 + } + + while true; do + show_menu + read choice + + case $choice in + 1) show_dashboard ;; + 2) list_mvp ;; + 3) create_pr_issue10 ;; + 4) review_prs ;; + 5) merge_pr ;; + 6) list_contributors ;; + 7) assign_issue ;; + 8) process_bounty ;; + 9) weekly_report ;; + 10) audit_repo ;; + 0) echo "Goodbye!"; exit 0 ;; + *) echo "Invalid option" ;; + esac + + echo "" + read -p "Press Enter..." + done +} + +main diff --git a/scripts/automation/cortex-pr-dashboard.sh b/scripts/automation/cortex-pr-dashboard.sh old mode 100755 new mode 100644 index df0b42df..b7a33ed9 --- a/scripts/automation/cortex-pr-dashboard.sh +++ b/scripts/automation/cortex-pr-dashboard.sh @@ -1,362 +1,362 @@ -#!/bin/bash -# CORTEX - MASTER PR DASHBOARD & MANAGEMENT -# Complete PR overview, batch operations, and bounty tracking - -set -e - -echo "šŸŽ›ļø CORTEX - MASTER PR DASHBOARD" -echo "================================" -echo "" - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") -export GH_TOKEN="$GITHUB_TOKEN" - -# Colors for terminal output -RED='\033[0;31m' -YELLOW='\033[1;33m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ“Š PR STATUS OVERVIEW" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Get all open PRs -prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,isDraft,reviewDecision --limit 50 2>/dev/null) - -total_prs=$(echo "$prs" | jq 'length') -contributor_prs=$(echo "$prs" | jq '[.[] | select(.author.login != "mikejmorgan-ai")] | length') -mike_prs=$(echo "$prs" | jq '[.[] | select(.author.login == "mikejmorgan-ai")] | length') - -echo "Total Open PRs: $total_prs" -echo " ā”œā”€ From Contributors: $contributor_prs (šŸ”„ Need review)" -echo " └─ From Mike: $mike_prs (Can merge anytime)" -echo "" - -# Calculate bounties at stake -echo "šŸ’° ESTIMATED BOUNTIES AT STAKE" -echo "────────────────────────────────" -echo "" - -declare -A BOUNTY_MAP -BOUNTY_MAP[17]=100 # Package Manager -BOUNTY_MAP[37]=125 # Progress Notifications -BOUNTY_MAP[38]=100 # Requirements Check -BOUNTY_MAP[21]=150 # Config Templates -BOUNTY_MAP[18]=100 # CLI Interface - -total_contributor_bounties=0 - -for pr in 17 37 38 21 18; do - bounty=${BOUNTY_MAP[$pr]} - total_contributor_bounties=$((total_contributor_bounties + bounty)) -done - -echo "Contributor PRs: \$$total_contributor_bounties" -echo "At 2x bonus (funding): \$$((total_contributor_bounties * 2))" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ”“ CRITICAL PRIORITY" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -pr17_info=$(gh pr view 17 --repo $REPO --json number,title,author,createdAt,state 2>/dev/null) -pr17_title=$(echo "$pr17_info" | jq -r '.title') -pr17_author=$(echo "$pr17_info" | jq -r '.author.login') -pr17_created=$(echo "$pr17_info" | jq -r '.createdAt' | cut -d'T' -f1) -pr17_days_old=$(( ( $(date +%s) - $(date -j -f "%Y-%m-%d" "$pr17_created" +%s 2>/dev/null || date +%s) ) / 86400 )) - -echo "PR #17: $pr17_title" -echo "Author: @$pr17_author" -echo "Age: $pr17_days_old days old" -echo "Bounty: \$100" -echo "Impact: āš ļø MVP BLOCKER - Everything waits on this" -echo "" -echo -e "${RED}ā–¶ ACTION REQUIRED: Review this PR FIRST${NC}" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "🟔 HIGH PRIORITY (Contributors Waiting)" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -for pr in 37 38 21; do - pr_info=$(gh pr view $pr --repo $REPO --json number,title,author,createdAt 2>/dev/null) - pr_title=$(echo "$pr_info" | jq -r '.title') - pr_author=$(echo "$pr_info" | jq -r '.author.login') - pr_bounty=${BOUNTY_MAP[$pr]} - - echo "PR #$pr: $pr_title" - echo " Author: @$pr_author | Bounty: \$$pr_bounty" -done - -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "🟢 MIKE'S PRs (Ready to Merge)" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -mike_pr_list=$(echo "$prs" | jq -r '.[] | select(.author.login == "mikejmorgan-ai") | .number') - -for pr in $mike_pr_list; do - pr_info=$(gh pr view $pr --repo $REPO --json number,title 2>/dev/null) - pr_title=$(echo "$pr_info" | jq -r '.title') - echo "PR #$pr: $pr_title" -done - -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸŽÆ QUICK ACTIONS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "What would you like to do?" -echo "" -echo " [1] Review PR #17 (THE CRITICAL BLOCKER) šŸ”“" -echo " [2] Review ALL contributor PRs (guided workflow) 🟔" -echo " [3] Merge ALL of Mike's PRs (batch operation) 🟢" -echo " [4] View detailed PR list in browser" -echo " [5] Generate bounty payment report" -echo " [6] Post Discord update" -echo " [q] Quit" -echo "" -echo -n "Choose action: " -read -n 1 choice -echo "" -echo "" - -case $choice in - 1) - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸ”“ REVIEWING PR #17 - PACKAGE MANAGER WRAPPER" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "This is THE MVP blocker. Everything depends on this." - echo "" - echo "Opening in browser for review..." - echo "" - - gh pr view 17 --repo $REPO --web - - echo "" - echo "After reviewing the code, what's your decision?" - echo "" - echo " [a] Approve & Merge (\$100 bounty to @chandrapratamar)" - echo " [c] Request Changes (specify what needs fixing)" - echo " [s] Skip for now (review later)" - echo "" - echo -n "Decision: " - read -n 1 decision - echo "" - echo "" - - case $decision in - a|A) - echo "āœ… Approving PR #17..." - - approval="āœ… **APPROVED - OUTSTANDING WORK!** - -@chandrapratamar - You just unblocked the entire MVP! šŸŽ‰šŸŽ‰šŸŽ‰ - -**This is THE critical feature** that everything else depends on. Your implementation: -- āœ… Translates natural language to apt commands perfectly -- āœ… Integrates seamlessly with our LLM layer -- āœ… Includes comprehensive tests -- āœ… Documentation is clear and complete - -**Payment Details:** -- **Bounty: \$100 USD** -- **Processing: Within 48 hours** -- **Method: Crypto (Bitcoin/USDC) or PayPal** -- **Bonus: 2x at funding (Feb 2025) = \$200 total** - -**You're now a core Cortex contributor!** 🧠⚔ - -We'll coordinate payment via your preferred method in the next comment. - -**Thank you for making history with us!** - ---- -*Automated approval from Cortex PR Management System*" - - echo "$approval" | gh pr review 17 --repo $REPO --approve --body-file - - - echo "" - echo "Merging PR #17..." - - gh pr merge 17 --repo $REPO --squash --delete-branch && { - echo "" - echo "šŸŽ‰šŸŽ‰šŸŽ‰ PR #17 MERGED! šŸŽ‰šŸŽ‰šŸŽ‰" - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸš€ MVP BLOCKER CLEARED!" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "This unblocks:" - echo " āœ… Issue #12 (Dependency Resolution)" - echo " āœ… Issue #10 (Installation Verification)" - echo " āœ… Issue #14 (Rollback System)" - echo " āœ… MVP demonstration" - echo " āœ… February funding timeline" - echo "" - echo "šŸ’° Bounty owed: \$100 to @chandrapratamar" - echo "" - echo "IMMEDIATELY post to Discord #announcements!" - echo "" - } || { - echo "āŒ Merge failed - needs manual intervention" - } - ;; - c|C) - echo "Requesting changes on PR #17..." - echo "" - echo "Enter what needs to change:" - echo "(Press Ctrl+D when done)" - echo "---" - feedback=$(cat) - - change_request="šŸ”„ **Changes Requested** - -Thank you @chandrapratamar for tackling this critical feature! - -Before we can merge, please address: - -$feedback - -**This is THE MVP blocker**, so I'll prioritize re-review once you update. - -Questions? Ping me here or in Discord (#dev-questions). - -We're close! šŸ’Ŗ" - - echo "$change_request" | gh pr review 17 --repo $REPO --request-changes --body-file - - echo "" - echo "āœ… Change request posted" - ;; - *) - echo "ā­ļø Skipped PR #17" - ;; - esac - ;; - - 2) - echo "🟔 LAUNCHING CONTRIBUTOR PR REVIEW WORKFLOW..." - echo "" - - # Check if review script exists - if [ -f "$HOME/cortex/review-contributor-prs.sh" ]; then - bash "$HOME/cortex/review-contributor-prs.sh" - else - echo "Review script not found. Download it first:" - echo " review-contributor-prs.sh" - fi - ;; - - 3) - echo "🟢 BATCH MERGING MIKE'S PRs..." - echo "" - - # Check if merge script exists - if [ -f "$HOME/cortex/merge-mike-prs.sh" ]; then - bash "$HOME/cortex/merge-mike-prs.sh" - else - echo "Merge script not found. Download it first:" - echo " merge-mike-prs.sh" - fi - ;; - - 4) - echo "🌐 Opening PR list in browser..." - gh pr list --repo $REPO --web - ;; - - 5) - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸ’° BOUNTY PAYMENT REPORT" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - - echo "PENDING BOUNTIES (if merged):" - echo "──────────────────────────────" - echo "" - echo "PR #17 - @chandrapratamar: \$100 (Package Manager)" - echo "PR #37 - @AlexanderLuzDH: \$125 (Progress Notifications)" - echo "PR #38 - @AlexanderLuzDH: \$100 (Requirements Check)" - echo "PR #21 - @aliraza556: \$150 (Config Templates)" - echo "PR #18 - @Sahilbhatane: \$100 (CLI Interface - DRAFT)" - echo "" - echo "──────────────────────────────" - echo "TOTAL PENDING: \$575" - echo "AT 2X BONUS (FUNDING): \$1,150" - echo "" - - if [ -f "$HOME/cortex/bounties_owed.csv" ]; then - echo "ALREADY MERGED (need payment):" - echo "──────────────────────────────" - tail -n +2 "$HOME/cortex/bounties_owed.csv" | while IFS=',' read -r pr dev feature amount date status; do - if [ "$status" = "PENDING" ]; then - echo "$pr - @$dev: \$$amount" - fi - done - echo "" - fi - ;; - - 6) - echo "šŸ“± GENERATING DISCORD ANNOUNCEMENT..." - echo "" - - announcement="šŸŽ‰ **CORTEX PROJECT UPDATE - $(date +%B\ %d,\ %Y)** - -**PR Review Session Complete!** - -**Current Status:** -- šŸ“Š **$total_prs PRs open** ($contributor_prs from contributors, $mike_prs from Mike) -- šŸ’° **\$$total_contributor_bounties in bounties** pending review -- šŸ”“ **PR #17 (Package Manager)** = THE MVP BLOCKER - -**Action Items:** -- Contributor PRs being reviewed this week -- Bounties will be processed within 48 hours of merge -- 2x bonus reminder: All bounties double at funding (Feb 2025) - -**For Contributors:** -- Check your PR status on GitHub -- Questions? #dev-questions channel -- New issues available for claiming - -**The Momentum is Real:** -- Professional team execution -- MVP timeline on track (Feb 2025) -- Building the future of Linux! 🧠⚔ - -Browse open issues: https://github.com/$REPO/issues -Join discussion: https://discord.gg/uCqHvxjU83" - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "$announcement" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Copy the above and post to Discord #announcements" - ;; - - q|Q) - echo "šŸ‘‹ Exiting dashboard..." - exit 0 - ;; - - *) - echo "Invalid choice" - ;; -esac - -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Dashboard session complete" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +#!/bin/bash +# CORTEX - MASTER PR DASHBOARD & MANAGEMENT +# Complete PR overview, batch operations, and bounty tracking + +set -e + +echo "šŸŽ›ļø CORTEX - MASTER PR DASHBOARD" +echo "================================" +echo "" + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") +export GH_TOKEN="$GITHUB_TOKEN" + +# Colors for terminal output +RED='\033[0;31m' +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ“Š PR STATUS OVERVIEW" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Get all open PRs +prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,isDraft,reviewDecision --limit 50 2>/dev/null) + +total_prs=$(echo "$prs" | jq 'length') +contributor_prs=$(echo "$prs" | jq '[.[] | select(.author.login != "mikejmorgan-ai")] | length') +mike_prs=$(echo "$prs" | jq '[.[] | select(.author.login == "mikejmorgan-ai")] | length') + +echo "Total Open PRs: $total_prs" +echo " ā”œā”€ From Contributors: $contributor_prs (šŸ”„ Need review)" +echo " └─ From Mike: $mike_prs (Can merge anytime)" +echo "" + +# Calculate bounties at stake +echo "šŸ’° ESTIMATED BOUNTIES AT STAKE" +echo "────────────────────────────────" +echo "" + +declare -A BOUNTY_MAP +BOUNTY_MAP[17]=100 # Package Manager +BOUNTY_MAP[37]=125 # Progress Notifications +BOUNTY_MAP[38]=100 # Requirements Check +BOUNTY_MAP[21]=150 # Config Templates +BOUNTY_MAP[18]=100 # CLI Interface + +total_contributor_bounties=0 + +for pr in 17 37 38 21 18; do + bounty=${BOUNTY_MAP[$pr]} + total_contributor_bounties=$((total_contributor_bounties + bounty)) +done + +echo "Contributor PRs: \$$total_contributor_bounties" +echo "At 2x bonus (funding): \$$((total_contributor_bounties * 2))" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ”“ CRITICAL PRIORITY" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +pr17_info=$(gh pr view 17 --repo $REPO --json number,title,author,createdAt,state 2>/dev/null) +pr17_title=$(echo "$pr17_info" | jq -r '.title') +pr17_author=$(echo "$pr17_info" | jq -r '.author.login') +pr17_created=$(echo "$pr17_info" | jq -r '.createdAt' | cut -d'T' -f1) +pr17_days_old=$(( ( $(date +%s) - $(date -j -f "%Y-%m-%d" "$pr17_created" +%s 2>/dev/null || date +%s) ) / 86400 )) + +echo "PR #17: $pr17_title" +echo "Author: @$pr17_author" +echo "Age: $pr17_days_old days old" +echo "Bounty: \$100" +echo "Impact: āš ļø MVP BLOCKER - Everything waits on this" +echo "" +echo -e "${RED}ā–¶ ACTION REQUIRED: Review this PR FIRST${NC}" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🟔 HIGH PRIORITY (Contributors Waiting)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +for pr in 37 38 21; do + pr_info=$(gh pr view $pr --repo $REPO --json number,title,author,createdAt 2>/dev/null) + pr_title=$(echo "$pr_info" | jq -r '.title') + pr_author=$(echo "$pr_info" | jq -r '.author.login') + pr_bounty=${BOUNTY_MAP[$pr]} + + echo "PR #$pr: $pr_title" + echo " Author: @$pr_author | Bounty: \$$pr_bounty" +done + +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🟢 MIKE'S PRs (Ready to Merge)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +mike_pr_list=$(echo "$prs" | jq -r '.[] | select(.author.login == "mikejmorgan-ai") | .number') + +for pr in $mike_pr_list; do + pr_info=$(gh pr view $pr --repo $REPO --json number,title 2>/dev/null) + pr_title=$(echo "$pr_info" | jq -r '.title') + echo "PR #$pr: $pr_title" +done + +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸŽÆ QUICK ACTIONS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "What would you like to do?" +echo "" +echo " [1] Review PR #17 (THE CRITICAL BLOCKER) šŸ”“" +echo " [2] Review ALL contributor PRs (guided workflow) 🟔" +echo " [3] Merge ALL of Mike's PRs (batch operation) 🟢" +echo " [4] View detailed PR list in browser" +echo " [5] Generate bounty payment report" +echo " [6] Post Discord update" +echo " [q] Quit" +echo "" +echo -n "Choose action: " +read -n 1 choice +echo "" +echo "" + +case $choice in + 1) + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸ”“ REVIEWING PR #17 - PACKAGE MANAGER WRAPPER" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "This is THE MVP blocker. Everything depends on this." + echo "" + echo "Opening in browser for review..." + echo "" + + gh pr view 17 --repo $REPO --web + + echo "" + echo "After reviewing the code, what's your decision?" + echo "" + echo " [a] Approve & Merge (\$100 bounty to @chandrapratamar)" + echo " [c] Request Changes (specify what needs fixing)" + echo " [s] Skip for now (review later)" + echo "" + echo -n "Decision: " + read -n 1 decision + echo "" + echo "" + + case $decision in + a|A) + echo "āœ… Approving PR #17..." + + approval="āœ… **APPROVED - OUTSTANDING WORK!** + +@chandrapratamar - You just unblocked the entire MVP! šŸŽ‰šŸŽ‰šŸŽ‰ + +**This is THE critical feature** that everything else depends on. Your implementation: +- āœ… Translates natural language to apt commands perfectly +- āœ… Integrates seamlessly with our LLM layer +- āœ… Includes comprehensive tests +- āœ… Documentation is clear and complete + +**Payment Details:** +- **Bounty: \$100 USD** +- **Processing: Within 48 hours** +- **Method: Crypto (Bitcoin/USDC) or PayPal** +- **Bonus: 2x at funding (Feb 2025) = \$200 total** + +**You're now a core Cortex contributor!** 🧠⚔ + +We'll coordinate payment via your preferred method in the next comment. + +**Thank you for making history with us!** + +--- +*Automated approval from Cortex PR Management System*" + + echo "$approval" | gh pr review 17 --repo $REPO --approve --body-file - + + echo "" + echo "Merging PR #17..." + + gh pr merge 17 --repo $REPO --squash --delete-branch && { + echo "" + echo "šŸŽ‰šŸŽ‰šŸŽ‰ PR #17 MERGED! šŸŽ‰šŸŽ‰šŸŽ‰" + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸš€ MVP BLOCKER CLEARED!" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "This unblocks:" + echo " āœ… Issue #12 (Dependency Resolution)" + echo " āœ… Issue #10 (Installation Verification)" + echo " āœ… Issue #14 (Rollback System)" + echo " āœ… MVP demonstration" + echo " āœ… February funding timeline" + echo "" + echo "šŸ’° Bounty owed: \$100 to @chandrapratamar" + echo "" + echo "IMMEDIATELY post to Discord #announcements!" + echo "" + } || { + echo "āŒ Merge failed - needs manual intervention" + } + ;; + c|C) + echo "Requesting changes on PR #17..." + echo "" + echo "Enter what needs to change:" + echo "(Press Ctrl+D when done)" + echo "---" + feedback=$(cat) + + change_request="šŸ”„ **Changes Requested** + +Thank you @chandrapratamar for tackling this critical feature! + +Before we can merge, please address: + +$feedback + +**This is THE MVP blocker**, so I'll prioritize re-review once you update. + +Questions? Ping me here or in Discord (#dev-questions). + +We're close! šŸ’Ŗ" + + echo "$change_request" | gh pr review 17 --repo $REPO --request-changes --body-file - + echo "" + echo "āœ… Change request posted" + ;; + *) + echo "ā­ļø Skipped PR #17" + ;; + esac + ;; + + 2) + echo "🟔 LAUNCHING CONTRIBUTOR PR REVIEW WORKFLOW..." + echo "" + + # Check if review script exists + if [ -f "$HOME/cortex/review-contributor-prs.sh" ]; then + bash "$HOME/cortex/review-contributor-prs.sh" + else + echo "Review script not found. Download it first:" + echo " review-contributor-prs.sh" + fi + ;; + + 3) + echo "🟢 BATCH MERGING MIKE'S PRs..." + echo "" + + # Check if merge script exists + if [ -f "$HOME/cortex/merge-mike-prs.sh" ]; then + bash "$HOME/cortex/merge-mike-prs.sh" + else + echo "Merge script not found. Download it first:" + echo " merge-mike-prs.sh" + fi + ;; + + 4) + echo "🌐 Opening PR list in browser..." + gh pr list --repo $REPO --web + ;; + + 5) + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸ’° BOUNTY PAYMENT REPORT" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + echo "PENDING BOUNTIES (if merged):" + echo "──────────────────────────────" + echo "" + echo "PR #17 - @chandrapratamar: \$100 (Package Manager)" + echo "PR #37 - @AlexanderLuzDH: \$125 (Progress Notifications)" + echo "PR #38 - @AlexanderLuzDH: \$100 (Requirements Check)" + echo "PR #21 - @aliraza556: \$150 (Config Templates)" + echo "PR #18 - @Sahilbhatane: \$100 (CLI Interface - DRAFT)" + echo "" + echo "──────────────────────────────" + echo "TOTAL PENDING: \$575" + echo "AT 2X BONUS (FUNDING): \$1,150" + echo "" + + if [ -f "$HOME/cortex/bounties_owed.csv" ]; then + echo "ALREADY MERGED (need payment):" + echo "──────────────────────────────" + tail -n +2 "$HOME/cortex/bounties_owed.csv" | while IFS=',' read -r pr dev feature amount date status; do + if [ "$status" = "PENDING" ]; then + echo "$pr - @$dev: \$$amount" + fi + done + echo "" + fi + ;; + + 6) + echo "šŸ“± GENERATING DISCORD ANNOUNCEMENT..." + echo "" + + announcement="šŸŽ‰ **CORTEX PROJECT UPDATE - $(date +%B\ %d,\ %Y)** + +**PR Review Session Complete!** + +**Current Status:** +- šŸ“Š **$total_prs PRs open** ($contributor_prs from contributors, $mike_prs from Mike) +- šŸ’° **\$$total_contributor_bounties in bounties** pending review +- šŸ”“ **PR #17 (Package Manager)** = THE MVP BLOCKER + +**Action Items:** +- Contributor PRs being reviewed this week +- Bounties will be processed within 48 hours of merge +- 2x bonus reminder: All bounties double at funding (Feb 2025) + +**For Contributors:** +- Check your PR status on GitHub +- Questions? #dev-questions channel +- New issues available for claiming + +**The Momentum is Real:** +- Professional team execution +- MVP timeline on track (Feb 2025) +- Building the future of Linux! 🧠⚔ + +Browse open issues: https://github.com/$REPO/issues +Join discussion: https://discord.gg/uCqHvxjU83" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "$announcement" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Copy the above and post to Discord #announcements" + ;; + + q|Q) + echo "šŸ‘‹ Exiting dashboard..." + exit 0 + ;; + + *) + echo "Invalid choice" + ;; +esac + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Dashboard session complete" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/scripts/automation/focus-on-mvp.sh b/scripts/automation/focus-on-mvp.sh old mode 100755 new mode 100644 index 5f5698a5..ebf1dc71 --- a/scripts/automation/focus-on-mvp.sh +++ b/scripts/automation/focus-on-mvp.sh @@ -1,105 +1,105 @@ -#!/bin/bash -# Close non-MVP issues to focus contributors on critical work - -set -e - -echo "šŸŽÆ FOCUSING REPOSITORY ON MVP ISSUES" -echo "======================================" -echo "" - -cd ~/cortex || { echo "āŒ cortex repo not found"; exit 1; } - -# Strategy: Close issues 46-200+ with explanation comment -# Keep issues 1-45 open (MVP critical work) - -echo "Strategy:" -echo " Keep open: Issues #1-45 (MVP critical)" -echo " Close: Issues #46+ (post-MVP features)" -echo "" - -read -p "Close issues #46-200 as 'post-MVP'? (y/n): " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 0 -fi - -# Comment to add when closing -CLOSE_MESSAGE="šŸŽÆ **Closing for MVP Focus** - -This issue is being closed to help the team focus on MVP-critical features (#1-45). - -**This is NOT abandoned** - it's an important feature we'll revisit after MVP completion. - -**Timeline:** -- **Now (Nov-Dec 2024):** Focus on MVP (Issues #1-45) -- **January 2025:** Reopen post-MVP features -- **February 2025:** Seed funding round - -**Want to work on this anyway?** -Comment below and we can discuss! We're always open to great contributions. - -**Tracking:** Labeled as \`post-mvp\` for easy filtering when we reopen. - -Thanks for understanding! šŸš€ - -— Mike (@mikejmorgan-ai)" - -echo "šŸ“ Closing issues #46-200..." -echo "" - -# Function to close issue -close_issue() { - local issue_num=$1 - - echo " Closing #$issue_num..." - - # Add comment - gh issue comment $issue_num --body "$CLOSE_MESSAGE" 2>/dev/null || { - echo " āš ļø Could not comment on #$issue_num (may not exist)" - return 1 - } - - # Add post-mvp label - gh issue edit $issue_num --add-label "post-mvp" 2>/dev/null - - # Close issue - gh issue close $issue_num --reason "not planned" 2>/dev/null || { - echo " āš ļø Could not close #$issue_num" - return 1 - } - - echo " āœ… Closed #$issue_num" - return 0 -} - -# Close issues 46-200 -CLOSED_COUNT=0 -FAILED_COUNT=0 - -for issue_num in {46..200}; do - if close_issue $issue_num; then - ((CLOSED_COUNT++)) - else - ((FAILED_COUNT++)) - fi - - # Rate limiting - pause every 10 issues - if (( issue_num % 10 == 0 )); then - echo " āøļø Pausing for rate limit..." - sleep 2 - fi -done - -echo "" -echo "==============================================" -echo "āœ… CLEANUP COMPLETE" -echo "==============================================" -echo "Issues closed: $CLOSED_COUNT" -echo "Failed/not found: $FAILED_COUNT" -echo "" -echo "Repository now shows MVP-focused issues only!" -echo "" -echo "View open issues: https://github.com/cortexlinux/cortex/issues" -echo "View post-MVP: https://github.com/cortexlinux/cortex/issues?q=is%3Aclosed+label%3Apost-mvp" -echo "" +#!/bin/bash +# Close non-MVP issues to focus contributors on critical work + +set -e + +echo "šŸŽÆ FOCUSING REPOSITORY ON MVP ISSUES" +echo "======================================" +echo "" + +cd ~/cortex || { echo "āŒ cortex repo not found"; exit 1; } + +# Strategy: Close issues 46-200+ with explanation comment +# Keep issues 1-45 open (MVP critical work) + +echo "Strategy:" +echo " Keep open: Issues #1-45 (MVP critical)" +echo " Close: Issues #46+ (post-MVP features)" +echo "" + +read -p "Close issues #46-200 as 'post-MVP'? (y/n): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +# Comment to add when closing +CLOSE_MESSAGE="šŸŽÆ **Closing for MVP Focus** + +This issue is being closed to help the team focus on MVP-critical features (#1-45). + +**This is NOT abandoned** - it's an important feature we'll revisit after MVP completion. + +**Timeline:** +- **Now (Nov-Dec 2024):** Focus on MVP (Issues #1-45) +- **January 2025:** Reopen post-MVP features +- **February 2025:** Seed funding round + +**Want to work on this anyway?** +Comment below and we can discuss! We're always open to great contributions. + +**Tracking:** Labeled as \`post-mvp\` for easy filtering when we reopen. + +Thanks for understanding! šŸš€ + +— Mike (@mikejmorgan-ai)" + +echo "šŸ“ Closing issues #46-200..." +echo "" + +# Function to close issue +close_issue() { + local issue_num=$1 + + echo " Closing #$issue_num..." + + # Add comment + gh issue comment $issue_num --body "$CLOSE_MESSAGE" 2>/dev/null || { + echo " āš ļø Could not comment on #$issue_num (may not exist)" + return 1 + } + + # Add post-mvp label + gh issue edit $issue_num --add-label "post-mvp" 2>/dev/null + + # Close issue + gh issue close $issue_num --reason "not planned" 2>/dev/null || { + echo " āš ļø Could not close #$issue_num" + return 1 + } + + echo " āœ… Closed #$issue_num" + return 0 +} + +# Close issues 46-200 +CLOSED_COUNT=0 +FAILED_COUNT=0 + +for issue_num in {46..200}; do + if close_issue $issue_num; then + ((CLOSED_COUNT++)) + else + ((FAILED_COUNT++)) + fi + + # Rate limiting - pause every 10 issues + if (( issue_num % 10 == 0 )); then + echo " āøļø Pausing for rate limit..." + sleep 2 + fi +done + +echo "" +echo "==============================================" +echo "āœ… CLEANUP COMPLETE" +echo "==============================================" +echo "Issues closed: $CLOSED_COUNT" +echo "Failed/not found: $FAILED_COUNT" +echo "" +echo "Repository now shows MVP-focused issues only!" +echo "" +echo "View open issues: https://github.com/cortexlinux/cortex/issues" +echo "View post-MVP: https://github.com/cortexlinux/cortex/issues?q=is%3Aclosed+label%3Apost-mvp" +echo "" diff --git a/scripts/automation/manage_cortex_prs.sh b/scripts/automation/manage_cortex_prs.sh old mode 100755 new mode 100644 index ee3d3d74..719a71bc --- a/scripts/automation/manage_cortex_prs.sh +++ b/scripts/automation/manage_cortex_prs.sh @@ -1,435 +1,435 @@ -#!/bin/bash -# Cortex Linux - Master PR Control & Team Coordination -# Complete automation: reviews, assignments, Discord, payments, everything - -set -e - -echo "🧠 CORTEX LINUX - MASTER PR CONTROL SYSTEM" -echo "==========================================" -echo "" - -# Configuration -REPO="cortexlinux/cortex" -REPO_DIR="$HOME/cortex" -DISCORD_INVITE="https://discord.gg/uCqHvxjU83" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") -BOUNTY_CSV="$REPO_DIR/bounties_paid.csv" - -# Ensure we're in the repo -cd "$REPO_DIR" || { echo "āŒ Repo not found at $REPO_DIR"; exit 1; } - -# Create bounty tracking CSV if it doesn't exist -if [ ! -f "$BOUNTY_CSV" ]; then - echo "PR_Number,Author,Amount,Status,Payment_Status,Date" > "$BOUNTY_CSV" -fi - -echo "šŸ“Š STEP 1: FETCHING ALL OPEN PRS" -echo "=================================" -echo "" - -# Get all open PRs -prs=$(gh pr list --repo "$REPO" --state open --json number,title,author,createdAt,reviews,isDraft,mergeable --limit 50) -total_prs=$(echo "$prs" | jq length) - -echo "Found $total_prs open PR(s)" -echo "" - -if [ "$total_prs" -eq 0 ]; then - echo "āœ… No PRs to process!" - exit 0 -fi - -# Display all PRs -echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Draft: \(.isDraft)"' -echo "" - -echo "šŸŽÆ STEP 2: CATEGORIZING PRS" -echo "===========================" -echo "" - -# Arrays for different PR categories -critical_prs=() -ready_to_merge=() -needs_review=() -draft_prs=() -stale_prs=() - -# Categorize each PR -while IFS= read -r pr_num; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - is_draft=$(echo "$pr_data" | jq -r '.isDraft') - created=$(echo "$pr_data" | jq -r '.createdAt') - mergeable=$(echo "$pr_data" | jq -r '.mergeable') - review_count=$(echo "$pr_data" | jq -r '.reviews | length') - - # Calculate age - created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) - now_ts=$(date +%s) - age_days=$(( (now_ts - created_ts) / 86400 )) - - # Skip drafts - if [ "$is_draft" = "true" ]; then - draft_prs+=($pr_num) - continue - fi - - # Check if it's the critical package manager PR - if [[ "$title" == *"package"* ]] || [[ "$title" == *"Package"* ]] || [ "$pr_num" -eq 195 ]; then - critical_prs+=($pr_num) - echo "šŸ”„ CRITICAL: PR #$pr_num - $title (Age: $age_days days)" - elif [ "$mergeable" = "MERGEABLE" ] && [ "$review_count" -gt 0 ]; then - ready_to_merge+=($pr_num) - echo "āœ… READY TO MERGE: PR #$pr_num - $title" - elif [ "$review_count" -eq 0 ]; then - needs_review+=($pr_num) - echo "šŸ“‹ NEEDS REVIEW: PR #$pr_num - $title (Age: $age_days days)" - fi - - # Check if stale (>5 days) - if [ "$age_days" -gt 5 ]; then - stale_prs+=($pr_num) - fi -done < <(echo "$prs" | jq -r '.[].number') - -echo "" -echo "Summary:" -echo " šŸ”„ Critical PRs: ${#critical_prs[@]}" -echo " āœ… Ready to merge: ${#ready_to_merge[@]}" -echo " šŸ“‹ Need review: ${#needs_review[@]}" -echo " šŸ“ Drafts: ${#draft_prs[@]}" -echo " ā° Stale (>5 days): ${#stale_prs[@]}" -echo "" - -read -p "Continue with automated processing? (y/n): " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 0 -fi - -echo "" -echo "šŸŽÆ STEP 3: PROCESSING CRITICAL PRS" -echo "==================================" -echo "" - -for pr_num in "${critical_prs[@]}"; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - - echo "Processing CRITICAL PR #$pr_num: $title" - echo "Author: @$author" - echo "" - - # Assign reviewers if not already assigned - echo " Assigning reviewers: dhvil, mikejmorgan-ai" - gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || echo " (Reviewers already assigned)" - - # Post urgent review comment - comment="šŸ”„ **CRITICAL PATH REVIEW** - -Hi @$author! This PR is blocking our MVP completion. - -**Urgent Review In Progress:** -- āœ… Technical review by @dhvil -- āœ… Final approval by @mikejmorgan-ai -- ā±ļø Target decision: Within 24 hours - -**Payment Ready:** -šŸ’° Bounty will be paid via Discord crypto (BTC/USDC) within 24 hours of merge - -**Join Discord for payment coordination:** -šŸ‘‰ $DISCORD_INVITE - -We're prioritizing this merge! Thanks for the critical work. šŸš€" - - gh pr comment $pr_num --body "$comment" 2>/dev/null || echo " (Comment already exists)" - - echo " āœ… Critical PR tagged and reviewers notified" - echo "" - sleep 1 -done - -echo "" -echo "āœ… STEP 4: AUTO-MERGING READY PRS" -echo "=================================" -echo "" - -merged_count=0 -for pr_num in "${ready_to_merge[@]}"; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - - echo "PR #$pr_num: $title by @$author" - echo " Status: Mergeable with approvals" - - # Determine bounty amount based on issue - bounty_amount="TBD" - if [[ "$title" == *"context"* ]] || [[ "$title" == *"Context"* ]]; then - bounty_amount="150" - elif [[ "$title" == *"logging"* ]] || [[ "$title" == *"Logging"* ]]; then - bounty_amount="100" - fi - - read -p " Merge PR #$pr_num? (y/n): " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]]; then - # Merge the PR - gh pr merge $pr_num --squash --delete-branch - echo " āœ… Merged!" - - # Post payment comment - payment_comment="šŸŽ‰ **PR MERGED!** - -Thanks @$author! Your contribution has been merged into main. - -**šŸ’° Payment Details:** -- Bounty: \$$bounty_amount (as specified in issue) -- Method: Crypto (Bitcoin or USDC) -- Timeline: Within 24 hours - -**Next Steps:** -1. Join Discord: $DISCORD_INVITE -2. DM @mikejmorgan with your wallet address -3. Receive payment confirmation - -Great work! Looking forward to your next contribution. šŸš€" - - gh pr comment $pr_num --body "$payment_comment" - - # Track in CSV - echo "$pr_num,$author,$bounty_amount,Merged,Pending Payment,$(date +%Y-%m-%d)" >> "$BOUNTY_CSV" - - ((merged_count++)) - echo "" - else - echo " ā­ļø Skipped" - echo "" - fi - sleep 1 -done - -echo "Merged $merged_count PR(s)" -echo "" - -echo "šŸ“‹ STEP 5: ASSIGNING REVIEWERS TO PENDING PRS" -echo "==============================================" -echo "" - -for pr_num in "${needs_review[@]}"; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - - echo "PR #$pr_num: $title by @$author" - - # Assign reviewers - if [ "$author" != "dhvil" ] && [ "$author" != "mikejmorgan-ai" ]; then - gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || true - echo " āœ… Assigned reviewers: dhvil, mikejmorgan-ai" - else - gh pr edit $pr_num --add-reviewer mikejmorgan-ai 2>/dev/null || true - echo " āœ… Assigned reviewer: mikejmorgan-ai" - fi - - # Post welcome comment - welcome_comment="Thanks @$author for this contribution! šŸŽ‰ - -**Review Process:** -1. āœ… Reviewers assigned - expect feedback within 24-48 hours -2. šŸ’¬ **Join Discord**: $DISCORD_INVITE -3. šŸ’° **Bounty Payment**: Crypto (BTC/USDC) via Discord after merge - -**Important:** -- All bounties tracked and paid through Discord -- Please join to coordinate payment details -- Typical merge → payment time: 24-48 hours - -Looking forward to reviewing this! šŸš€" - - # Check if we already commented - existing=$(gh pr view $pr_num --json comments --jq '[.comments[] | select(.author.login == "mikejmorgan-ai")] | length') - if [ "$existing" -eq 0 ]; then - gh pr comment $pr_num --body "$welcome_comment" - echo " āœ… Posted welcome comment" - else - echo " (Welcome comment already exists)" - fi - - echo "" - sleep 1 -done - -echo "" -echo "ā° STEP 6: SENDING STALE PR REMINDERS" -echo "=====================================" -echo "" - -for pr_num in "${stale_prs[@]}"; do - # Skip if it's in draft or critical (already handled) - if [[ " ${draft_prs[@]} " =~ " ${pr_num} " ]] || [[ " ${critical_prs[@]} " =~ " ${pr_num} " ]]; then - continue - fi - - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - created=$(echo "$pr_data" | jq -r '.createdAt') - - created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) - now_ts=$(date +%s) - age_days=$(( (now_ts - created_ts) / 86400 )) - - echo "PR #$pr_num: $title by @$author ($age_days days old)" - - stale_comment="Hi @$author! šŸ‘‹ - -This PR has been open for $age_days days. Quick status check: - -šŸ“‹ **Checklist:** -- [ ] Joined Discord? ($DISCORD_INVITE) -- [ ] All tests passing? -- [ ] Addressed review feedback? - -šŸ’° **Payment Reminder:** -- Bounties paid via crypto (Bitcoin/USDC) -- Processed through Discord DMs -- Sent within 24 hours of merge - -Need help? Let us know in Discord! We want to get this merged and pay you ASAP. šŸš€" - - gh pr comment $pr_num --body "$stale_comment" - echo " āœ… Sent reminder" - echo "" - sleep 1 -done - -echo "" -echo "šŸ’¬ STEP 7: GENERATING DISCORD ANNOUNCEMENT" -echo "==========================================" -echo "" - -cat << DISCORD_EOF > /tmp/discord_announcement.txt -šŸš€ **PR STATUS UPDATE - $(date +"%B %d, %Y")** - -Just completed automated PR processing! Here's where we stand: - -**šŸ“Š Statistics:** -- Total Open PRs: $total_prs -- šŸ”„ Critical (Package Manager): ${#critical_prs[@]} -- āœ… Merged Today: $merged_count -- šŸ“‹ Under Review: ${#needs_review[@]} -- ā° Stale Reminders Sent: ${#stale_prs[@]} - -**šŸŽÆ Focus Areas:** -DISCORD_EOF - -if [ ${#critical_prs[@]} -gt 0 ]; then - echo "• šŸ”„ PR #${critical_prs[0]} (Package Manager) - CRITICAL PATH - Under urgent review" >> /tmp/discord_announcement.txt -fi - -cat << DISCORD_EOF2 >> /tmp/discord_announcement.txt - -**šŸ’° Payment Process:** -1. PR gets merged āœ… -2. I DM you for wallet address šŸ’¬ -3. Crypto sent within 24 hours šŸ’ø -4. You confirm receipt āœ… - -**All contributors:** Join Discord for bounty coordination! -šŸ‘‰ $DISCORD_INVITE - -Let's keep the momentum going! šŸ”„ - -- Mike -DISCORD_EOF2 - -echo "Discord announcement generated:" -echo "===============================" -cat /tmp/discord_announcement.txt -echo "===============================" -echo "" -echo "šŸ“‹ Copy the above to Discord #announcements" -echo "" - -echo "" -echo "šŸ“Š STEP 8: PAYMENT TRACKING SUMMARY" -echo "===================================" -echo "" - -if [ -f "$BOUNTY_CSV" ]; then - echo "Payments Pending:" - tail -n +2 "$BOUNTY_CSV" | grep "Pending" 2>/dev/null | while IFS=, read -r pr author amount status payment date; do - echo " PR #$pr - @$author - \$$amount - $date" - done || echo " No pending payments" - echo "" - echo "Full tracking: $BOUNTY_CSV" -fi - -echo "" -echo "šŸ“§ STEP 9: CONTRIBUTOR DM TEMPLATES" -echo "===================================" -echo "" - -# Generate DM templates for unique contributors -contributors=$(echo "$prs" | jq -r '.[].author.login' | sort -u) - -echo "Send these DMs on Discord:" -echo "" - -for contributor in $contributors; do - pr_count=$(echo "$prs" | jq -r --arg author "$contributor" '[.[] | select(.author.login == $author)] | length') - - if [ "$pr_count" -gt 0 ]; then - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "To: @$contributor ($pr_count open PR)" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - cat << DM_EOF - -Hey! Just processed your Cortex PR(s) - great work! šŸŽ‰ - -**Quick Check:** -1. Have you joined Discord? ($DISCORD_INVITE) -2. What's your crypto wallet address? (BTC or USDC) -3. Any blockers I can help with? - -**Payment Timeline:** -- PR review: 24-48 hours -- Merge decision: Clear feedback either way -- Payment: Within 24 hours of merge - -Looking forward to merging your work! - -- Mike - -DM_EOF - fi -done - -echo "" -echo "==============================================" -echo "āœ… MASTER PR CONTROL COMPLETE" -echo "==============================================" -echo "" - -echo "šŸ“Š Summary of Actions:" -echo " • Reviewed $total_prs PRs" -echo " • Assigned reviewers to ${#needs_review[@]} PRs" -echo " • Merged $merged_count PRs" -echo " • Flagged ${#critical_prs[@]} critical PR(s)" -echo " • Sent ${#stale_prs[@]} stale reminders" -echo "" - -echo "šŸ“‹ Next Manual Steps:" -echo " 1. Copy Discord announcement to #announcements" -echo " 2. Send DMs to contributors (templates above)" -echo " 3. Review critical PR #${critical_prs[0]:-N/A} urgently" -echo " 4. Process $merged_count payment(s) via crypto" -echo "" - -echo "šŸ”„ Run this script daily to maintain PR velocity!" -echo "" -echo "āœ… All done!" +#!/bin/bash +# Cortex Linux - Master PR Control & Team Coordination +# Complete automation: reviews, assignments, Discord, payments, everything + +set -e + +echo "🧠 CORTEX LINUX - MASTER PR CONTROL SYSTEM" +echo "==========================================" +echo "" + +# Configuration +REPO="cortexlinux/cortex" +REPO_DIR="$HOME/cortex" +DISCORD_INVITE="https://discord.gg/uCqHvxjU83" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") +BOUNTY_CSV="$REPO_DIR/bounties_paid.csv" + +# Ensure we're in the repo +cd "$REPO_DIR" || { echo "āŒ Repo not found at $REPO_DIR"; exit 1; } + +# Create bounty tracking CSV if it doesn't exist +if [ ! -f "$BOUNTY_CSV" ]; then + echo "PR_Number,Author,Amount,Status,Payment_Status,Date" > "$BOUNTY_CSV" +fi + +echo "šŸ“Š STEP 1: FETCHING ALL OPEN PRS" +echo "=================================" +echo "" + +# Get all open PRs +prs=$(gh pr list --repo "$REPO" --state open --json number,title,author,createdAt,reviews,isDraft,mergeable --limit 50) +total_prs=$(echo "$prs" | jq length) + +echo "Found $total_prs open PR(s)" +echo "" + +if [ "$total_prs" -eq 0 ]; then + echo "āœ… No PRs to process!" + exit 0 +fi + +# Display all PRs +echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Draft: \(.isDraft)"' +echo "" + +echo "šŸŽÆ STEP 2: CATEGORIZING PRS" +echo "===========================" +echo "" + +# Arrays for different PR categories +critical_prs=() +ready_to_merge=() +needs_review=() +draft_prs=() +stale_prs=() + +# Categorize each PR +while IFS= read -r pr_num; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + is_draft=$(echo "$pr_data" | jq -r '.isDraft') + created=$(echo "$pr_data" | jq -r '.createdAt') + mergeable=$(echo "$pr_data" | jq -r '.mergeable') + review_count=$(echo "$pr_data" | jq -r '.reviews | length') + + # Calculate age + created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) + now_ts=$(date +%s) + age_days=$(( (now_ts - created_ts) / 86400 )) + + # Skip drafts + if [ "$is_draft" = "true" ]; then + draft_prs+=($pr_num) + continue + fi + + # Check if it's the critical package manager PR + if [[ "$title" == *"package"* ]] || [[ "$title" == *"Package"* ]] || [ "$pr_num" -eq 195 ]; then + critical_prs+=($pr_num) + echo "šŸ”„ CRITICAL: PR #$pr_num - $title (Age: $age_days days)" + elif [ "$mergeable" = "MERGEABLE" ] && [ "$review_count" -gt 0 ]; then + ready_to_merge+=($pr_num) + echo "āœ… READY TO MERGE: PR #$pr_num - $title" + elif [ "$review_count" -eq 0 ]; then + needs_review+=($pr_num) + echo "šŸ“‹ NEEDS REVIEW: PR #$pr_num - $title (Age: $age_days days)" + fi + + # Check if stale (>5 days) + if [ "$age_days" -gt 5 ]; then + stale_prs+=($pr_num) + fi +done < <(echo "$prs" | jq -r '.[].number') + +echo "" +echo "Summary:" +echo " šŸ”„ Critical PRs: ${#critical_prs[@]}" +echo " āœ… Ready to merge: ${#ready_to_merge[@]}" +echo " šŸ“‹ Need review: ${#needs_review[@]}" +echo " šŸ“ Drafts: ${#draft_prs[@]}" +echo " ā° Stale (>5 days): ${#stale_prs[@]}" +echo "" + +read -p "Continue with automated processing? (y/n): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +echo "" +echo "šŸŽÆ STEP 3: PROCESSING CRITICAL PRS" +echo "==================================" +echo "" + +for pr_num in "${critical_prs[@]}"; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + + echo "Processing CRITICAL PR #$pr_num: $title" + echo "Author: @$author" + echo "" + + # Assign reviewers if not already assigned + echo " Assigning reviewers: dhvil, mikejmorgan-ai" + gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || echo " (Reviewers already assigned)" + + # Post urgent review comment + comment="šŸ”„ **CRITICAL PATH REVIEW** + +Hi @$author! This PR is blocking our MVP completion. + +**Urgent Review In Progress:** +- āœ… Technical review by @dhvil +- āœ… Final approval by @mikejmorgan-ai +- ā±ļø Target decision: Within 24 hours + +**Payment Ready:** +šŸ’° Bounty will be paid via Discord crypto (BTC/USDC) within 24 hours of merge + +**Join Discord for payment coordination:** +šŸ‘‰ $DISCORD_INVITE + +We're prioritizing this merge! Thanks for the critical work. šŸš€" + + gh pr comment $pr_num --body "$comment" 2>/dev/null || echo " (Comment already exists)" + + echo " āœ… Critical PR tagged and reviewers notified" + echo "" + sleep 1 +done + +echo "" +echo "āœ… STEP 4: AUTO-MERGING READY PRS" +echo "=================================" +echo "" + +merged_count=0 +for pr_num in "${ready_to_merge[@]}"; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + + echo "PR #$pr_num: $title by @$author" + echo " Status: Mergeable with approvals" + + # Determine bounty amount based on issue + bounty_amount="TBD" + if [[ "$title" == *"context"* ]] || [[ "$title" == *"Context"* ]]; then + bounty_amount="150" + elif [[ "$title" == *"logging"* ]] || [[ "$title" == *"Logging"* ]]; then + bounty_amount="100" + fi + + read -p " Merge PR #$pr_num? (y/n): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + # Merge the PR + gh pr merge $pr_num --squash --delete-branch + echo " āœ… Merged!" + + # Post payment comment + payment_comment="šŸŽ‰ **PR MERGED!** + +Thanks @$author! Your contribution has been merged into main. + +**šŸ’° Payment Details:** +- Bounty: \$$bounty_amount (as specified in issue) +- Method: Crypto (Bitcoin or USDC) +- Timeline: Within 24 hours + +**Next Steps:** +1. Join Discord: $DISCORD_INVITE +2. DM @mikejmorgan with your wallet address +3. Receive payment confirmation + +Great work! Looking forward to your next contribution. šŸš€" + + gh pr comment $pr_num --body "$payment_comment" + + # Track in CSV + echo "$pr_num,$author,$bounty_amount,Merged,Pending Payment,$(date +%Y-%m-%d)" >> "$BOUNTY_CSV" + + ((merged_count++)) + echo "" + else + echo " ā­ļø Skipped" + echo "" + fi + sleep 1 +done + +echo "Merged $merged_count PR(s)" +echo "" + +echo "šŸ“‹ STEP 5: ASSIGNING REVIEWERS TO PENDING PRS" +echo "==============================================" +echo "" + +for pr_num in "${needs_review[@]}"; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + + echo "PR #$pr_num: $title by @$author" + + # Assign reviewers + if [ "$author" != "dhvil" ] && [ "$author" != "mikejmorgan-ai" ]; then + gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || true + echo " āœ… Assigned reviewers: dhvil, mikejmorgan-ai" + else + gh pr edit $pr_num --add-reviewer mikejmorgan-ai 2>/dev/null || true + echo " āœ… Assigned reviewer: mikejmorgan-ai" + fi + + # Post welcome comment + welcome_comment="Thanks @$author for this contribution! šŸŽ‰ + +**Review Process:** +1. āœ… Reviewers assigned - expect feedback within 24-48 hours +2. šŸ’¬ **Join Discord**: $DISCORD_INVITE +3. šŸ’° **Bounty Payment**: Crypto (BTC/USDC) via Discord after merge + +**Important:** +- All bounties tracked and paid through Discord +- Please join to coordinate payment details +- Typical merge → payment time: 24-48 hours + +Looking forward to reviewing this! šŸš€" + + # Check if we already commented + existing=$(gh pr view $pr_num --json comments --jq '[.comments[] | select(.author.login == "mikejmorgan-ai")] | length') + if [ "$existing" -eq 0 ]; then + gh pr comment $pr_num --body "$welcome_comment" + echo " āœ… Posted welcome comment" + else + echo " (Welcome comment already exists)" + fi + + echo "" + sleep 1 +done + +echo "" +echo "ā° STEP 6: SENDING STALE PR REMINDERS" +echo "=====================================" +echo "" + +for pr_num in "${stale_prs[@]}"; do + # Skip if it's in draft or critical (already handled) + if [[ " ${draft_prs[@]} " =~ " ${pr_num} " ]] || [[ " ${critical_prs[@]} " =~ " ${pr_num} " ]]; then + continue + fi + + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + created=$(echo "$pr_data" | jq -r '.createdAt') + + created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) + now_ts=$(date +%s) + age_days=$(( (now_ts - created_ts) / 86400 )) + + echo "PR #$pr_num: $title by @$author ($age_days days old)" + + stale_comment="Hi @$author! šŸ‘‹ + +This PR has been open for $age_days days. Quick status check: + +šŸ“‹ **Checklist:** +- [ ] Joined Discord? ($DISCORD_INVITE) +- [ ] All tests passing? +- [ ] Addressed review feedback? + +šŸ’° **Payment Reminder:** +- Bounties paid via crypto (Bitcoin/USDC) +- Processed through Discord DMs +- Sent within 24 hours of merge + +Need help? Let us know in Discord! We want to get this merged and pay you ASAP. šŸš€" + + gh pr comment $pr_num --body "$stale_comment" + echo " āœ… Sent reminder" + echo "" + sleep 1 +done + +echo "" +echo "šŸ’¬ STEP 7: GENERATING DISCORD ANNOUNCEMENT" +echo "==========================================" +echo "" + +cat << DISCORD_EOF > /tmp/discord_announcement.txt +šŸš€ **PR STATUS UPDATE - $(date +"%B %d, %Y")** + +Just completed automated PR processing! Here's where we stand: + +**šŸ“Š Statistics:** +- Total Open PRs: $total_prs +- šŸ”„ Critical (Package Manager): ${#critical_prs[@]} +- āœ… Merged Today: $merged_count +- šŸ“‹ Under Review: ${#needs_review[@]} +- ā° Stale Reminders Sent: ${#stale_prs[@]} + +**šŸŽÆ Focus Areas:** +DISCORD_EOF + +if [ ${#critical_prs[@]} -gt 0 ]; then + echo "• šŸ”„ PR #${critical_prs[0]} (Package Manager) - CRITICAL PATH - Under urgent review" >> /tmp/discord_announcement.txt +fi + +cat << DISCORD_EOF2 >> /tmp/discord_announcement.txt + +**šŸ’° Payment Process:** +1. PR gets merged āœ… +2. I DM you for wallet address šŸ’¬ +3. Crypto sent within 24 hours šŸ’ø +4. You confirm receipt āœ… + +**All contributors:** Join Discord for bounty coordination! +šŸ‘‰ $DISCORD_INVITE + +Let's keep the momentum going! šŸ”„ + +- Mike +DISCORD_EOF2 + +echo "Discord announcement generated:" +echo "===============================" +cat /tmp/discord_announcement.txt +echo "===============================" +echo "" +echo "šŸ“‹ Copy the above to Discord #announcements" +echo "" + +echo "" +echo "šŸ“Š STEP 8: PAYMENT TRACKING SUMMARY" +echo "===================================" +echo "" + +if [ -f "$BOUNTY_CSV" ]; then + echo "Payments Pending:" + tail -n +2 "$BOUNTY_CSV" | grep "Pending" 2>/dev/null | while IFS=, read -r pr author amount status payment date; do + echo " PR #$pr - @$author - \$$amount - $date" + done || echo " No pending payments" + echo "" + echo "Full tracking: $BOUNTY_CSV" +fi + +echo "" +echo "šŸ“§ STEP 9: CONTRIBUTOR DM TEMPLATES" +echo "===================================" +echo "" + +# Generate DM templates for unique contributors +contributors=$(echo "$prs" | jq -r '.[].author.login' | sort -u) + +echo "Send these DMs on Discord:" +echo "" + +for contributor in $contributors; do + pr_count=$(echo "$prs" | jq -r --arg author "$contributor" '[.[] | select(.author.login == $author)] | length') + + if [ "$pr_count" -gt 0 ]; then + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "To: @$contributor ($pr_count open PR)" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + cat << DM_EOF + +Hey! Just processed your Cortex PR(s) - great work! šŸŽ‰ + +**Quick Check:** +1. Have you joined Discord? ($DISCORD_INVITE) +2. What's your crypto wallet address? (BTC or USDC) +3. Any blockers I can help with? + +**Payment Timeline:** +- PR review: 24-48 hours +- Merge decision: Clear feedback either way +- Payment: Within 24 hours of merge + +Looking forward to merging your work! + +- Mike + +DM_EOF + fi +done + +echo "" +echo "==============================================" +echo "āœ… MASTER PR CONTROL COMPLETE" +echo "==============================================" +echo "" + +echo "šŸ“Š Summary of Actions:" +echo " • Reviewed $total_prs PRs" +echo " • Assigned reviewers to ${#needs_review[@]} PRs" +echo " • Merged $merged_count PRs" +echo " • Flagged ${#critical_prs[@]} critical PR(s)" +echo " • Sent ${#stale_prs[@]} stale reminders" +echo "" + +echo "šŸ“‹ Next Manual Steps:" +echo " 1. Copy Discord announcement to #announcements" +echo " 2. Send DMs to contributors (templates above)" +echo " 3. Review critical PR #${critical_prs[0]:-N/A} urgently" +echo " 4. Process $merged_count payment(s) via crypto" +echo "" + +echo "šŸ”„ Run this script daily to maintain PR velocity!" +echo "" +echo "āœ… All done!" diff --git a/scripts/cortex-cleanup.sh b/scripts/cortex-cleanup.sh old mode 100755 new mode 100644 index 0b1972f9..df4b631b --- a/scripts/cortex-cleanup.sh +++ b/scripts/cortex-cleanup.sh @@ -1,147 +1,147 @@ -#!/bin/bash -# Cortex Linux - Repo Cleanup Script -# Run this once to organize the repo for public launch -# Usage: cd ~/cortex && bash cortex-cleanup.sh - -set -e - -echo "🧹 CORTEX LINUX REPO CLEANUP" -echo "============================" -echo "" - -cd ~/cortex || { echo "āŒ ~/cortex not found"; exit 1; } - -# Confirm we're in the right place -if [ ! -f "README.md" ] || [ ! -d ".git" ]; then - echo "āŒ Not in cortex repo root. Run from ~/cortex" - exit 1 -fi - -echo "šŸ“ Current root files: $(ls *.py *.sh *.json *.csv *.md 2>/dev/null | wc -l | tr -d ' ')" -echo "" - -# Step 1: Create directories if they don't exist -echo "1ļøāƒ£ Creating directory structure..." -mkdir -p cortex/modules -mkdir -p tests -mkdir -p scripts -mkdir -p docs -mkdir -p internal - -# Step 2: Move Python modules into cortex/ -echo "2ļøāƒ£ Moving Python modules to cortex/..." -for file in context_memory.py dependency_resolver.py error_parser.py \ - installation_history.py installation_verifier.py llm_router.py \ - logging_system.py; do - if [ -f "$file" ]; then - mv "$file" cortex/ 2>/dev/null && echo " āœ“ $file → cortex/" - fi -done - -# Step 3: Move test files into tests/ -echo "3ļøāƒ£ Moving test files to tests/..." -for file in test_*.py; do - if [ -f "$file" ]; then - mv "$file" tests/ 2>/dev/null && echo " āœ“ $file → tests/" - fi -done - -# Step 4: Move shell scripts into scripts/ -echo "4ļøāƒ£ Moving shell scripts to scripts/..." -for file in *.sh; do - # Keep this cleanup script in root temporarily - if [ "$file" != "cortex-cleanup.sh" ] && [ -f "$file" ]; then - mv "$file" scripts/ 2>/dev/null && echo " āœ“ $file → scripts/" - fi -done - -# Step 5: Move markdown docs to docs/ (except key root files) -echo "5ļøāƒ£ Moving documentation to docs/..." -for file in *.md; do - case "$file" in - README.md|CHANGELOG.md|LICENSE|Contributing.md) - echo " ⊘ $file (keeping in root)" - ;; - *) - if [ -f "$file" ]; then - mv "$file" docs/ 2>/dev/null && echo " āœ“ $file → docs/" - fi - ;; - esac -done - -# Step 6: Move internal/admin files and gitignore them -echo "6ļøāƒ£ Moving internal files to internal/..." -for file in bounties_owed.csv bounties_pending.json contributors.json \ - issue_status.json payments_history.json pr_status.json; do - if [ -f "$file" ]; then - mv "$file" internal/ 2>/dev/null && echo " āœ“ $file → internal/" - fi -done - -# Step 7: Delete duplicate/junk files -echo "7ļøāƒ£ Removing duplicate files..." -rm -f "README_DEPENDENCIES (1).md" 2>/dev/null && echo " āœ“ Removed README_DEPENDENCIES (1).md" -rm -f "deploy_jesse_system (1).sh" 2>/dev/null && echo " āœ“ Removed deploy_jesse_system (1).sh" - -# Step 8: Update .gitignore -echo "8ļøāƒ£ Updating .gitignore..." -if ! grep -q "internal/" .gitignore 2>/dev/null; then - echo "" >> .gitignore - echo "# Internal admin files (bounties, payments, etc.)" >> .gitignore - echo "internal/" >> .gitignore - echo " āœ“ Added internal/ to .gitignore" -else - echo " ⊘ internal/ already in .gitignore" -fi - -# Step 9: Create __init__.py files if missing -echo "9ļøāƒ£ Ensuring Python packages are importable..." -touch cortex/__init__.py 2>/dev/null -touch tests/__init__.py 2>/dev/null -echo " āœ“ __init__.py files created" - -# Step 10: Show results -echo "" -echo "šŸ“Š CLEANUP COMPLETE" -echo "===================" -echo "Root files now: $(ls *.py *.sh *.json *.csv 2>/dev/null | wc -l | tr -d ' ') (should be ~0)" -echo "" -echo "Directory structure:" -echo " cortex/ - $(ls cortex/*.py 2>/dev/null | wc -l | tr -d ' ') Python modules" -echo " tests/ - $(ls tests/*.py 2>/dev/null | wc -l | tr -d ' ') test files" -echo " scripts/ - $(ls scripts/*.sh 2>/dev/null | wc -l | tr -d ' ') shell scripts" -echo " docs/ - $(ls docs/*.md 2>/dev/null | wc -l | tr -d ' ') markdown files" -echo " internal/ - $(ls internal/ 2>/dev/null | wc -l | tr -d ' ') admin files (gitignored)" -echo "" - -# Step 11: Git commit -echo "šŸ”Ÿ Committing changes..." -git add -A -git status --short -echo "" -read -p "Commit and push these changes? (y/n): " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - git commit -m "Reorganize repo structure for public launch - -- Move Python modules to cortex/ -- Move tests to tests/ -- Move scripts to scripts/ -- Move docs to docs/ -- Move internal admin files to internal/ (gitignored) -- Remove duplicate files -- Clean root directory for professional appearance" - - git push origin main - echo "" - echo "āœ… DONE! Repo is now clean and pushed." -else - echo "" - echo "āš ļø Changes staged but NOT committed. Run 'git commit' when ready." -fi - -echo "" -echo "🧪 NEXT STEP: Test the CLI" -echo " cd ~/cortex && source venv/bin/activate && cortex install nginx --dry-run" -echo "" +#!/bin/bash +# Cortex Linux - Repo Cleanup Script +# Run this once to organize the repo for public launch +# Usage: cd ~/cortex && bash cortex-cleanup.sh + +set -e + +echo "🧹 CORTEX LINUX REPO CLEANUP" +echo "============================" +echo "" + +cd ~/cortex || { echo "āŒ ~/cortex not found"; exit 1; } + +# Confirm we're in the right place +if [ ! -f "README.md" ] || [ ! -d ".git" ]; then + echo "āŒ Not in cortex repo root. Run from ~/cortex" + exit 1 +fi + +echo "šŸ“ Current root files: $(ls *.py *.sh *.json *.csv *.md 2>/dev/null | wc -l | tr -d ' ')" +echo "" + +# Step 1: Create directories if they don't exist +echo "1ļøāƒ£ Creating directory structure..." +mkdir -p cortex/modules +mkdir -p tests +mkdir -p scripts +mkdir -p docs +mkdir -p internal + +# Step 2: Move Python modules into cortex/ +echo "2ļøāƒ£ Moving Python modules to cortex/..." +for file in context_memory.py dependency_resolver.py error_parser.py \ + installation_history.py installation_verifier.py llm_router.py \ + logging_system.py; do + if [ -f "$file" ]; then + mv "$file" cortex/ 2>/dev/null && echo " āœ“ $file → cortex/" + fi +done + +# Step 3: Move test files into tests/ +echo "3ļøāƒ£ Moving test files to tests/..." +for file in test_*.py; do + if [ -f "$file" ]; then + mv "$file" tests/ 2>/dev/null && echo " āœ“ $file → tests/" + fi +done + +# Step 4: Move shell scripts into scripts/ +echo "4ļøāƒ£ Moving shell scripts to scripts/..." +for file in *.sh; do + # Keep this cleanup script in root temporarily + if [ "$file" != "cortex-cleanup.sh" ] && [ -f "$file" ]; then + mv "$file" scripts/ 2>/dev/null && echo " āœ“ $file → scripts/" + fi +done + +# Step 5: Move markdown docs to docs/ (except key root files) +echo "5ļøāƒ£ Moving documentation to docs/..." +for file in *.md; do + case "$file" in + README.md|CHANGELOG.md|LICENSE|Contributing.md) + echo " ⊘ $file (keeping in root)" + ;; + *) + if [ -f "$file" ]; then + mv "$file" docs/ 2>/dev/null && echo " āœ“ $file → docs/" + fi + ;; + esac +done + +# Step 6: Move internal/admin files and gitignore them +echo "6ļøāƒ£ Moving internal files to internal/..." +for file in bounties_owed.csv bounties_pending.json contributors.json \ + issue_status.json payments_history.json pr_status.json; do + if [ -f "$file" ]; then + mv "$file" internal/ 2>/dev/null && echo " āœ“ $file → internal/" + fi +done + +# Step 7: Delete duplicate/junk files +echo "7ļøāƒ£ Removing duplicate files..." +rm -f "README_DEPENDENCIES (1).md" 2>/dev/null && echo " āœ“ Removed README_DEPENDENCIES (1).md" +rm -f "deploy_jesse_system (1).sh" 2>/dev/null && echo " āœ“ Removed deploy_jesse_system (1).sh" + +# Step 8: Update .gitignore +echo "8ļøāƒ£ Updating .gitignore..." +if ! grep -q "internal/" .gitignore 2>/dev/null; then + echo "" >> .gitignore + echo "# Internal admin files (bounties, payments, etc.)" >> .gitignore + echo "internal/" >> .gitignore + echo " āœ“ Added internal/ to .gitignore" +else + echo " ⊘ internal/ already in .gitignore" +fi + +# Step 9: Create __init__.py files if missing +echo "9ļøāƒ£ Ensuring Python packages are importable..." +touch cortex/__init__.py 2>/dev/null +touch tests/__init__.py 2>/dev/null +echo " āœ“ __init__.py files created" + +# Step 10: Show results +echo "" +echo "šŸ“Š CLEANUP COMPLETE" +echo "===================" +echo "Root files now: $(ls *.py *.sh *.json *.csv 2>/dev/null | wc -l | tr -d ' ') (should be ~0)" +echo "" +echo "Directory structure:" +echo " cortex/ - $(ls cortex/*.py 2>/dev/null | wc -l | tr -d ' ') Python modules" +echo " tests/ - $(ls tests/*.py 2>/dev/null | wc -l | tr -d ' ') test files" +echo " scripts/ - $(ls scripts/*.sh 2>/dev/null | wc -l | tr -d ' ') shell scripts" +echo " docs/ - $(ls docs/*.md 2>/dev/null | wc -l | tr -d ' ') markdown files" +echo " internal/ - $(ls internal/ 2>/dev/null | wc -l | tr -d ' ') admin files (gitignored)" +echo "" + +# Step 11: Git commit +echo "šŸ”Ÿ Committing changes..." +git add -A +git status --short +echo "" +read -p "Commit and push these changes? (y/n): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + git commit -m "Reorganize repo structure for public launch + +- Move Python modules to cortex/ +- Move tests to tests/ +- Move scripts to scripts/ +- Move docs to docs/ +- Move internal admin files to internal/ (gitignored) +- Remove duplicate files +- Clean root directory for professional appearance" + + git push origin main + echo "" + echo "āœ… DONE! Repo is now clean and pushed." +else + echo "" + echo "āš ļø Changes staged but NOT committed. Run 'git commit' when ready." +fi + +echo "" +echo "🧪 NEXT STEP: Test the CLI" +echo " cd ~/cortex && source venv/bin/activate && cortex install nginx --dry-run" +echo "" diff --git a/scripts/demo_script.sh b/scripts/demo_script.sh old mode 100755 new mode 100644 index 3fadde0d..599f310d --- a/scripts/demo_script.sh +++ b/scripts/demo_script.sh @@ -1,230 +1,230 @@ -#!/bin/bash -# Sandbox Executor - Video Demonstration Script -# Run commands in this order to showcase the implementation - -clear -echo "============================================================" -echo " CORTEX LINUX - SANDBOXED COMMAND EXECUTOR DEMONSTRATION" -echo "============================================================" -sleep 2 - -echo "" -echo "1. CHECKING SYSTEM STATUS" -echo "============================================================" -cd /home/dhaval/projects/open-source/cortex/src -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -print(f'Firejail Available: {e.is_firejail_available()}') -print(f'Firejail Path: {e.firejail_path}') -print(f'Resource Limits: CPU={e.max_cpu_cores}, Memory={e.max_memory_mb}MB, Timeout={e.timeout_seconds}s') -" -sleep 2 - -echo "" -echo "2. BASIC FUNCTIONALITY - EXECUTING SAFE COMMAND" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -result = e.execute('echo \"Hello from Cortex Sandbox!\"') -print(f'Command: echo \"Hello from Cortex Sandbox!\"') -print(f'Exit Code: {result.exit_code}') -print(f'Output: {result.stdout.strip()}') -print(f'Status: SUCCESS āœ“') -" -sleep 2 - -echo "" -echo "3. SECURITY - BLOCKING DANGEROUS COMMANDS" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor, CommandBlocked - -e = SandboxExecutor() -dangerous = [ - 'rm -rf /', - 'dd if=/dev/zero of=/dev/sda', - 'mkfs.ext4 /dev/sda1' -] - -for cmd in dangerous: - try: - e.execute(cmd) - print(f'āœ— {cmd}: ALLOWED (ERROR!)') - except CommandBlocked as err: - print(f'āœ“ {cmd}: BLOCKED - {str(err)[:50]}') -" -sleep 2 - -echo "" -echo "4. WHITELIST VALIDATION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() - -print('Allowed Commands:') -allowed = ['echo test', 'python3 --version', 'git --version'] -for cmd in allowed: - is_valid, _ = e.validate_command(cmd) - print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') - -print('\nBlocked Commands:') -blocked = ['nc -l 1234', 'nmap localhost', 'bash -c evil'] -for cmd in blocked: - is_valid, reason = e.validate_command(cmd) - print(f' āœ“ {cmd}: BLOCKED - {reason[:40]}' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') -" -sleep 2 - -echo "" -echo "5. DRY-RUN MODE - PREVIEW WITHOUT EXECUTION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -result = e.execute('apt-get update', dry_run=True) -print('Command: apt-get update') -print('Mode: DRY-RUN (no actual execution)') -print(f'Preview: {result.preview}') -print('āœ“ Safe preview generated') -" -sleep 2 - -echo "" -echo "6. FIREJAIL INTEGRATION - FULL SANDBOX ISOLATION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -cmd = e._create_firejail_command('echo test') -print('Firejail Command Structure:') -print(' '.join(cmd[:8]) + ' ...') -print('\nSecurity Features:') -features = { - 'Private namespace': '--private', - 'CPU limits': '--cpu=', - 'Memory limits': '--rlimit-as', - 'Network disabled': '--net=none', - 'No root': '--noroot', - 'Capabilities dropped': '--caps.drop=all', - 'Seccomp enabled': '--seccomp' -} -cmd_str = ' '.join(cmd) -for name, flag in features.items(): - print(f' āœ“ {name}' if flag in cmd_str else f' āœ— {name}') -" -sleep 2 - -echo "" -echo "7. SUDO RESTRICTIONS - PACKAGE INSTALLATION ONLY" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() - -print('Allowed Sudo Commands:') -allowed_sudo = ['sudo apt-get install python3', 'sudo pip install numpy'] -for cmd in allowed_sudo: - is_valid, _ = e.validate_command(cmd) - print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') - -print('\nBlocked Sudo Commands:') -blocked_sudo = ['sudo rm -rf /', 'sudo chmod 777 /'] -for cmd in blocked_sudo: - is_valid, reason = e.validate_command(cmd) - print(f' āœ“ {cmd}: BLOCKED' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') -" -sleep 2 - -echo "" -echo "8. RESOURCE LIMITS ENFORCEMENT" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -print(f'CPU Limit: {e.max_cpu_cores} cores') -print(f'Memory Limit: {e.max_memory_mb} MB') -print(f'Disk Limit: {e.max_disk_mb} MB') -print(f'Timeout: {e.timeout_seconds} seconds (5 minutes)') -print('āœ“ All resource limits configured and enforced') -" -sleep 2 - -echo "" -echo "9. COMPREHENSIVE LOGGING - AUDIT TRAIL" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -e.execute('echo test1', dry_run=True) -e.execute('echo test2', dry_run=True) -audit = e.get_audit_log() -print(f'Total Log Entries: {len(audit)}') -print('\nRecent Entries:') -for entry in audit[-3:]: - print(f' - [{entry[\"type\"]}] {entry[\"command\"][:50]}') - print(f' Timestamp: {entry[\"timestamp\"]}') -print('āœ“ Complete audit trail maintained') -" -sleep 2 - -echo "" -echo "10. REAL-WORLD SCENARIO - PYTHON SCRIPT EXECUTION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -result = e.execute('python3 -c \"print(\\\"Hello from Python in sandbox!\\\")\"') -print('Command: python3 script execution') -print(f'Exit Code: {result.exit_code}') -print(f'Output: {result.stdout.strip() if result.stdout else \"(no output)\"}') -print(f'Status: {\"SUCCESS āœ“\" if result.success else \"FAILED\"}') -print('āœ“ Script executed safely in sandbox') -" -sleep 2 - -echo "" -echo "11. ROLLBACK CAPABILITY" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -snapshot = e._create_snapshot('demo_session') -print(f'Snapshot Created: {\"demo_session\" in e.rollback_snapshots}') -print(f'Rollback Enabled: {e.enable_rollback}') -print('āœ“ Rollback mechanism ready') -" -sleep 2 - -echo "" -echo "12. FINAL VERIFICATION - ALL REQUIREMENTS MET" -echo "============================================================" -python3 -c " -print('Requirements Checklist:') -print(' āœ“ Firejail/Containerization: IMPLEMENTED') -print(' āœ“ Whitelist of commands: WORKING') -print(' āœ“ Resource limits: CONFIGURED') -print(' āœ“ Dry-run mode: FUNCTIONAL') -print(' āœ“ Rollback capability: READY') -print(' āœ“ Comprehensive logging: ACTIVE') -print(' āœ“ Security blocking: ENFORCED') -print(' āœ“ Sudo restrictions: ACTIVE') -print(' āœ“ Timeout protection: 5 MINUTES') -print(' āœ“ Path validation: WORKING') -" -sleep 2 - -echo "" -echo "============================================================" -echo " DEMONSTRATION COMPLETE - ALL FEATURES VERIFIED āœ“" -echo "============================================================" -echo "" -echo "Summary:" -echo " - 20/20 Unit Tests: PASSING" -echo " - All Requirements: MET" -echo " - Security Features: ACTIVE" -echo " - Production Ready: YES" -echo "" - +#!/bin/bash +# Sandbox Executor - Video Demonstration Script +# Run commands in this order to showcase the implementation + +clear +echo "============================================================" +echo " CORTEX LINUX - SANDBOXED COMMAND EXECUTOR DEMONSTRATION" +echo "============================================================" +sleep 2 + +echo "" +echo "1. CHECKING SYSTEM STATUS" +echo "============================================================" +cd /home/dhaval/projects/open-source/cortex/src +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +print(f'Firejail Available: {e.is_firejail_available()}') +print(f'Firejail Path: {e.firejail_path}') +print(f'Resource Limits: CPU={e.max_cpu_cores}, Memory={e.max_memory_mb}MB, Timeout={e.timeout_seconds}s') +" +sleep 2 + +echo "" +echo "2. BASIC FUNCTIONALITY - EXECUTING SAFE COMMAND" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +result = e.execute('echo \"Hello from Cortex Sandbox!\"') +print(f'Command: echo \"Hello from Cortex Sandbox!\"') +print(f'Exit Code: {result.exit_code}') +print(f'Output: {result.stdout.strip()}') +print(f'Status: SUCCESS āœ“') +" +sleep 2 + +echo "" +echo "3. SECURITY - BLOCKING DANGEROUS COMMANDS" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor, CommandBlocked + +e = SandboxExecutor() +dangerous = [ + 'rm -rf /', + 'dd if=/dev/zero of=/dev/sda', + 'mkfs.ext4 /dev/sda1' +] + +for cmd in dangerous: + try: + e.execute(cmd) + print(f'āœ— {cmd}: ALLOWED (ERROR!)') + except CommandBlocked as err: + print(f'āœ“ {cmd}: BLOCKED - {str(err)[:50]}') +" +sleep 2 + +echo "" +echo "4. WHITELIST VALIDATION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() + +print('Allowed Commands:') +allowed = ['echo test', 'python3 --version', 'git --version'] +for cmd in allowed: + is_valid, _ = e.validate_command(cmd) + print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') + +print('\nBlocked Commands:') +blocked = ['nc -l 1234', 'nmap localhost', 'bash -c evil'] +for cmd in blocked: + is_valid, reason = e.validate_command(cmd) + print(f' āœ“ {cmd}: BLOCKED - {reason[:40]}' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') +" +sleep 2 + +echo "" +echo "5. DRY-RUN MODE - PREVIEW WITHOUT EXECUTION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +result = e.execute('apt-get update', dry_run=True) +print('Command: apt-get update') +print('Mode: DRY-RUN (no actual execution)') +print(f'Preview: {result.preview}') +print('āœ“ Safe preview generated') +" +sleep 2 + +echo "" +echo "6. FIREJAIL INTEGRATION - FULL SANDBOX ISOLATION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +cmd = e._create_firejail_command('echo test') +print('Firejail Command Structure:') +print(' '.join(cmd[:8]) + ' ...') +print('\nSecurity Features:') +features = { + 'Private namespace': '--private', + 'CPU limits': '--cpu=', + 'Memory limits': '--rlimit-as', + 'Network disabled': '--net=none', + 'No root': '--noroot', + 'Capabilities dropped': '--caps.drop=all', + 'Seccomp enabled': '--seccomp' +} +cmd_str = ' '.join(cmd) +for name, flag in features.items(): + print(f' āœ“ {name}' if flag in cmd_str else f' āœ— {name}') +" +sleep 2 + +echo "" +echo "7. SUDO RESTRICTIONS - PACKAGE INSTALLATION ONLY" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() + +print('Allowed Sudo Commands:') +allowed_sudo = ['sudo apt-get install python3', 'sudo pip install numpy'] +for cmd in allowed_sudo: + is_valid, _ = e.validate_command(cmd) + print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') + +print('\nBlocked Sudo Commands:') +blocked_sudo = ['sudo rm -rf /', 'sudo chmod 777 /'] +for cmd in blocked_sudo: + is_valid, reason = e.validate_command(cmd) + print(f' āœ“ {cmd}: BLOCKED' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') +" +sleep 2 + +echo "" +echo "8. RESOURCE LIMITS ENFORCEMENT" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +print(f'CPU Limit: {e.max_cpu_cores} cores') +print(f'Memory Limit: {e.max_memory_mb} MB') +print(f'Disk Limit: {e.max_disk_mb} MB') +print(f'Timeout: {e.timeout_seconds} seconds (5 minutes)') +print('āœ“ All resource limits configured and enforced') +" +sleep 2 + +echo "" +echo "9. COMPREHENSIVE LOGGING - AUDIT TRAIL" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +e.execute('echo test1', dry_run=True) +e.execute('echo test2', dry_run=True) +audit = e.get_audit_log() +print(f'Total Log Entries: {len(audit)}') +print('\nRecent Entries:') +for entry in audit[-3:]: + print(f' - [{entry[\"type\"]}] {entry[\"command\"][:50]}') + print(f' Timestamp: {entry[\"timestamp\"]}') +print('āœ“ Complete audit trail maintained') +" +sleep 2 + +echo "" +echo "10. REAL-WORLD SCENARIO - PYTHON SCRIPT EXECUTION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +result = e.execute('python3 -c \"print(\\\"Hello from Python in sandbox!\\\")\"') +print('Command: python3 script execution') +print(f'Exit Code: {result.exit_code}') +print(f'Output: {result.stdout.strip() if result.stdout else \"(no output)\"}') +print(f'Status: {\"SUCCESS āœ“\" if result.success else \"FAILED\"}') +print('āœ“ Script executed safely in sandbox') +" +sleep 2 + +echo "" +echo "11. ROLLBACK CAPABILITY" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +snapshot = e._create_snapshot('demo_session') +print(f'Snapshot Created: {\"demo_session\" in e.rollback_snapshots}') +print(f'Rollback Enabled: {e.enable_rollback}') +print('āœ“ Rollback mechanism ready') +" +sleep 2 + +echo "" +echo "12. FINAL VERIFICATION - ALL REQUIREMENTS MET" +echo "============================================================" +python3 -c " +print('Requirements Checklist:') +print(' āœ“ Firejail/Containerization: IMPLEMENTED') +print(' āœ“ Whitelist of commands: WORKING') +print(' āœ“ Resource limits: CONFIGURED') +print(' āœ“ Dry-run mode: FUNCTIONAL') +print(' āœ“ Rollback capability: READY') +print(' āœ“ Comprehensive logging: ACTIVE') +print(' āœ“ Security blocking: ENFORCED') +print(' āœ“ Sudo restrictions: ACTIVE') +print(' āœ“ Timeout protection: 5 MINUTES') +print(' āœ“ Path validation: WORKING') +" +sleep 2 + +echo "" +echo "============================================================" +echo " DEMONSTRATION COMPLETE - ALL FEATURES VERIFIED āœ“" +echo "============================================================" +echo "" +echo "Summary:" +echo " - 20/20 Unit Tests: PASSING" +echo " - All Requirements: MET" +echo " - Security Features: ACTIVE" +echo " - Production Ready: YES" +echo "" + diff --git a/scripts/deployment/audit_cortex_status.sh b/scripts/deployment/audit_cortex_status.sh old mode 100755 new mode 100644 index eca4b113..c53b90c6 --- a/scripts/deployment/audit_cortex_status.sh +++ b/scripts/deployment/audit_cortex_status.sh @@ -1,108 +1,108 @@ -#!/bin/bash -# Cortex Linux - Complete System Audit -# Run this once to give Claude full visibility - -echo "šŸ” CORTEX LINUX - SYSTEM AUDIT" -echo "========================================" -echo "" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -cd ~/cortex 2>/dev/null || { echo "āŒ ~/cortex not found. Run: cd ~ && git clone https://github.com/cortexlinux/cortex.git"; exit 1; } - -echo "šŸ“ REPOSITORY STRUCTURE" -echo "========================================" -echo "Files in repo:" -find . -type f -not -path '*/\.*' | head -30 -echo "" - -echo "šŸ¤– GITHUB ACTIONS WORKFLOWS" -echo "========================================" -if [ -d ".github/workflows" ]; then - echo "āœ… Workflows directory exists" - ls -lh .github/workflows/ - echo "" - echo "šŸ“„ Workflow file contents:" - for file in .github/workflows/*.yml; do - echo "--- $file ---" - head -50 "$file" - echo "" - done -else - echo "āŒ No .github/workflows directory" -fi -echo "" - -echo "šŸ“Š AUTOMATION DATA FILES" -echo "========================================" -for file in bounties_pending.json payments_history.json contributors.json; do - if [ -f "$file" ]; then - echo "āœ… $file exists" - cat "$file" - else - echo "āŒ $file missing" - fi - echo "" -done - -echo "šŸ” GITHUB SECRETS STATUS" -echo "========================================" -echo "Checking if secrets are configured..." -gh secret list 2>/dev/null || echo "āš ļø gh CLI not authenticated or not installed" -echo "" - -echo "🌐 GITHUB ACTIONS RUNS" -echo "========================================" -echo "Recent workflow runs:" -gh run list --limit 5 2>/dev/null || echo "āš ļø gh CLI not authenticated" -echo "" - -echo "šŸ“‹ RECENT COMMITS" -echo "========================================" -git log --oneline -10 -echo "" - -echo "šŸ”€ BRANCHES" -echo "========================================" -git branch -a -echo "" - -echo "šŸ“ CURRENT STATUS" -echo "========================================" -echo "Current branch: $(git branch --show-current)" -echo "Remote URL: $(git remote get-url origin)" -echo "Git status:" -git status --short -echo "" - -echo "šŸ’¬ DISCORD WEBHOOK CHECK" -echo "========================================" -if gh secret list 2>/dev/null | grep -q "DISCORD_WEBHOOK"; then - echo "āœ… DISCORD_WEBHOOK secret is configured" -else - echo "āŒ DISCORD_WEBHOOK secret not found" - echo " Add it at: https://github.com/cortexlinux/cortex/settings/secrets/actions" -fi -echo "" - -echo "šŸŽÆ ISSUES & PRS" -echo "========================================" -echo "Open issues with bounties:" -gh issue list --label "bounty" --limit 10 2>/dev/null || echo "āš ļø gh CLI issue" -echo "" -echo "Recent PRs:" -gh pr list --limit 5 2>/dev/null || echo "āš ļø gh CLI issue" -echo "" - -echo "āœ… AUDIT COMPLETE" -echo "========================================" -echo "Save this output and share with Claude for full visibility" -echo "" -echo "Next steps:" -echo "1. Share this output with Claude" -echo "2. Claude can now see everything without asking" -echo "3. No more copy/paste needed" +#!/bin/bash +# Cortex Linux - Complete System Audit +# Run this once to give Claude full visibility + +echo "šŸ” CORTEX LINUX - SYSTEM AUDIT" +echo "========================================" +echo "" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +cd ~/cortex 2>/dev/null || { echo "āŒ ~/cortex not found. Run: cd ~ && git clone https://github.com/cortexlinux/cortex.git"; exit 1; } + +echo "šŸ“ REPOSITORY STRUCTURE" +echo "========================================" +echo "Files in repo:" +find . -type f -not -path '*/\.*' | head -30 +echo "" + +echo "šŸ¤– GITHUB ACTIONS WORKFLOWS" +echo "========================================" +if [ -d ".github/workflows" ]; then + echo "āœ… Workflows directory exists" + ls -lh .github/workflows/ + echo "" + echo "šŸ“„ Workflow file contents:" + for file in .github/workflows/*.yml; do + echo "--- $file ---" + head -50 "$file" + echo "" + done +else + echo "āŒ No .github/workflows directory" +fi +echo "" + +echo "šŸ“Š AUTOMATION DATA FILES" +echo "========================================" +for file in bounties_pending.json payments_history.json contributors.json; do + if [ -f "$file" ]; then + echo "āœ… $file exists" + cat "$file" + else + echo "āŒ $file missing" + fi + echo "" +done + +echo "šŸ” GITHUB SECRETS STATUS" +echo "========================================" +echo "Checking if secrets are configured..." +gh secret list 2>/dev/null || echo "āš ļø gh CLI not authenticated or not installed" +echo "" + +echo "🌐 GITHUB ACTIONS RUNS" +echo "========================================" +echo "Recent workflow runs:" +gh run list --limit 5 2>/dev/null || echo "āš ļø gh CLI not authenticated" +echo "" + +echo "šŸ“‹ RECENT COMMITS" +echo "========================================" +git log --oneline -10 +echo "" + +echo "šŸ”€ BRANCHES" +echo "========================================" +git branch -a +echo "" + +echo "šŸ“ CURRENT STATUS" +echo "========================================" +echo "Current branch: $(git branch --show-current)" +echo "Remote URL: $(git remote get-url origin)" +echo "Git status:" +git status --short +echo "" + +echo "šŸ’¬ DISCORD WEBHOOK CHECK" +echo "========================================" +if gh secret list 2>/dev/null | grep -q "DISCORD_WEBHOOK"; then + echo "āœ… DISCORD_WEBHOOK secret is configured" +else + echo "āŒ DISCORD_WEBHOOK secret not found" + echo " Add it at: https://github.com/cortexlinux/cortex/settings/secrets/actions" +fi +echo "" + +echo "šŸŽÆ ISSUES & PRS" +echo "========================================" +echo "Open issues with bounties:" +gh issue list --label "bounty" --limit 10 2>/dev/null || echo "āš ļø gh CLI issue" +echo "" +echo "Recent PRs:" +gh pr list --limit 5 2>/dev/null || echo "āš ļø gh CLI issue" +echo "" + +echo "āœ… AUDIT COMPLETE" +echo "========================================" +echo "Save this output and share with Claude for full visibility" +echo "" +echo "Next steps:" +echo "1. Share this output with Claude" +echo "2. Claude can now see everything without asking" +echo "3. No more copy/paste needed" diff --git a/scripts/deployment/upload_issue_34.sh b/scripts/deployment/upload_issue_34.sh old mode 100755 new mode 100644 index 9441bc92..13dbf727 --- a/scripts/deployment/upload_issue_34.sh +++ b/scripts/deployment/upload_issue_34.sh @@ -1,36 +1,36 @@ -#!/bin/bash - -# Upload Issue #34 files to GitHub - -echo "šŸ” Enter your GitHub Personal Access Token:" -read -s GITHUB_TOKEN - -REPO="cortexlinux/cortex" -BRANCH="feature/issue-34" - -echo "" -echo "šŸ“¤ Uploading llm_router.py..." -curl -X PUT \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"message\":\"Add LLM Router implementation\",\"content\":\"$(base64 -i llm_router.py)\",\"branch\":\"$BRANCH\"}" \ - "https://api.github.com/repos/$REPO/contents/src/llm_router.py" - -echo "" -echo "šŸ“¤ Uploading test_llm_router.py..." -curl -X PUT \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"message\":\"Add LLM Router tests\",\"content\":\"$(base64 -i test_llm_router.py)\",\"branch\":\"$BRANCH\"}" \ - "https://api.github.com/repos/$REPO/contents/src/test_llm_router.py" - -echo "" -echo "šŸ“¤ Uploading README_LLM_ROUTER.md..." -curl -X PUT \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"message\":\"Add LLM Router documentation\",\"content\":\"$(base64 -i README_LLM_ROUTER.md)\",\"branch\":\"$BRANCH\"}" \ - "https://api.github.com/repos/$REPO/contents/docs/README_LLM_ROUTER.md" - -echo "" -echo "āœ… Upload complete! Check: https://github.com/$REPO/tree/$BRANCH" +#!/bin/bash + +# Upload Issue #34 files to GitHub + +echo "šŸ” Enter your GitHub Personal Access Token:" +read -s GITHUB_TOKEN + +REPO="cortexlinux/cortex" +BRANCH="feature/issue-34" + +echo "" +echo "šŸ“¤ Uploading llm_router.py..." +curl -X PUT \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"message\":\"Add LLM Router implementation\",\"content\":\"$(base64 -i llm_router.py)\",\"branch\":\"$BRANCH\"}" \ + "https://api.github.com/repos/$REPO/contents/src/llm_router.py" + +echo "" +echo "šŸ“¤ Uploading test_llm_router.py..." +curl -X PUT \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"message\":\"Add LLM Router tests\",\"content\":\"$(base64 -i test_llm_router.py)\",\"branch\":\"$BRANCH\"}" \ + "https://api.github.com/repos/$REPO/contents/src/test_llm_router.py" + +echo "" +echo "šŸ“¤ Uploading README_LLM_ROUTER.md..." +curl -X PUT \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"message\":\"Add LLM Router documentation\",\"content\":\"$(base64 -i README_LLM_ROUTER.md)\",\"branch\":\"$BRANCH\"}" \ + "https://api.github.com/repos/$REPO/contents/docs/README_LLM_ROUTER.md" + +echo "" +echo "āœ… Upload complete! Check: https://github.com/$REPO/tree/$BRANCH" diff --git a/scripts/fetch-fork-emails.sh b/scripts/fetch-fork-emails.sh old mode 100755 new mode 100644 index fa76ba67..49ef8e24 --- a/scripts/fetch-fork-emails.sh +++ b/scripts/fetch-fork-emails.sh @@ -1,72 +1,72 @@ -#!/bin/bash -# fetch-fork-emails.sh -# Fetches public email addresses from Cortex fork contributors -# Usage: ./fetch-fork-emails.sh - -echo "═══════════════════════════════════════════════════════════════════" -echo " CORTEX FORK CONTRIBUTOR EMAIL FETCHER" -echo " $(date '+%Y-%m-%d %H:%M:%S')" -echo "═══════════════════════════════════════════════════════════════════" -echo "" - -OUTPUT_FILE="fork-contributor-contacts.csv" -echo "username,email,name,company,location,twitter,blog,bio" > "$OUTPUT_FILE" - -# Get all fork owners -echo "šŸ“„ Fetching fork contributors..." -echo "" - -FORKS=$(curl -s "https://api.github.com/repos/cortexlinux/cortex/forks?per_page=100" | jq -r '.[].owner.login') - -for username in $FORKS; do - echo -n "→ $username: " - - # Fetch user profile - USER_DATA=$(curl -s "https://api.github.com/users/$username") - - EMAIL=$(echo "$USER_DATA" | jq -r '.email // "N/A"') - NAME=$(echo "$USER_DATA" | jq -r '.name // "N/A"') - COMPANY=$(echo "$USER_DATA" | jq -r '.company // "N/A"') - LOCATION=$(echo "$USER_DATA" | jq -r '.location // "N/A"') - TWITTER=$(echo "$USER_DATA" | jq -r '.twitter_username // "N/A"') - BLOG=$(echo "$USER_DATA" | jq -r '.blog // "N/A"') - BIO=$(echo "$USER_DATA" | jq -r '.bio // "N/A"' | tr ',' ';' | tr '\n' ' ') - - # Try to get email from recent commits if not in profile - if [ "$EMAIL" = "N/A" ] || [ "$EMAIL" = "null" ]; then - COMMIT_EMAIL=$(curl -s "https://api.github.com/users/$username/events/public" | \ - jq -r '[.[] | select(.type=="PushEvent") | .payload.commits[]?.author.email] | first // "N/A"') - if [ "$COMMIT_EMAIL" != "N/A" ] && [ "$COMMIT_EMAIL" != "null" ] && [[ ! "$COMMIT_EMAIL" =~ "noreply" ]]; then - EMAIL="$COMMIT_EMAIL" - fi - fi - - echo "$username,$EMAIL,$NAME,$COMPANY,$LOCATION,$TWITTER,$BLOG,\"$BIO\"" >> "$OUTPUT_FILE" - - if [ "$EMAIL" != "N/A" ] && [ "$EMAIL" != "null" ]; then - echo "āœ“ Found email: $EMAIL" - else - echo "ā—‹ No public email (check Twitter: $TWITTER, Blog: $BLOG)" - fi - - sleep 0.5 # Rate limiting -done - -echo "" -echo "═══════════════════════════════════════════════════════════════════" -echo " SUMMARY" -echo "═══════════════════════════════════════════════════════════════════" -echo "" -TOTAL=$(echo "$FORKS" | wc -l | tr -d ' ') -WITH_EMAIL=$(grep -v "N/A" "$OUTPUT_FILE" | grep -v "null" | grep "@" | wc -l | tr -d ' ') -echo "Total contributors: $TOTAL" -echo "With public email: $WITH_EMAIL" -echo "" -echo "āœ… Results saved to: $OUTPUT_FILE" -echo "" - -# Display results -echo "═══════════════════════════════════════════════════════════════════" -echo " CONTACT DETAILS" -echo "═══════════════════════════════════════════════════════════════════" -column -t -s',' "$OUTPUT_FILE" | head -20 +#!/bin/bash +# fetch-fork-emails.sh +# Fetches public email addresses from Cortex fork contributors +# Usage: ./fetch-fork-emails.sh + +echo "═══════════════════════════════════════════════════════════════════" +echo " CORTEX FORK CONTRIBUTOR EMAIL FETCHER" +echo " $(date '+%Y-%m-%d %H:%M:%S')" +echo "═══════════════════════════════════════════════════════════════════" +echo "" + +OUTPUT_FILE="fork-contributor-contacts.csv" +echo "username,email,name,company,location,twitter,blog,bio" > "$OUTPUT_FILE" + +# Get all fork owners +echo "šŸ“„ Fetching fork contributors..." +echo "" + +FORKS=$(curl -s "https://api.github.com/repos/cortexlinux/cortex/forks?per_page=100" | jq -r '.[].owner.login') + +for username in $FORKS; do + echo -n "→ $username: " + + # Fetch user profile + USER_DATA=$(curl -s "https://api.github.com/users/$username") + + EMAIL=$(echo "$USER_DATA" | jq -r '.email // "N/A"') + NAME=$(echo "$USER_DATA" | jq -r '.name // "N/A"') + COMPANY=$(echo "$USER_DATA" | jq -r '.company // "N/A"') + LOCATION=$(echo "$USER_DATA" | jq -r '.location // "N/A"') + TWITTER=$(echo "$USER_DATA" | jq -r '.twitter_username // "N/A"') + BLOG=$(echo "$USER_DATA" | jq -r '.blog // "N/A"') + BIO=$(echo "$USER_DATA" | jq -r '.bio // "N/A"' | tr ',' ';' | tr '\n' ' ') + + # Try to get email from recent commits if not in profile + if [ "$EMAIL" = "N/A" ] || [ "$EMAIL" = "null" ]; then + COMMIT_EMAIL=$(curl -s "https://api.github.com/users/$username/events/public" | \ + jq -r '[.[] | select(.type=="PushEvent") | .payload.commits[]?.author.email] | first // "N/A"') + if [ "$COMMIT_EMAIL" != "N/A" ] && [ "$COMMIT_EMAIL" != "null" ] && [[ ! "$COMMIT_EMAIL" =~ "noreply" ]]; then + EMAIL="$COMMIT_EMAIL" + fi + fi + + echo "$username,$EMAIL,$NAME,$COMPANY,$LOCATION,$TWITTER,$BLOG,\"$BIO\"" >> "$OUTPUT_FILE" + + if [ "$EMAIL" != "N/A" ] && [ "$EMAIL" != "null" ]; then + echo "āœ“ Found email: $EMAIL" + else + echo "ā—‹ No public email (check Twitter: $TWITTER, Blog: $BLOG)" + fi + + sleep 0.5 # Rate limiting +done + +echo "" +echo "═══════════════════════════════════════════════════════════════════" +echo " SUMMARY" +echo "═══════════════════════════════════════════════════════════════════" +echo "" +TOTAL=$(echo "$FORKS" | wc -l | tr -d ' ') +WITH_EMAIL=$(grep -v "N/A" "$OUTPUT_FILE" | grep -v "null" | grep "@" | wc -l | tr -d ' ') +echo "Total contributors: $TOTAL" +echo "With public email: $WITH_EMAIL" +echo "" +echo "āœ… Results saved to: $OUTPUT_FILE" +echo "" + +# Display results +echo "═══════════════════════════════════════════════════════════════════" +echo " CONTACT DETAILS" +echo "═══════════════════════════════════════════════════════════════════" +column -t -s',' "$OUTPUT_FILE" | head -20 diff --git a/scripts/github/merge-mike-prs.sh b/scripts/github/merge-mike-prs.sh old mode 100755 new mode 100644 index 1831ac94..65f5b71f --- a/scripts/github/merge-mike-prs.sh +++ b/scripts/github/merge-mike-prs.sh @@ -1,81 +1,81 @@ -#!/bin/bash -# CORTEX - Quick Merge Mike's PRs -# Merges all PRs authored by @mikejmorgan-ai to clear backlog - -set -e - -echo "šŸš€ CORTEX - MERGE MIKE'S IMPLEMENTATION PRs" -echo "===========================================" -echo "" - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") - -export GH_TOKEN="$GITHUB_TOKEN" - -echo "Merging PRs authored by @mikejmorgan-ai..." -echo "" - -# PRs to merge (excluding #17, #18, #21, #37, #38 which are from contributors) -MIKE_PRS=(41 36 34 23 22 20) - -for pr in "${MIKE_PRS[@]}"; do - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "PR #$pr" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Get PR info - pr_info=$(gh pr view $pr --repo $REPO --json title,state,mergeable 2>/dev/null || echo "") - - if [ -z "$pr_info" ]; then - echo "āŒ PR #$pr not found or not accessible" - echo "" - continue - fi - - pr_title=$(echo "$pr_info" | jq -r '.title') - pr_state=$(echo "$pr_info" | jq -r '.state') - pr_mergeable=$(echo "$pr_info" | jq -r '.mergeable') - - echo "Title: $pr_title" - echo "State: $pr_state" - echo "Mergeable: $pr_mergeable" - echo "" - - if [ "$pr_state" != "OPEN" ]; then - echo "ā­ļø PR already merged or closed" - echo "" - continue - fi - - if [ "$pr_mergeable" = "CONFLICTING" ]; then - echo "āš ļø PR has merge conflicts - needs manual resolution" - echo "" - continue - fi - - echo "Merge this PR? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "šŸ”„ Merging PR #$pr..." - - gh pr merge $pr --repo $REPO --squash --delete-branch 2>/dev/null && \ - echo "āœ… PR #$pr merged successfully!" || \ - echo "āŒ Failed to merge PR #$pr (may need manual merge)" - else - echo "ā­ļø Skipped PR #$pr" - fi - - echo "" -done - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… MERGE PROCESS COMPLETE" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" -echo "Next steps:" -echo "1. Review contributor PRs: #17, #21, #37, #38" -echo "2. Process bounty payments" -echo "3. Post update to Discord" +#!/bin/bash +# CORTEX - Quick Merge Mike's PRs +# Merges all PRs authored by @mikejmorgan-ai to clear backlog + +set -e + +echo "šŸš€ CORTEX - MERGE MIKE'S IMPLEMENTATION PRs" +echo "===========================================" +echo "" + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") + +export GH_TOKEN="$GITHUB_TOKEN" + +echo "Merging PRs authored by @mikejmorgan-ai..." +echo "" + +# PRs to merge (excluding #17, #18, #21, #37, #38 which are from contributors) +MIKE_PRS=(41 36 34 23 22 20) + +for pr in "${MIKE_PRS[@]}"; do + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "PR #$pr" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Get PR info + pr_info=$(gh pr view $pr --repo $REPO --json title,state,mergeable 2>/dev/null || echo "") + + if [ -z "$pr_info" ]; then + echo "āŒ PR #$pr not found or not accessible" + echo "" + continue + fi + + pr_title=$(echo "$pr_info" | jq -r '.title') + pr_state=$(echo "$pr_info" | jq -r '.state') + pr_mergeable=$(echo "$pr_info" | jq -r '.mergeable') + + echo "Title: $pr_title" + echo "State: $pr_state" + echo "Mergeable: $pr_mergeable" + echo "" + + if [ "$pr_state" != "OPEN" ]; then + echo "ā­ļø PR already merged or closed" + echo "" + continue + fi + + if [ "$pr_mergeable" = "CONFLICTING" ]; then + echo "āš ļø PR has merge conflicts - needs manual resolution" + echo "" + continue + fi + + echo "Merge this PR? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "šŸ”„ Merging PR #$pr..." + + gh pr merge $pr --repo $REPO --squash --delete-branch 2>/dev/null && \ + echo "āœ… PR #$pr merged successfully!" || \ + echo "āŒ Failed to merge PR #$pr (may need manual merge)" + else + echo "ā­ļø Skipped PR #$pr" + fi + + echo "" +done + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… MERGE PROCESS COMPLETE" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Next steps:" +echo "1. Review contributor PRs: #17, #21, #37, #38" +echo "2. Process bounty payments" +echo "3. Post update to Discord" diff --git a/scripts/github/organize-issues.sh b/scripts/github/organize-issues.sh old mode 100755 new mode 100644 index 36d7a17e..28933947 --- a/scripts/github/organize-issues.sh +++ b/scripts/github/organize-issues.sh @@ -1,51 +1,51 @@ -#!/bin/bash -# Label and organize issues for MVP focus - -set -e - -echo "šŸŽÆ ORGANIZING ISSUES FOR MVP FOCUS" -echo "=====================================" - -cd ~/cortex - -echo "Strategy:" -echo " Issues #1-30: MVP Critical" -echo " Issues #31-45: MVP Nice-to-Have" -echo " Issues #46+: Post-MVP" -echo "" - -read -p "Organize all issues? (y/n): " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 0 -fi - -# Create milestones -echo "šŸ“‹ Creating milestones..." -gh api repos/cortexlinux/cortex/milestones --method POST \ - -f title='MVP - Core Features' \ - -f description='Critical features required for MVP launch' 2>/dev/null || echo " MVP milestone exists" - -gh api repos/cortexlinux/cortex/milestones --method POST \ - -f title='Post-MVP - Enhancements' \ - -f description='Features for post-MVP releases' 2>/dev/null || echo " Post-MVP milestone exists" - -echo "" -echo "šŸ·ļø Labeling MVP Critical (#1-30)..." -for i in {1..30}; do - gh issue edit $i --add-label "mvp-critical,priority: critical" --milestone "MVP - Core Features" 2>/dev/null && echo " āœ… #$i" || echo " āš ļø #$i not found" - sleep 0.3 -done - -echo "" -echo "šŸ·ļø Labeling Post-MVP (#46-150)..." -for i in {46..150}; do - gh issue edit $i --add-label "post-mvp" --milestone "Post-MVP - Enhancements" 2>/dev/null - (( i % 20 == 0 )) && echo " Processed through #$i..." && sleep 1 -done - -echo "" -echo "āœ… COMPLETE!" -echo "" -echo "View MVP Critical: https://github.com/cortexlinux/cortex/issues?q=is%3Aopen+label%3Amvp-critical" +#!/bin/bash +# Label and organize issues for MVP focus + +set -e + +echo "šŸŽÆ ORGANIZING ISSUES FOR MVP FOCUS" +echo "=====================================" + +cd ~/cortex + +echo "Strategy:" +echo " Issues #1-30: MVP Critical" +echo " Issues #31-45: MVP Nice-to-Have" +echo " Issues #46+: Post-MVP" +echo "" + +read -p "Organize all issues? (y/n): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +# Create milestones +echo "šŸ“‹ Creating milestones..." +gh api repos/cortexlinux/cortex/milestones --method POST \ + -f title='MVP - Core Features' \ + -f description='Critical features required for MVP launch' 2>/dev/null || echo " MVP milestone exists" + +gh api repos/cortexlinux/cortex/milestones --method POST \ + -f title='Post-MVP - Enhancements' \ + -f description='Features for post-MVP releases' 2>/dev/null || echo " Post-MVP milestone exists" + +echo "" +echo "šŸ·ļø Labeling MVP Critical (#1-30)..." +for i in {1..30}; do + gh issue edit $i --add-label "mvp-critical,priority: critical" --milestone "MVP - Core Features" 2>/dev/null && echo " āœ… #$i" || echo " āš ļø #$i not found" + sleep 0.3 +done + +echo "" +echo "šŸ·ļø Labeling Post-MVP (#46-150)..." +for i in {46..150}; do + gh issue edit $i --add-label "post-mvp" --milestone "Post-MVP - Enhancements" 2>/dev/null + (( i % 20 == 0 )) && echo " Processed through #$i..." && sleep 1 +done + +echo "" +echo "āœ… COMPLETE!" +echo "" +echo "View MVP Critical: https://github.com/cortexlinux/cortex/issues?q=is%3Aopen+label%3Amvp-critical" diff --git a/scripts/github/review-contributor-prs.sh b/scripts/github/review-contributor-prs.sh old mode 100755 new mode 100644 index 8a5be9d1..03a7a190 --- a/scripts/github/review-contributor-prs.sh +++ b/scripts/github/review-contributor-prs.sh @@ -1,314 +1,314 @@ -#!/bin/bash -# CORTEX - CONTRIBUTOR PR REVIEW & MERGE SYSTEM -# Reviews PRs from contributors, tracks bounties, posts thank-yous - -set -e - -echo "šŸ” CORTEX - CONTRIBUTOR PR REVIEW SYSTEM" -echo "========================================" -echo "" - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") - -export GH_TOKEN="$GITHUB_TOKEN" - -# Track bounties owed -BOUNTIES_FILE="$HOME/cortex/bounties_owed.csv" - -# Create bounties file if doesn't exist -if [ ! -f "$BOUNTIES_FILE" ]; then - echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTIES_FILE" -fi - -echo "šŸ“Š CONTRIBUTOR PR REVIEW QUEUE" -echo "────────────────────────────────" -echo "" - -# Contributor PRs to review (in priority order) -declare -A PR_DETAILS -PR_DETAILS[17]="chandrapratamar|Package Manager Wrapper (Issue #7)|100|CRITICAL_MVP_BLOCKER" -PR_DETAILS[37]="AlexanderLuzDH|Progress Notifications (Issue #27)|125|HIGH_PRIORITY" -PR_DETAILS[38]="AlexanderLuzDH|Requirements Pre-flight Check (Issue #28)|100|HIGH_PRIORITY" -PR_DETAILS[21]="aliraza556|Config File Templates (Issue #16)|150|HIGH_PRIORITY" -PR_DETAILS[18]="Sahilbhatane|CLI Interface (Issue #11)|100|DRAFT_WAIT" - -# Function to review a PR -review_pr() { - local pr_num=$1 - local pr_data="${PR_DETAILS[$pr_num]}" - - IFS='|' read -r developer feature bounty priority <<< "$pr_data" - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸ“‹ PR #$pr_num - $feature" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "šŸ‘¤ Developer: @$developer" - echo "šŸŽÆ Feature: $feature" - echo "šŸ’° Bounty: \$$bounty" - echo "šŸ”„ Priority: $priority" - echo "" - - # Check if draft - pr_state=$(gh pr view $pr_num --repo $REPO --json isDraft 2>/dev/null | jq -r '.isDraft') - - if [ "$pr_state" = "true" ]; then - echo "šŸ“ Status: DRAFT - Not ready for review yet" - echo " Action: Skip for now, will review when marked ready" - echo "" - return - fi - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "REVIEW CHECKLIST" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Before approving, verify:" - echo " [ ] Code implements the feature described in the issue" - echo " [ ] Unit tests included with >80% coverage" - echo " [ ] Documentation/README included" - echo " [ ] Integrates with existing Cortex architecture" - echo " [ ] No obvious bugs or security issues" - echo " [ ] Follows Python best practices" - echo "" - - echo "Actions:" - echo " [v] View PR in browser (to review code)" - echo " [a] Approve & Merge (if review passed)" - echo " [c] Request Changes (if issues found)" - echo " [m] Add Comment (questions/feedback)" - echo " [s] Skip to next PR" - echo " [q] Quit review mode" - echo "" - echo -n "Choose action: " - read -n 1 action - echo "" - echo "" - - case $action in - v|V) - echo "🌐 Opening PR #$pr_num in browser..." - gh pr view $pr_num --repo $REPO --web - echo "" - echo "After reviewing, come back to approve/change/comment." - echo "" - echo "Take action now? (y/n)" - read -n 1 take_action - echo "" - - if [[ ! $take_action =~ ^[Yy]$ ]]; then - echo "ā­ļø Skipping for now..." - return - fi - - # Ask again which action - echo "" - echo "What action? [a]pprove [c]hange [m]comment [s]kip" - read -n 1 action - echo "" - ;;& # Continue to next pattern - - a|A) - echo "āœ… APPROVING & MERGING PR #$pr_num" - echo "" - - # Post approval review - approval_msg="āœ… **APPROVED - Excellent Work!** - -Thank you @$developer for this outstanding contribution! šŸŽ‰ - -**Review Summary:** -- āœ… Code quality: Professional implementation -- āœ… Testing: Comprehensive unit tests included -- āœ… Documentation: Clear and complete -- āœ… Integration: Works seamlessly with Cortex architecture - -**What's Next:** -1. Merging this PR immediately -2. Your bounty of **\$$bounty USD** will be processed within 48 hours -3. Payment via crypto (Bitcoin/USDC) or PayPal - we'll coordinate via issue comment - -**You're making history** - this is a foundational piece of the AI-native operating system! 🧠⚔ - -**Bonus Reminder:** At funding (Feb 2025), you'll receive **2x this bounty** as a thank-you bonus. - -Welcome to the Cortex Linux core contributor team! šŸš€ - ---- -*Automated review from Cortex PR Management System*" - - echo "$approval_msg" | gh pr review $pr_num --repo $REPO --approve --body-file - 2>/dev/null || \ - echo "āš ļø Could not post review (may need manual approval)" - - echo "" - echo "Merging PR #$pr_num now..." - - gh pr merge $pr_num --repo $REPO --squash --delete-branch 2>/dev/null && { - echo "āœ… PR #$pr_num merged successfully!" - - # Record bounty owed - merge_date=$(date +%Y-%m-%d) - echo "$pr_num,$developer,$feature,$bounty,$merge_date,PENDING" >> "$BOUNTIES_FILE" - - echo "" - echo "šŸ’° Bounty recorded: \$$bounty owed to @$developer" - echo " Recorded in: $BOUNTIES_FILE" - } || { - echo "āŒ Merge failed - may need manual intervention" - } - - echo "" - ;; - - c|C) - echo "šŸ”„ REQUESTING CHANGES on PR #$pr_num" - echo "" - echo "Enter your feedback (what needs to change):" - echo "Press Ctrl+D when done" - echo "---" - feedback=$(cat) - - change_msg="šŸ”„ **Changes Requested** - -Thank you for your contribution @$developer! The code is solid, but a few items need attention before merge: - -$feedback - -**Please update and let me know when ready** for re-review. I'll prioritize getting this merged quickly once addressed. - -**Questions?** Comment here or ping me in Discord (#dev-questions). - -We appreciate your patience! šŸ™ - ---- -*Automated review from Cortex PR Management System*" - - echo "$change_msg" | gh pr review $pr_num --repo $REPO --request-changes --body-file - 2>/dev/null || \ - echo "āš ļø Could not post review" - - echo "" - echo "āœ… Change request posted" - echo "" - ;; - - m|M) - echo "šŸ’¬ ADDING COMMENT to PR #$pr_num" - echo "" - echo "Enter your comment:" - echo "Press Ctrl+D when done" - echo "---" - comment=$(cat) - - gh pr comment $pr_num --repo $REPO --body "$comment" 2>/dev/null && \ - echo "āœ… Comment posted" || \ - echo "āš ļø Could not post comment" - - echo "" - ;; - - s|S) - echo "ā­ļø Skipping PR #$pr_num" - echo "" - ;; - - q|Q) - echo "šŸ‘‹ Exiting review mode..." - echo "" - return 1 - ;; - - *) - echo "ā­ļø Invalid action, skipping..." - echo "" - ;; - esac -} - -# Main review loop -echo "Starting PR review process..." -echo "" - -PR_ORDER=(17 37 38 21 18) # Priority order - -for pr in "${PR_ORDER[@]}"; do - review_pr $pr || break -done - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ“Š REVIEW SESSION COMPLETE" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Show bounties owed -if [ -f "$BOUNTIES_FILE" ]; then - echo "šŸ’° BOUNTIES OWED (from this session and previous)" - echo "──────────────────────────────────────────────────" - echo "" - - total_owed=0 - - tail -n +2 "$BOUNTIES_FILE" | while IFS=',' read -r pr dev feature amount date status; do - if [ "$status" = "PENDING" ]; then - echo " PR #$pr - @$dev: \$$amount ($feature)" - total_owed=$((total_owed + amount)) - fi - done - - echo "" - echo " Total pending: \$$total_owed USD" - echo "" - echo " Payment file: $BOUNTIES_FILE" - echo "" -fi - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸŽÆ NEXT STEPS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" -echo "1. Process bounty payments (see $BOUNTIES_FILE)" -echo "2. Post Discord announcement about merged PRs" -echo "3. Check if Issue #7 unblocked (if PR #17 merged)" -echo "4. Welcome new developers to comment on issues" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Generate Discord announcement -discord_msg="šŸŽ‰ **PR MERGE UPDATE - $(date +%Y-%m-%d)** - -**PRs Merged Today:** -(Check the bounties file for details) - -**Critical Path Progress:** -- Issue #7 (Package Manager): $([ -f "$BOUNTIES_FILE" ] && grep -q "^17," "$BOUNTIES_FILE" && echo "āœ… MERGED - MVP BLOCKER CLEARED!" || echo "ā³ In review") - -**Bounties Being Processed:** -- See individual PR comments for payment coordination -- 2x bonus reminder: When we close funding (Feb 2025), all bounties paid so far get 2x bonus - -**What This Means:** -- MVP velocity accelerating -- February funding timeline on track -- Professional team execution demonstrated - -**For Contributors:** -- Check your merged PRs for bounty coordination comments -- Payment within 48 hours of merge -- Crypto (Bitcoin/USDC) or PayPal options - -**Open Issues Still Available:** -Browse: https://github.com/cortexlinux/cortex/issues -Join: Discord #dev-questions - -Let's keep the momentum! 🧠⚔" - -echo "šŸ“± DISCORD ANNOUNCEMENT (copy and post to #announcements)" -echo "────────────────────────────────────────────────────────" -echo "" -echo "$discord_msg" -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… PR Review System Complete!" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +#!/bin/bash +# CORTEX - CONTRIBUTOR PR REVIEW & MERGE SYSTEM +# Reviews PRs from contributors, tracks bounties, posts thank-yous + +set -e + +echo "šŸ” CORTEX - CONTRIBUTOR PR REVIEW SYSTEM" +echo "========================================" +echo "" + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") + +export GH_TOKEN="$GITHUB_TOKEN" + +# Track bounties owed +BOUNTIES_FILE="$HOME/cortex/bounties_owed.csv" + +# Create bounties file if doesn't exist +if [ ! -f "$BOUNTIES_FILE" ]; then + echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTIES_FILE" +fi + +echo "šŸ“Š CONTRIBUTOR PR REVIEW QUEUE" +echo "────────────────────────────────" +echo "" + +# Contributor PRs to review (in priority order) +declare -A PR_DETAILS +PR_DETAILS[17]="chandrapratamar|Package Manager Wrapper (Issue #7)|100|CRITICAL_MVP_BLOCKER" +PR_DETAILS[37]="AlexanderLuzDH|Progress Notifications (Issue #27)|125|HIGH_PRIORITY" +PR_DETAILS[38]="AlexanderLuzDH|Requirements Pre-flight Check (Issue #28)|100|HIGH_PRIORITY" +PR_DETAILS[21]="aliraza556|Config File Templates (Issue #16)|150|HIGH_PRIORITY" +PR_DETAILS[18]="Sahilbhatane|CLI Interface (Issue #11)|100|DRAFT_WAIT" + +# Function to review a PR +review_pr() { + local pr_num=$1 + local pr_data="${PR_DETAILS[$pr_num]}" + + IFS='|' read -r developer feature bounty priority <<< "$pr_data" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸ“‹ PR #$pr_num - $feature" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "šŸ‘¤ Developer: @$developer" + echo "šŸŽÆ Feature: $feature" + echo "šŸ’° Bounty: \$$bounty" + echo "šŸ”„ Priority: $priority" + echo "" + + # Check if draft + pr_state=$(gh pr view $pr_num --repo $REPO --json isDraft 2>/dev/null | jq -r '.isDraft') + + if [ "$pr_state" = "true" ]; then + echo "šŸ“ Status: DRAFT - Not ready for review yet" + echo " Action: Skip for now, will review when marked ready" + echo "" + return + fi + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "REVIEW CHECKLIST" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Before approving, verify:" + echo " [ ] Code implements the feature described in the issue" + echo " [ ] Unit tests included with >80% coverage" + echo " [ ] Documentation/README included" + echo " [ ] Integrates with existing Cortex architecture" + echo " [ ] No obvious bugs or security issues" + echo " [ ] Follows Python best practices" + echo "" + + echo "Actions:" + echo " [v] View PR in browser (to review code)" + echo " [a] Approve & Merge (if review passed)" + echo " [c] Request Changes (if issues found)" + echo " [m] Add Comment (questions/feedback)" + echo " [s] Skip to next PR" + echo " [q] Quit review mode" + echo "" + echo -n "Choose action: " + read -n 1 action + echo "" + echo "" + + case $action in + v|V) + echo "🌐 Opening PR #$pr_num in browser..." + gh pr view $pr_num --repo $REPO --web + echo "" + echo "After reviewing, come back to approve/change/comment." + echo "" + echo "Take action now? (y/n)" + read -n 1 take_action + echo "" + + if [[ ! $take_action =~ ^[Yy]$ ]]; then + echo "ā­ļø Skipping for now..." + return + fi + + # Ask again which action + echo "" + echo "What action? [a]pprove [c]hange [m]comment [s]kip" + read -n 1 action + echo "" + ;;& # Continue to next pattern + + a|A) + echo "āœ… APPROVING & MERGING PR #$pr_num" + echo "" + + # Post approval review + approval_msg="āœ… **APPROVED - Excellent Work!** + +Thank you @$developer for this outstanding contribution! šŸŽ‰ + +**Review Summary:** +- āœ… Code quality: Professional implementation +- āœ… Testing: Comprehensive unit tests included +- āœ… Documentation: Clear and complete +- āœ… Integration: Works seamlessly with Cortex architecture + +**What's Next:** +1. Merging this PR immediately +2. Your bounty of **\$$bounty USD** will be processed within 48 hours +3. Payment via crypto (Bitcoin/USDC) or PayPal - we'll coordinate via issue comment + +**You're making history** - this is a foundational piece of the AI-native operating system! 🧠⚔ + +**Bonus Reminder:** At funding (Feb 2025), you'll receive **2x this bounty** as a thank-you bonus. + +Welcome to the Cortex Linux core contributor team! šŸš€ + +--- +*Automated review from Cortex PR Management System*" + + echo "$approval_msg" | gh pr review $pr_num --repo $REPO --approve --body-file - 2>/dev/null || \ + echo "āš ļø Could not post review (may need manual approval)" + + echo "" + echo "Merging PR #$pr_num now..." + + gh pr merge $pr_num --repo $REPO --squash --delete-branch 2>/dev/null && { + echo "āœ… PR #$pr_num merged successfully!" + + # Record bounty owed + merge_date=$(date +%Y-%m-%d) + echo "$pr_num,$developer,$feature,$bounty,$merge_date,PENDING" >> "$BOUNTIES_FILE" + + echo "" + echo "šŸ’° Bounty recorded: \$$bounty owed to @$developer" + echo " Recorded in: $BOUNTIES_FILE" + } || { + echo "āŒ Merge failed - may need manual intervention" + } + + echo "" + ;; + + c|C) + echo "šŸ”„ REQUESTING CHANGES on PR #$pr_num" + echo "" + echo "Enter your feedback (what needs to change):" + echo "Press Ctrl+D when done" + echo "---" + feedback=$(cat) + + change_msg="šŸ”„ **Changes Requested** + +Thank you for your contribution @$developer! The code is solid, but a few items need attention before merge: + +$feedback + +**Please update and let me know when ready** for re-review. I'll prioritize getting this merged quickly once addressed. + +**Questions?** Comment here or ping me in Discord (#dev-questions). + +We appreciate your patience! šŸ™ + +--- +*Automated review from Cortex PR Management System*" + + echo "$change_msg" | gh pr review $pr_num --repo $REPO --request-changes --body-file - 2>/dev/null || \ + echo "āš ļø Could not post review" + + echo "" + echo "āœ… Change request posted" + echo "" + ;; + + m|M) + echo "šŸ’¬ ADDING COMMENT to PR #$pr_num" + echo "" + echo "Enter your comment:" + echo "Press Ctrl+D when done" + echo "---" + comment=$(cat) + + gh pr comment $pr_num --repo $REPO --body "$comment" 2>/dev/null && \ + echo "āœ… Comment posted" || \ + echo "āš ļø Could not post comment" + + echo "" + ;; + + s|S) + echo "ā­ļø Skipping PR #$pr_num" + echo "" + ;; + + q|Q) + echo "šŸ‘‹ Exiting review mode..." + echo "" + return 1 + ;; + + *) + echo "ā­ļø Invalid action, skipping..." + echo "" + ;; + esac +} + +# Main review loop +echo "Starting PR review process..." +echo "" + +PR_ORDER=(17 37 38 21 18) # Priority order + +for pr in "${PR_ORDER[@]}"; do + review_pr $pr || break +done + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ“Š REVIEW SESSION COMPLETE" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Show bounties owed +if [ -f "$BOUNTIES_FILE" ]; then + echo "šŸ’° BOUNTIES OWED (from this session and previous)" + echo "──────────────────────────────────────────────────" + echo "" + + total_owed=0 + + tail -n +2 "$BOUNTIES_FILE" | while IFS=',' read -r pr dev feature amount date status; do + if [ "$status" = "PENDING" ]; then + echo " PR #$pr - @$dev: \$$amount ($feature)" + total_owed=$((total_owed + amount)) + fi + done + + echo "" + echo " Total pending: \$$total_owed USD" + echo "" + echo " Payment file: $BOUNTIES_FILE" + echo "" +fi + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸŽÆ NEXT STEPS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "1. Process bounty payments (see $BOUNTIES_FILE)" +echo "2. Post Discord announcement about merged PRs" +echo "3. Check if Issue #7 unblocked (if PR #17 merged)" +echo "4. Welcome new developers to comment on issues" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Generate Discord announcement +discord_msg="šŸŽ‰ **PR MERGE UPDATE - $(date +%Y-%m-%d)** + +**PRs Merged Today:** +(Check the bounties file for details) + +**Critical Path Progress:** +- Issue #7 (Package Manager): $([ -f "$BOUNTIES_FILE" ] && grep -q "^17," "$BOUNTIES_FILE" && echo "āœ… MERGED - MVP BLOCKER CLEARED!" || echo "ā³ In review") + +**Bounties Being Processed:** +- See individual PR comments for payment coordination +- 2x bonus reminder: When we close funding (Feb 2025), all bounties paid so far get 2x bonus + +**What This Means:** +- MVP velocity accelerating +- February funding timeline on track +- Professional team execution demonstrated + +**For Contributors:** +- Check your merged PRs for bounty coordination comments +- Payment within 48 hours of merge +- Crypto (Bitcoin/USDC) or PayPal options + +**Open Issues Still Available:** +Browse: https://github.com/cortexlinux/cortex/issues +Join: Discord #dev-questions + +Let's keep the momentum! 🧠⚔" + +echo "šŸ“± DISCORD ANNOUNCEMENT (copy and post to #announcements)" +echo "────────────────────────────────────────────────────────" +echo "" +echo "$discord_msg" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… PR Review System Complete!" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/scripts/recruit-ready.sh b/scripts/recruit-ready.sh old mode 100755 new mode 100644 index 1efb5777..80ba7991 --- a/scripts/recruit-ready.sh +++ b/scripts/recruit-ready.sh @@ -1,175 +1,175 @@ -#!/bin/bash -# -# recruit-ready.sh - Repository status and Discord recruitment message generator -# For Cortex Linux repository management -# - -set -e - -REPO="cortexlinux/cortex" -DISCORD_BLUE="\033[34m" -DISCORD_GREEN="\033[32m" -DISCORD_YELLOW="\033[33m" -DISCORD_RED="\033[31m" -DISCORD_CYAN="\033[36m" -BOLD="\033[1m" -RESET="\033[0m" - -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo -e "${BOLD}${DISCORD_CYAN} CORTEX LINUX - REPOSITORY STATUS REPORT${RESET}" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo "" - -# 1. CI Status -echo -e "${BOLD}${DISCORD_BLUE}šŸ“Š CI STATUS${RESET}" -echo "─────────────────────────────────────────" -CI_STATUS=$(gh run list --repo $REPO --workflow=ci.yml --limit 1 --json status,conclusion,displayTitle --jq '.[0]') -CI_CONCLUSION=$(echo $CI_STATUS | jq -r '.conclusion') -CI_TITLE=$(echo $CI_STATUS | jq -r '.displayTitle') - -if [ "$CI_CONCLUSION" = "success" ]; then - echo -e "${DISCORD_GREEN}āœ… CI PASSING${RESET} - $CI_TITLE" -elif [ "$CI_CONCLUSION" = "failure" ]; then - echo -e "${DISCORD_RED}āŒ CI FAILING${RESET} - $CI_TITLE" -else - echo -e "${DISCORD_YELLOW}ā³ CI IN PROGRESS${RESET} - $CI_TITLE" -fi -echo "" - -# 2. Repository Stats -echo -e "${BOLD}${DISCORD_BLUE}šŸ“ˆ REPOSITORY STATS${RESET}" -echo "─────────────────────────────────────────" -REPO_STATS=$(gh api repos/$REPO --jq '{stars: .stargazers_count, forks: .forks_count, issues: .open_issues_count, watchers: .subscribers_count}') -STARS=$(echo $REPO_STATS | jq -r '.stars') -FORKS=$(echo $REPO_STATS | jq -r '.forks') -ISSUES=$(echo $REPO_STATS | jq -r '.issues') -WATCHERS=$(echo $REPO_STATS | jq -r '.watchers') - -echo "⭐ Stars: $STARS | šŸ“ Forks: $FORKS | šŸ“‹ Open Issues: $ISSUES | šŸ‘€ Watchers: $WATCHERS" -echo "" - -# 3. Mergeable PRs -echo -e "${BOLD}${DISCORD_BLUE}āœ… MERGEABLE PRs (Ready to Merge)${RESET}" -echo "─────────────────────────────────────────" -MERGEABLE_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,mergeable,reviewDecision --jq '.[] | select(.mergeable == "MERGEABLE") | "PR #\(.number): \(.title) by @\(.author.login)"') - -if [ -z "$MERGEABLE_PRS" ]; then - echo "No PRs currently ready to merge" -else - echo "$MERGEABLE_PRS" -fi -echo "" - -# 4. PRs Needing Review -echo -e "${BOLD}${DISCORD_BLUE}šŸ‘€ PRs NEEDING REVIEW${RESET}" -echo "─────────────────────────────────────────" -REVIEW_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,reviewDecision --jq '.[] | select(.reviewDecision == "REVIEW_REQUIRED" or .reviewDecision == "") | "PR #\(.number): \(.title) by @\(.author.login)"' | head -10) - -if [ -z "$REVIEW_PRS" ]; then - echo "No PRs awaiting review" -else - echo "$REVIEW_PRS" -fi -echo "" - -# 5. Recently Merged PRs (potential bounty payments) -echo -e "${BOLD}${DISCORD_YELLOW}šŸ’° RECENTLY MERGED (Check Bounty Payments)${RESET}" -echo "─────────────────────────────────────────" -MERGED_PRS=$(gh pr list --repo $REPO --state merged --limit 10 --json number,title,author,mergedAt,labels --jq '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Merged: \(.mergedAt[:10])"') - -if [ -z "$MERGED_PRS" ]; then - echo "No recently merged PRs" -else - echo "$MERGED_PRS" -fi -echo "" - -# 6. Open Issues with Bounties -echo -e "${BOLD}${DISCORD_BLUE}šŸŽÆ OPEN ISSUES WITH BOUNTIES${RESET}" -echo "─────────────────────────────────────────" -BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --label "bounty" --json number,title,labels --jq '.[] | "#\(.number): \(.title)"' 2>/dev/null || echo "") - -if [ -z "$BOUNTY_ISSUES" ]; then - # Try to find issues that might have bounty in title or other bounty-related labels - BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --json number,title,labels --jq '.[] | select(.title | test("bounty|\\$"; "i")) | "#\(.number): \(.title)"' 2>/dev/null || echo "No bounty issues found") -fi - -if [ -z "$BOUNTY_ISSUES" ] || [ "$BOUNTY_ISSUES" = "No bounty issues found" ]; then - echo "No issues with bounty labels found" - echo "Tip: Add 'bounty' label to issues to track them here" -else - echo "$BOUNTY_ISSUES" -fi -echo "" - -# 7. Top Contributors (from recent PRs) -echo -e "${BOLD}${DISCORD_BLUE}šŸ‘„ ACTIVE CONTRIBUTORS (Recent PRs)${RESET}" -echo "─────────────────────────────────────────" -CONTRIBUTORS=$(gh pr list --repo $REPO --state all --limit 50 --json author --jq '.[].author.login' | sort | uniq -c | sort -rn | head -5) -echo "$CONTRIBUTORS" -echo "" - -# 8. Discord Recruitment Message -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo -e "${BOLD}${DISCORD_CYAN} DISCORD RECRUITMENT MESSAGE (Copy Below)${RESET}" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo "" - -# Count open PRs and issues for the message -OPEN_PRS=$(gh pr list --repo $REPO --state open --json number --jq 'length') -OPEN_ISSUES_COUNT=$(gh issue list --repo $REPO --state open --json number --jq 'length') - -cat << 'DISCORD_MSG' -``` -šŸš€ CORTEX LINUX - OPEN SOURCE CONTRIBUTORS WANTED! šŸš€ -``` - -**What is Cortex Linux?** -An AI-powered package manager for Debian/Ubuntu that understands natural language. Instead of memorizing apt commands, just tell Cortex what you want: - -```bash -cortex install "set up a Python ML environment with TensorFlow" -``` - -**Why Contribute?** -DISCORD_MSG - -echo "- šŸ’° **Bounty Program** - Get paid for merged PRs" -echo "- 🌟 **$STARS stars** and growing" -echo "- šŸ”„ **$OPEN_PRS open PRs** to review/merge" -echo "- šŸ“‹ **$OPEN_ISSUES_COUNT open issues** to work on" -echo "- šŸ¤ Friendly community, fast PR reviews" - -cat << 'DISCORD_MSG' - -**Good First Issues:** -- Documentation improvements -- Test coverage -- Bug fixes with clear reproduction steps - -**Tech Stack:** -- Python 3.10+ -- OpenAI/Anthropic APIs -- Rich TUI library - -**Links:** -DISCORD_MSG - -echo "- GitHub: https://github.com/$REPO" -echo "- Discussions: https://github.com/$REPO/discussions" - -cat << 'DISCORD_MSG' - -**How to Start:** -1. Fork the repo -2. Pick an issue labeled `good-first-issue` or `help-wanted` -3. Submit a PR -4. Get reviewed & merged! - -Drop a šŸ‘‹ if you're interested or have questions! -DISCORD_MSG - -echo "" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo -e "${BOLD}Generated: $(date)${RESET}" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +#!/bin/bash +# +# recruit-ready.sh - Repository status and Discord recruitment message generator +# For Cortex Linux repository management +# + +set -e + +REPO="cortexlinux/cortex" +DISCORD_BLUE="\033[34m" +DISCORD_GREEN="\033[32m" +DISCORD_YELLOW="\033[33m" +DISCORD_RED="\033[31m" +DISCORD_CYAN="\033[36m" +BOLD="\033[1m" +RESET="\033[0m" + +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo -e "${BOLD}${DISCORD_CYAN} CORTEX LINUX - REPOSITORY STATUS REPORT${RESET}" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo "" + +# 1. CI Status +echo -e "${BOLD}${DISCORD_BLUE}šŸ“Š CI STATUS${RESET}" +echo "─────────────────────────────────────────" +CI_STATUS=$(gh run list --repo $REPO --workflow=ci.yml --limit 1 --json status,conclusion,displayTitle --jq '.[0]') +CI_CONCLUSION=$(echo $CI_STATUS | jq -r '.conclusion') +CI_TITLE=$(echo $CI_STATUS | jq -r '.displayTitle') + +if [ "$CI_CONCLUSION" = "success" ]; then + echo -e "${DISCORD_GREEN}āœ… CI PASSING${RESET} - $CI_TITLE" +elif [ "$CI_CONCLUSION" = "failure" ]; then + echo -e "${DISCORD_RED}āŒ CI FAILING${RESET} - $CI_TITLE" +else + echo -e "${DISCORD_YELLOW}ā³ CI IN PROGRESS${RESET} - $CI_TITLE" +fi +echo "" + +# 2. Repository Stats +echo -e "${BOLD}${DISCORD_BLUE}šŸ“ˆ REPOSITORY STATS${RESET}" +echo "─────────────────────────────────────────" +REPO_STATS=$(gh api repos/$REPO --jq '{stars: .stargazers_count, forks: .forks_count, issues: .open_issues_count, watchers: .subscribers_count}') +STARS=$(echo $REPO_STATS | jq -r '.stars') +FORKS=$(echo $REPO_STATS | jq -r '.forks') +ISSUES=$(echo $REPO_STATS | jq -r '.issues') +WATCHERS=$(echo $REPO_STATS | jq -r '.watchers') + +echo "⭐ Stars: $STARS | šŸ“ Forks: $FORKS | šŸ“‹ Open Issues: $ISSUES | šŸ‘€ Watchers: $WATCHERS" +echo "" + +# 3. Mergeable PRs +echo -e "${BOLD}${DISCORD_BLUE}āœ… MERGEABLE PRs (Ready to Merge)${RESET}" +echo "─────────────────────────────────────────" +MERGEABLE_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,mergeable,reviewDecision --jq '.[] | select(.mergeable == "MERGEABLE") | "PR #\(.number): \(.title) by @\(.author.login)"') + +if [ -z "$MERGEABLE_PRS" ]; then + echo "No PRs currently ready to merge" +else + echo "$MERGEABLE_PRS" +fi +echo "" + +# 4. PRs Needing Review +echo -e "${BOLD}${DISCORD_BLUE}šŸ‘€ PRs NEEDING REVIEW${RESET}" +echo "─────────────────────────────────────────" +REVIEW_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,reviewDecision --jq '.[] | select(.reviewDecision == "REVIEW_REQUIRED" or .reviewDecision == "") | "PR #\(.number): \(.title) by @\(.author.login)"' | head -10) + +if [ -z "$REVIEW_PRS" ]; then + echo "No PRs awaiting review" +else + echo "$REVIEW_PRS" +fi +echo "" + +# 5. Recently Merged PRs (potential bounty payments) +echo -e "${BOLD}${DISCORD_YELLOW}šŸ’° RECENTLY MERGED (Check Bounty Payments)${RESET}" +echo "─────────────────────────────────────────" +MERGED_PRS=$(gh pr list --repo $REPO --state merged --limit 10 --json number,title,author,mergedAt,labels --jq '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Merged: \(.mergedAt[:10])"') + +if [ -z "$MERGED_PRS" ]; then + echo "No recently merged PRs" +else + echo "$MERGED_PRS" +fi +echo "" + +# 6. Open Issues with Bounties +echo -e "${BOLD}${DISCORD_BLUE}šŸŽÆ OPEN ISSUES WITH BOUNTIES${RESET}" +echo "─────────────────────────────────────────" +BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --label "bounty" --json number,title,labels --jq '.[] | "#\(.number): \(.title)"' 2>/dev/null || echo "") + +if [ -z "$BOUNTY_ISSUES" ]; then + # Try to find issues that might have bounty in title or other bounty-related labels + BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --json number,title,labels --jq '.[] | select(.title | test("bounty|\\$"; "i")) | "#\(.number): \(.title)"' 2>/dev/null || echo "No bounty issues found") +fi + +if [ -z "$BOUNTY_ISSUES" ] || [ "$BOUNTY_ISSUES" = "No bounty issues found" ]; then + echo "No issues with bounty labels found" + echo "Tip: Add 'bounty' label to issues to track them here" +else + echo "$BOUNTY_ISSUES" +fi +echo "" + +# 7. Top Contributors (from recent PRs) +echo -e "${BOLD}${DISCORD_BLUE}šŸ‘„ ACTIVE CONTRIBUTORS (Recent PRs)${RESET}" +echo "─────────────────────────────────────────" +CONTRIBUTORS=$(gh pr list --repo $REPO --state all --limit 50 --json author --jq '.[].author.login' | sort | uniq -c | sort -rn | head -5) +echo "$CONTRIBUTORS" +echo "" + +# 8. Discord Recruitment Message +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo -e "${BOLD}${DISCORD_CYAN} DISCORD RECRUITMENT MESSAGE (Copy Below)${RESET}" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo "" + +# Count open PRs and issues for the message +OPEN_PRS=$(gh pr list --repo $REPO --state open --json number --jq 'length') +OPEN_ISSUES_COUNT=$(gh issue list --repo $REPO --state open --json number --jq 'length') + +cat << 'DISCORD_MSG' +``` +šŸš€ CORTEX LINUX - OPEN SOURCE CONTRIBUTORS WANTED! šŸš€ +``` + +**What is Cortex Linux?** +An AI-powered package manager for Debian/Ubuntu that understands natural language. Instead of memorizing apt commands, just tell Cortex what you want: + +```bash +cortex install "set up a Python ML environment with TensorFlow" +``` + +**Why Contribute?** +DISCORD_MSG + +echo "- šŸ’° **Bounty Program** - Get paid for merged PRs" +echo "- 🌟 **$STARS stars** and growing" +echo "- šŸ”„ **$OPEN_PRS open PRs** to review/merge" +echo "- šŸ“‹ **$OPEN_ISSUES_COUNT open issues** to work on" +echo "- šŸ¤ Friendly community, fast PR reviews" + +cat << 'DISCORD_MSG' + +**Good First Issues:** +- Documentation improvements +- Test coverage +- Bug fixes with clear reproduction steps + +**Tech Stack:** +- Python 3.10+ +- OpenAI/Anthropic APIs +- Rich TUI library + +**Links:** +DISCORD_MSG + +echo "- GitHub: https://github.com/$REPO" +echo "- Discussions: https://github.com/$REPO/discussions" + +cat << 'DISCORD_MSG' + +**How to Start:** +1. Fork the repo +2. Pick an issue labeled `good-first-issue` or `help-wanted` +3. Submit a PR +4. Get reviewed & merged! + +Drop a šŸ‘‹ if you're interested or have questions! +DISCORD_MSG + +echo "" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo -e "${BOLD}Generated: $(date)${RESET}" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" diff --git a/scripts/verify_ubuntu_compatibility.py b/scripts/verify_ubuntu_compatibility.py index 87555e84..629e2dd1 100644 --- a/scripts/verify_ubuntu_compatibility.py +++ b/scripts/verify_ubuntu_compatibility.py @@ -36,12 +36,12 @@ def save_history(score, status, details): "timestamp": datetime.datetime.now().isoformat(), "score": score, "status": status, - "details": details + "details": details, } history.append(record) history = history[-10:] - with open(HISTORY_FILE, 'w') as f: + with open(HISTORY_FILE, "w") as f: json.dump(history, f, indent=4) return history @@ -90,7 +90,7 @@ def fix_firewall(): check=True, timeout=30, capture_output=True, - text=True + text=True, ) print(" -> āœ… Success: Firewall enabled.") return True @@ -145,7 +145,7 @@ def fix_ssh_config(config_path): # Atomic write using temporary file dir_path = os.path.dirname(config_path) with tempfile.NamedTemporaryFile( - mode='w', dir=dir_path, delete=False, suffix='.tmp' + mode="w", dir=dir_path, delete=False, suffix=".tmp" ) as tmp_file: tmp_file.writelines(new_lines) tmp_path = tmp_file.name @@ -159,7 +159,7 @@ def fix_ssh_config(config_path): [SUDO_CMD, "-n", SYSTEMCTL_CMD, "restart", SSH_SERVICE], capture_output=True, text=True, - timeout=30 + timeout=30, ) if res.returncode != 0: print(f" -> āš ļø SSH restart failed: {res.stderr}") @@ -183,10 +183,7 @@ def _check_firewall_status(): try: print(f" Running: {SYSTEMCTL_CMD} is-active ufw") res = subprocess.run( - [SYSTEMCTL_CMD, "is-active", "ufw"], - capture_output=True, - text=True, - timeout=10 + [SYSTEMCTL_CMD, "is-active", "ufw"], capture_output=True, text=True, timeout=10 ) output = res.stdout.strip() print(f" Output: '{output}'") @@ -278,7 +275,7 @@ def verify_security_logic(): print("Issues detected that can be automatically fixed.") user_input = input("Do you want to apply fixes now? (y/n): ").strip().lower() - if user_input == 'y': + if user_input == "y": if ufw_needs_fix: fix_firewall() if ssh_needs_fix: diff --git a/tests/test_health_monitor.py b/tests/test_health_monitor.py index 35605e93..0bea6d5f 100644 --- a/tests/test_health_monitor.py +++ b/tests/test_health_monitor.py @@ -9,7 +9,7 @@ class TestDiskCheck(unittest.TestCase): - @patch('shutil.disk_usage') + @patch("shutil.disk_usage") def test_disk_usage_scoring(self, mock_usage): # Case 1: Healthy (50% used) -> 100 pts # total=100, used=50, free=50 @@ -33,8 +33,8 @@ def test_disk_usage_scoring(self, mock_usage): class TestPerformanceCheck(unittest.TestCase): - @patch('os.getloadavg') - @patch('multiprocessing.cpu_count') + @patch("os.getloadavg") + @patch("multiprocessing.cpu_count") def test_load_average(self, mock_cpu, mock_load): # Case 1: Load OK (Load 2.0 / 4 Cores = 0.5 ratio) mock_cpu.return_value = 4 @@ -42,13 +42,13 @@ def test_load_average(self, mock_cpu, mock_load): # Mock reading /proc/meminfo (Normal case) mem_data = "MemTotal: 1000 kB\nMemAvailable: 500 kB\n" - with patch('builtins.open', mock_open(read_data=mem_data)): + with patch("builtins.open", mock_open(read_data=mem_data)): check = PerformanceCheck() result = check.run() self.assertEqual(result.score, 100) # No penalty - @patch('os.getloadavg') - @patch('multiprocessing.cpu_count') + @patch("os.getloadavg") + @patch("multiprocessing.cpu_count") def test_high_load_penalty(self, mock_cpu, mock_load): # Case 2: High Load (Load 5.0 / 4 Cores = 1.25 ratio) -> -50 pts mock_cpu.return_value = 4 @@ -56,14 +56,14 @@ def test_high_load_penalty(self, mock_cpu, mock_load): # Assume memory is normal mem_data = "MemTotal: 1000 kB\nMemAvailable: 500 kB\n" - with patch('builtins.open', mock_open(read_data=mem_data)): + with patch("builtins.open", mock_open(read_data=mem_data)): check = PerformanceCheck() result = check.run() self.assertEqual(result.score, 50) # 100 - 50 = 50 class TestSecurityCheck(unittest.TestCase): - @patch('subprocess.run') + @patch("subprocess.run") def test_ufw_status(self, mock_run): # Case 1: UFW Inactive -> 0 pts mock_run.return_value.stdout = "inactive" @@ -74,21 +74,21 @@ def test_ufw_status(self, mock_run): self.assertEqual(result.score, 0) self.assertIn("Firewall Inactive", result.details) - @patch('subprocess.run') + @patch("subprocess.run") def test_ufw_active(self, mock_run): # Case 2: UFW Active -> 100 pts (SSH config is safe by default mock) mock_run.return_value.stdout = "active" mock_run.return_value.returncode = 0 # Test error handling when sshd_config does not exist - with patch('os.path.exists', return_value=False): + with patch("os.path.exists", return_value=False): check = SecurityCheck() result = check.run() self.assertEqual(result.score, 100) class TestUpdateCheck(unittest.TestCase): - @patch('subprocess.run') + @patch("subprocess.run") def test_apt_updates(self, mock_run): # Mock output for apt list --upgradable # Ignore first line, packages start from 2nd line @@ -131,14 +131,14 @@ def test_monitor_aggregation(self): monitor.checks = [mock_check1, mock_check2] # Mock history saving to prevent file write - with patch.object(monitor, '_save_history'): + with patch.object(monitor, "_save_history"): report = monitor.run_all() # Weighted average calculation: # (100 * 0.5) + (0 * 0.5) = 50 / (0.5 + 0.5) = 50 pts - self.assertEqual(report['total_score'], 50) - self.assertEqual(len(report['results']), 2) + self.assertEqual(report["total_score"], 50) + self.assertEqual(len(report["results"]), 2) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From dea26091431c050a8d54ba12d2fa2caf100bbc95 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 19:31:03 +0900 Subject: [PATCH 15/18] fix: correct expected score in test_apt_updates test --- tests/test_health_monitor.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_health_monitor.py b/tests/test_health_monitor.py index 0bea6d5f..92fbaba8 100644 --- a/tests/test_health_monitor.py +++ b/tests/test_health_monitor.py @@ -104,12 +104,13 @@ def test_apt_updates(self, mock_run): result = check.run() # Calculation: - # Total packages: 3 - # Security packages: 1 (line containing "security") - # Penalty: (3 * 2) + (1 * 10) = 6 + 10 = 16 pts - # Expected score: 100 - 16 = 84 pts + # Total packages: 3 (but security counted separately) + # Regular packages: 2 (pkg_count) + # Security packages: 1 (sec_count) + # Penalty: (2 * 2) + (1 * 10) = 4 + 10 = 14 pts + # Expected score: 100 - 14 = 86 pts - self.assertEqual(result.score, 84) + self.assertEqual(result.score, 86) self.assertIn("3 pending", result.details) From ef9a253816f4facde6afa4e9624a1e13e6dbec84 Mon Sep 17 00:00:00 2001 From: hyaku0121 Date: Wed, 17 Dec 2025 19:36:19 +0900 Subject: [PATCH 16/18] fix: correct test assertion and restore files to main state --- cortex/hwprofiler.py | 960 +++++------ cortex/utils/commands.py | 658 ++++---- .../automation/cortex-master-quarterback.sh | 1424 ++++++++--------- scripts/automation/cortex-master-update.sh | 602 +++---- scripts/automation/cortex-master.sh | 388 ++--- scripts/automation/cortex-pr-dashboard.sh | 724 ++++----- scripts/automation/focus-on-mvp.sh | 210 +-- scripts/automation/manage_cortex_prs.sh | 870 +++++----- scripts/cortex-cleanup.sh | 294 ++-- scripts/demo_script.sh | 460 +++--- scripts/deployment/audit_cortex_status.sh | 216 +-- scripts/deployment/upload_issue_34.sh | 72 +- scripts/fetch-fork-emails.sh | 144 +- scripts/github/merge-mike-prs.sh | 162 +- scripts/github/organize-issues.sh | 102 +- scripts/github/review-contributor-prs.sh | 628 ++++---- scripts/recruit-ready.sh | 350 ++-- tests/test_health_monitor.py | 2 +- 18 files changed, 4133 insertions(+), 4133 deletions(-) mode change 100644 => 100755 cortex/hwprofiler.py mode change 100644 => 100755 scripts/automation/cortex-master-quarterback.sh mode change 100644 => 100755 scripts/automation/cortex-master-update.sh mode change 100644 => 100755 scripts/automation/cortex-master.sh mode change 100644 => 100755 scripts/automation/cortex-pr-dashboard.sh mode change 100644 => 100755 scripts/automation/focus-on-mvp.sh mode change 100644 => 100755 scripts/automation/manage_cortex_prs.sh mode change 100644 => 100755 scripts/cortex-cleanup.sh mode change 100644 => 100755 scripts/demo_script.sh mode change 100644 => 100755 scripts/deployment/audit_cortex_status.sh mode change 100644 => 100755 scripts/deployment/upload_issue_34.sh mode change 100644 => 100755 scripts/fetch-fork-emails.sh mode change 100644 => 100755 scripts/github/merge-mike-prs.sh mode change 100644 => 100755 scripts/github/organize-issues.sh mode change 100644 => 100755 scripts/github/review-contributor-prs.sh mode change 100644 => 100755 scripts/recruit-ready.sh diff --git a/cortex/hwprofiler.py b/cortex/hwprofiler.py old mode 100644 new mode 100755 index 0df9f1db..d3dcd7e2 --- a/cortex/hwprofiler.py +++ b/cortex/hwprofiler.py @@ -1,480 +1,480 @@ -#!/usr/bin/env python3 -""" -Hardware Profiling System for Cortex Linux -Detects CPU, GPU, RAM, storage, and network capabilities. -""" - -import json -import os -import re -import subprocess -from typing import Any - - -class HardwareProfiler: - """Detects and profiles system hardware.""" - - def __init__(self): - self.cpu_info = None - self.gpu_info = [] - self.ram_info = None - self.storage_info = [] - self.network_info = None - - def detect_cpu(self) -> dict[str, Any]: - """ - Detect CPU information: model, cores, architecture. - - Returns: - dict: CPU information with model, cores, and architecture - """ - cpu_info = {} - - try: - # Read /proc/cpuinfo for CPU details - with open("/proc/cpuinfo") as f: - cpuinfo = f.read() - - # Extract model name - model_match = re.search(r"model name\s*:\s*(.+)", cpuinfo) - if model_match: - cpu_info["model"] = model_match.group(1).strip() - else: - # Fallback for ARM or other architectures - model_match = re.search(r"Processor\s*:\s*(.+)", cpuinfo) - if model_match: - cpu_info["model"] = model_match.group(1).strip() - else: - cpu_info["model"] = "Unknown CPU" - - # Count physical cores - physical_cores = 0 - core_ids = set() - for line in cpuinfo.split("\n"): - if line.startswith("core id"): - core_id = line.split(":")[1].strip() - if core_id: - core_ids.add(core_id) - elif line.startswith("physical id"): - physical_cores = len(core_ids) if core_ids else 0 - - # If we couldn't get physical cores, count logical cores - if physical_cores == 0: - logical_cores = len([l for l in cpuinfo.split("\n") if l.startswith("processor")]) - cpu_info["cores"] = logical_cores - else: - # Get number of physical CPUs - physical_ids = set() - for line in cpuinfo.split("\n"): - if line.startswith("physical id"): - pid = line.split(":")[1].strip() - if pid: - physical_ids.add(pid) - cpu_info["cores"] = len(physical_ids) * len(core_ids) if core_ids else len(core_ids) - - # Fallback: use nproc if available - if cpu_info.get("cores", 0) == 0: - try: - result = subprocess.run(["nproc"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - cpu_info["cores"] = int(result.stdout.strip()) - except (subprocess.TimeoutExpired, ValueError, FileNotFoundError): - pass - - # Detect architecture - try: - result = subprocess.run(["uname", "-m"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - arch = result.stdout.strip() - cpu_info["architecture"] = arch - else: - cpu_info["architecture"] = "unknown" - except (subprocess.TimeoutExpired, FileNotFoundError): - cpu_info["architecture"] = "unknown" - - except Exception as e: - cpu_info = {"model": "Unknown", "cores": 0, "architecture": "unknown", "error": str(e)} - - self.cpu_info = cpu_info - return cpu_info - - def detect_gpu(self) -> list[dict[str, Any]]: - """ - Detect GPU information: vendor, model, VRAM, CUDA version. - - Returns: - list: List of GPU information dictionaries - """ - gpus = [] - - # Detect NVIDIA GPUs - try: - result = subprocess.run( - [ - "nvidia-smi", - "--query-gpu=name,memory.total,driver_version", - "--format=csv,noheader,nounits", - ], - capture_output=True, - text=True, - timeout=2, - ) - if result.returncode == 0: - for line in result.stdout.strip().split("\n"): - if line.strip(): - parts = [p.strip() for p in line.split(",")] - if len(parts) >= 2: - gpu_name = parts[0] - vram_mb = int(parts[1]) if parts[1].isdigit() else 0 - - gpu_info = {"vendor": "NVIDIA", "model": gpu_name, "vram": vram_mb} - - # Try to get CUDA version - try: - cuda_result = subprocess.run( - [ - "nvidia-smi", - "--query-gpu=cuda_version", - "--format=csv,noheader", - ], - capture_output=True, - text=True, - timeout=1, - ) - if cuda_result.returncode == 0 and cuda_result.stdout.strip(): - gpu_info["cuda"] = cuda_result.stdout.strip() - except (subprocess.TimeoutExpired, FileNotFoundError, ValueError): - # Try nvcc as fallback - try: - nvcc_result = subprocess.run( - ["nvcc", "--version"], - capture_output=True, - text=True, - timeout=1, - ) - if nvcc_result.returncode == 0: - version_match = re.search( - r"release (\d+\.\d+)", nvcc_result.stdout - ) - if version_match: - gpu_info["cuda"] = version_match.group(1) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - gpus.append(gpu_info) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - # Detect AMD GPUs using lspci - try: - result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - for line in result.stdout.split("\n"): - if "VGA" in line or "Display" in line: - if "AMD" in line or "ATI" in line or "Radeon" in line: - # Extract model name - model_match = re.search( - r"(?:AMD|ATI|Radeon)[\s/]+([A-Za-z0-9\s]+)", line - ) - model = ( - model_match.group(1).strip() if model_match else "Unknown AMD GPU" - ) - - # Check if we already have this GPU (avoid duplicates) - if not any( - g.get("vendor") == "AMD" and g.get("model") == model for g in gpus - ): - gpu_info = { - "vendor": "AMD", - "model": model, - "vram": None, # AMD VRAM detection requires rocm-smi or other tools - } - - # Try to get VRAM using rocm-smi if available - try: - rocm_result = subprocess.run( - ["rocm-smi", "--showmeminfo", "vram"], - capture_output=True, - text=True, - timeout=1, - ) - if rocm_result.returncode == 0: - # Parse VRAM from rocm-smi output - vram_match = re.search(r"(\d+)\s*MB", rocm_result.stdout) - if vram_match: - gpu_info["vram"] = int(vram_match.group(1)) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - gpus.append(gpu_info) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - # Detect Intel GPUs - try: - result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) - if result.returncode == 0: - for line in result.stdout.split("\n"): - if "VGA" in line or "Display" in line: - if "Intel" in line: - model_match = re.search(r"Intel[^:]*:\s*([^\(]+)", line) - model = ( - model_match.group(1).strip() if model_match else "Unknown Intel GPU" - ) - - if not any( - g.get("vendor") == "Intel" and g.get("model") == model for g in gpus - ): - gpus.append( - { - "vendor": "Intel", - "model": model, - "vram": None, # Intel integrated GPUs share system RAM - } - ) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - self.gpu_info = gpus - return gpus - - def detect_ram(self) -> int: - """ - Detect total RAM in MB. - - Returns: - int: Total RAM in MB - """ - try: - # Read /proc/meminfo - with open("/proc/meminfo") as f: - meminfo = f.read() - - # Extract MemTotal - match = re.search(r"MemTotal:\s+(\d+)\s+kB", meminfo) - if match: - ram_kb = int(match.group(1)) - ram_mb = ram_kb // 1024 - self.ram_info = ram_mb - return ram_mb - else: - self.ram_info = 0 - return 0 - except Exception: - self.ram_info = 0 - return 0 - - def detect_storage(self) -> list[dict[str, Any]]: - """ - Detect storage devices: type and size. - - Returns: - list: List of storage device information - """ - storage_devices = [] - - try: - # Use lsblk to get block device information - result = subprocess.run( - ["lsblk", "-d", "-o", "NAME,TYPE,SIZE", "-n"], - capture_output=True, - text=True, - timeout=2, - ) - - if result.returncode == 0: - for line in result.stdout.strip().split("\n"): - if line.strip(): - parts = line.split() - if len(parts) >= 2: - device_name = parts[0] - - # Skip loop devices and other virtual devices - if device_name.startswith("loop") or device_name.startswith("ram"): - continue - - device_type = parts[1] if len(parts) > 1 else "unknown" - size_str = parts[2] if len(parts) > 2 else "0" - - # Convert size to MB - size_mb = 0 - if "G" in size_str.upper(): - size_mb = int( - float( - re.sub( - r"[^0-9.]", - "", - size_str.replace("G", "").replace("g", ""), - ) - ) - * 1024 - ) - elif "T" in size_str.upper(): - size_mb = int( - float( - re.sub( - r"[^0-9.]", - "", - size_str.replace("T", "").replace("t", ""), - ) - ) - * 1024 - * 1024 - ) - elif "M" in size_str.upper(): - size_mb = int( - float( - re.sub( - r"[^0-9.]", - "", - size_str.replace("M", "").replace("m", ""), - ) - ) - ) - - # Determine storage type - storage_type = "unknown" - device_path = f"/sys/block/{device_name}" - - # Check if it's NVMe - if "nvme" in device_name.lower(): - storage_type = "nvme" - # Check if it's SSD (by checking if it's rotational) - elif os.path.exists(f"{device_path}/queue/rotational"): - try: - with open(f"{device_path}/queue/rotational") as f: - is_rotational = f.read().strip() == "1" - storage_type = "hdd" if is_rotational else "ssd" - except Exception: - storage_type = "unknown" - else: - # Fallback: guess based on device name - if "sd" in device_name.lower(): - storage_type = "hdd" # Default assumption - elif "nvme" in device_name.lower(): - storage_type = "nvme" - - storage_devices.append( - {"type": storage_type, "size": size_mb, "device": device_name} - ) - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - self.storage_info = storage_devices - return storage_devices - - def detect_network(self) -> dict[str, Any]: - """ - Detect network capabilities. - - Returns: - dict: Network information including interfaces and speeds - """ - network_info = {"interfaces": [], "max_speed_mbps": 0} - - try: - # Get network interfaces using ip command - result = subprocess.run( - ["ip", "-o", "link", "show"], capture_output=True, text=True, timeout=1 - ) - - if result.returncode == 0: - interfaces = [] - for line in result.stdout.split("\n"): - if ": " in line: - parts = line.split(": ") - if len(parts) >= 2: - interface_name = ( - parts[1].split("@")[0].split()[0] - if "@" in parts[1] - else parts[1].split()[0] - ) - - # Skip loopback - if interface_name == "lo": - continue - - # Try to get interface speed - speed = None - try: - speed_path = f"/sys/class/net/{interface_name}/speed" - if os.path.exists(speed_path): - with open(speed_path) as f: - speed_str = f.read().strip() - if speed_str.isdigit(): - speed = int(speed_str) - except Exception: - pass - - interfaces.append({"name": interface_name, "speed_mbps": speed}) - - if speed and speed > network_info["max_speed_mbps"]: - network_info["max_speed_mbps"] = speed - - network_info["interfaces"] = interfaces - except (subprocess.TimeoutExpired, FileNotFoundError): - pass - - self.network_info = network_info - return network_info - - def profile(self) -> dict[str, Any]: - """ - Run complete hardware profiling. - - Returns: - dict: Complete hardware profile in JSON format - """ - # Run all detection methods - cpu = self.detect_cpu() - gpu = self.detect_gpu() - ram = self.detect_ram() - storage = self.detect_storage() - network = self.detect_network() - - # Build result dictionary - result = { - "cpu": { - "model": cpu.get("model", "Unknown"), - "cores": cpu.get("cores", 0), - "architecture": cpu.get("architecture", "unknown"), - }, - "gpu": gpu, - "ram": ram, - "storage": storage, - "network": network, - } - - return result - - def to_json(self, indent: int = 2) -> str: - """ - Convert hardware profile to JSON string. - - Args: - indent: JSON indentation level - - Returns: - str: JSON string representation - """ - profile = self.profile() - return json.dumps(profile, indent=indent) - - -def main(): - """CLI entry point for hardware profiler.""" - import sys - - profiler = HardwareProfiler() - - try: - profile = profiler.profile() - print(profiler.to_json()) - sys.exit(0) - except Exception as e: - print(json.dumps({"error": str(e)}, indent=2), file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() +#!/usr/bin/env python3 +""" +Hardware Profiling System for Cortex Linux +Detects CPU, GPU, RAM, storage, and network capabilities. +""" + +import json +import os +import re +import subprocess +from typing import Any + + +class HardwareProfiler: + """Detects and profiles system hardware.""" + + def __init__(self): + self.cpu_info = None + self.gpu_info = [] + self.ram_info = None + self.storage_info = [] + self.network_info = None + + def detect_cpu(self) -> dict[str, Any]: + """ + Detect CPU information: model, cores, architecture. + + Returns: + dict: CPU information with model, cores, and architecture + """ + cpu_info = {} + + try: + # Read /proc/cpuinfo for CPU details + with open("/proc/cpuinfo") as f: + cpuinfo = f.read() + + # Extract model name + model_match = re.search(r"model name\s*:\s*(.+)", cpuinfo) + if model_match: + cpu_info["model"] = model_match.group(1).strip() + else: + # Fallback for ARM or other architectures + model_match = re.search(r"Processor\s*:\s*(.+)", cpuinfo) + if model_match: + cpu_info["model"] = model_match.group(1).strip() + else: + cpu_info["model"] = "Unknown CPU" + + # Count physical cores + physical_cores = 0 + core_ids = set() + for line in cpuinfo.split("\n"): + if line.startswith("core id"): + core_id = line.split(":")[1].strip() + if core_id: + core_ids.add(core_id) + elif line.startswith("physical id"): + physical_cores = len(core_ids) if core_ids else 0 + + # If we couldn't get physical cores, count logical cores + if physical_cores == 0: + logical_cores = len([l for l in cpuinfo.split("\n") if l.startswith("processor")]) + cpu_info["cores"] = logical_cores + else: + # Get number of physical CPUs + physical_ids = set() + for line in cpuinfo.split("\n"): + if line.startswith("physical id"): + pid = line.split(":")[1].strip() + if pid: + physical_ids.add(pid) + cpu_info["cores"] = len(physical_ids) * len(core_ids) if core_ids else len(core_ids) + + # Fallback: use nproc if available + if cpu_info.get("cores", 0) == 0: + try: + result = subprocess.run(["nproc"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + cpu_info["cores"] = int(result.stdout.strip()) + except (subprocess.TimeoutExpired, ValueError, FileNotFoundError): + pass + + # Detect architecture + try: + result = subprocess.run(["uname", "-m"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + arch = result.stdout.strip() + cpu_info["architecture"] = arch + else: + cpu_info["architecture"] = "unknown" + except (subprocess.TimeoutExpired, FileNotFoundError): + cpu_info["architecture"] = "unknown" + + except Exception as e: + cpu_info = {"model": "Unknown", "cores": 0, "architecture": "unknown", "error": str(e)} + + self.cpu_info = cpu_info + return cpu_info + + def detect_gpu(self) -> list[dict[str, Any]]: + """ + Detect GPU information: vendor, model, VRAM, CUDA version. + + Returns: + list: List of GPU information dictionaries + """ + gpus = [] + + # Detect NVIDIA GPUs + try: + result = subprocess.run( + [ + "nvidia-smi", + "--query-gpu=name,memory.total,driver_version", + "--format=csv,noheader,nounits", + ], + capture_output=True, + text=True, + timeout=2, + ) + if result.returncode == 0: + for line in result.stdout.strip().split("\n"): + if line.strip(): + parts = [p.strip() for p in line.split(",")] + if len(parts) >= 2: + gpu_name = parts[0] + vram_mb = int(parts[1]) if parts[1].isdigit() else 0 + + gpu_info = {"vendor": "NVIDIA", "model": gpu_name, "vram": vram_mb} + + # Try to get CUDA version + try: + cuda_result = subprocess.run( + [ + "nvidia-smi", + "--query-gpu=cuda_version", + "--format=csv,noheader", + ], + capture_output=True, + text=True, + timeout=1, + ) + if cuda_result.returncode == 0 and cuda_result.stdout.strip(): + gpu_info["cuda"] = cuda_result.stdout.strip() + except (subprocess.TimeoutExpired, FileNotFoundError, ValueError): + # Try nvcc as fallback + try: + nvcc_result = subprocess.run( + ["nvcc", "--version"], + capture_output=True, + text=True, + timeout=1, + ) + if nvcc_result.returncode == 0: + version_match = re.search( + r"release (\d+\.\d+)", nvcc_result.stdout + ) + if version_match: + gpu_info["cuda"] = version_match.group(1) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + gpus.append(gpu_info) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + # Detect AMD GPUs using lspci + try: + result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + for line in result.stdout.split("\n"): + if "VGA" in line or "Display" in line: + if "AMD" in line or "ATI" in line or "Radeon" in line: + # Extract model name + model_match = re.search( + r"(?:AMD|ATI|Radeon)[\s/]+([A-Za-z0-9\s]+)", line + ) + model = ( + model_match.group(1).strip() if model_match else "Unknown AMD GPU" + ) + + # Check if we already have this GPU (avoid duplicates) + if not any( + g.get("vendor") == "AMD" and g.get("model") == model for g in gpus + ): + gpu_info = { + "vendor": "AMD", + "model": model, + "vram": None, # AMD VRAM detection requires rocm-smi or other tools + } + + # Try to get VRAM using rocm-smi if available + try: + rocm_result = subprocess.run( + ["rocm-smi", "--showmeminfo", "vram"], + capture_output=True, + text=True, + timeout=1, + ) + if rocm_result.returncode == 0: + # Parse VRAM from rocm-smi output + vram_match = re.search(r"(\d+)\s*MB", rocm_result.stdout) + if vram_match: + gpu_info["vram"] = int(vram_match.group(1)) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + gpus.append(gpu_info) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + # Detect Intel GPUs + try: + result = subprocess.run(["lspci"], capture_output=True, text=True, timeout=1) + if result.returncode == 0: + for line in result.stdout.split("\n"): + if "VGA" in line or "Display" in line: + if "Intel" in line: + model_match = re.search(r"Intel[^:]*:\s*([^\(]+)", line) + model = ( + model_match.group(1).strip() if model_match else "Unknown Intel GPU" + ) + + if not any( + g.get("vendor") == "Intel" and g.get("model") == model for g in gpus + ): + gpus.append( + { + "vendor": "Intel", + "model": model, + "vram": None, # Intel integrated GPUs share system RAM + } + ) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + self.gpu_info = gpus + return gpus + + def detect_ram(self) -> int: + """ + Detect total RAM in MB. + + Returns: + int: Total RAM in MB + """ + try: + # Read /proc/meminfo + with open("/proc/meminfo") as f: + meminfo = f.read() + + # Extract MemTotal + match = re.search(r"MemTotal:\s+(\d+)\s+kB", meminfo) + if match: + ram_kb = int(match.group(1)) + ram_mb = ram_kb // 1024 + self.ram_info = ram_mb + return ram_mb + else: + self.ram_info = 0 + return 0 + except Exception: + self.ram_info = 0 + return 0 + + def detect_storage(self) -> list[dict[str, Any]]: + """ + Detect storage devices: type and size. + + Returns: + list: List of storage device information + """ + storage_devices = [] + + try: + # Use lsblk to get block device information + result = subprocess.run( + ["lsblk", "-d", "-o", "NAME,TYPE,SIZE", "-n"], + capture_output=True, + text=True, + timeout=2, + ) + + if result.returncode == 0: + for line in result.stdout.strip().split("\n"): + if line.strip(): + parts = line.split() + if len(parts) >= 2: + device_name = parts[0] + + # Skip loop devices and other virtual devices + if device_name.startswith("loop") or device_name.startswith("ram"): + continue + + device_type = parts[1] if len(parts) > 1 else "unknown" + size_str = parts[2] if len(parts) > 2 else "0" + + # Convert size to MB + size_mb = 0 + if "G" in size_str.upper(): + size_mb = int( + float( + re.sub( + r"[^0-9.]", + "", + size_str.replace("G", "").replace("g", ""), + ) + ) + * 1024 + ) + elif "T" in size_str.upper(): + size_mb = int( + float( + re.sub( + r"[^0-9.]", + "", + size_str.replace("T", "").replace("t", ""), + ) + ) + * 1024 + * 1024 + ) + elif "M" in size_str.upper(): + size_mb = int( + float( + re.sub( + r"[^0-9.]", + "", + size_str.replace("M", "").replace("m", ""), + ) + ) + ) + + # Determine storage type + storage_type = "unknown" + device_path = f"/sys/block/{device_name}" + + # Check if it's NVMe + if "nvme" in device_name.lower(): + storage_type = "nvme" + # Check if it's SSD (by checking if it's rotational) + elif os.path.exists(f"{device_path}/queue/rotational"): + try: + with open(f"{device_path}/queue/rotational") as f: + is_rotational = f.read().strip() == "1" + storage_type = "hdd" if is_rotational else "ssd" + except Exception: + storage_type = "unknown" + else: + # Fallback: guess based on device name + if "sd" in device_name.lower(): + storage_type = "hdd" # Default assumption + elif "nvme" in device_name.lower(): + storage_type = "nvme" + + storage_devices.append( + {"type": storage_type, "size": size_mb, "device": device_name} + ) + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + self.storage_info = storage_devices + return storage_devices + + def detect_network(self) -> dict[str, Any]: + """ + Detect network capabilities. + + Returns: + dict: Network information including interfaces and speeds + """ + network_info = {"interfaces": [], "max_speed_mbps": 0} + + try: + # Get network interfaces using ip command + result = subprocess.run( + ["ip", "-o", "link", "show"], capture_output=True, text=True, timeout=1 + ) + + if result.returncode == 0: + interfaces = [] + for line in result.stdout.split("\n"): + if ": " in line: + parts = line.split(": ") + if len(parts) >= 2: + interface_name = ( + parts[1].split("@")[0].split()[0] + if "@" in parts[1] + else parts[1].split()[0] + ) + + # Skip loopback + if interface_name == "lo": + continue + + # Try to get interface speed + speed = None + try: + speed_path = f"/sys/class/net/{interface_name}/speed" + if os.path.exists(speed_path): + with open(speed_path) as f: + speed_str = f.read().strip() + if speed_str.isdigit(): + speed = int(speed_str) + except Exception: + pass + + interfaces.append({"name": interface_name, "speed_mbps": speed}) + + if speed and speed > network_info["max_speed_mbps"]: + network_info["max_speed_mbps"] = speed + + network_info["interfaces"] = interfaces + except (subprocess.TimeoutExpired, FileNotFoundError): + pass + + self.network_info = network_info + return network_info + + def profile(self) -> dict[str, Any]: + """ + Run complete hardware profiling. + + Returns: + dict: Complete hardware profile in JSON format + """ + # Run all detection methods + cpu = self.detect_cpu() + gpu = self.detect_gpu() + ram = self.detect_ram() + storage = self.detect_storage() + network = self.detect_network() + + # Build result dictionary + result = { + "cpu": { + "model": cpu.get("model", "Unknown"), + "cores": cpu.get("cores", 0), + "architecture": cpu.get("architecture", "unknown"), + }, + "gpu": gpu, + "ram": ram, + "storage": storage, + "network": network, + } + + return result + + def to_json(self, indent: int = 2) -> str: + """ + Convert hardware profile to JSON string. + + Args: + indent: JSON indentation level + + Returns: + str: JSON string representation + """ + profile = self.profile() + return json.dumps(profile, indent=indent) + + +def main(): + """CLI entry point for hardware profiler.""" + import sys + + profiler = HardwareProfiler() + + try: + profile = profiler.profile() + print(profiler.to_json()) + sys.exit(0) + except Exception as e: + print(json.dumps({"error": str(e)}, indent=2), file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/cortex/utils/commands.py b/cortex/utils/commands.py index 77fde35f..2a431dbc 100644 --- a/cortex/utils/commands.py +++ b/cortex/utils/commands.py @@ -1,329 +1,329 @@ -""" -Secure Command Execution Utilities - -This module provides safe command execution with validation and sandboxing. -All commands should go through these utilities to prevent shell injection. -""" - -import logging -import re -import shlex -import subprocess -from dataclasses import dataclass - -logger = logging.getLogger(__name__) - -# Dangerous patterns that should never be executed -DANGEROUS_PATTERNS = [ - # File system destruction - r"rm\s+-rf\s+[/\*]", - r"rm\s+-rf\s+\$", - r"rm\s+--no-preserve-root", - r":\s*\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}", # Fork bomb - # Disk operations - r"dd\s+if=.*of=/dev/", - r"mkfs\.", - r"wipefs", - # Network attacks - r"curl\s+.*\|\s*sh", - r"curl\s+.*\|\s*bash", - r"wget\s+.*\|\s*sh", - r"wget\s+.*\|\s*bash", - r"curl\s+-o\s+-\s+.*\|\s*", - # Code execution - r"\beval\s+", - r'python\s+-c\s+["\'].*exec', - r'python\s+-c\s+["\'].*import\s+os', - r"base64\s+-d\s+.*\|", - r"\$\(.*\)", # Command substitution (dangerous in some contexts) - # System modification - r">\s*/etc/", - r"chmod\s+777", - r"chmod\s+\+s", - r"chown\s+.*:.*\s+/", - # Privilege escalation - r"sudo\s+su\s*$", - r"sudo\s+-i\s*$", - # Environment manipulation - r"export\s+LD_PRELOAD", - r"export\s+LD_LIBRARY_PATH.*=/", -] - -# Commands that are allowed (allowlist for package management) -ALLOWED_COMMAND_PREFIXES = [ - "apt", - "apt-get", - "apt-cache", - "dpkg", - "yum", - "dnf", - "pacman", - "zypper", - "pip", - "pip3", - "npm", - "systemctl", - "service", - "docker", - "docker-compose", - "kubectl", - "git", - "curl", # Only for downloading, not piping to shell - "wget", # Only for downloading, not piping to shell - "tar", - "unzip", - "chmod", - "chown", - "mkdir", - "cp", - "mv", - "ln", - "cat", - "echo", - "tee", - "grep", - "sed", - "awk", - "head", - "tail", - "sort", - "uniq", - "wc", - "ls", - "find", - "which", - "whereis", - "id", - "whoami", - "hostname", - "uname", - "lsb_release", - "nvidia-smi", - "nvcc", - "make", - "cmake", - "gcc", - "g++", - "python", - "python3", - "node", - "java", - "go", - "rustc", - "cargo", -] - - -@dataclass -class CommandResult: - """Result of a command execution.""" - - success: bool - stdout: str - stderr: str - return_code: int - command: str - - -class CommandValidationError(Exception): - """Raised when a command fails validation.""" - - pass - - -def validate_command(command: str, strict: bool = True) -> tuple[bool, str | None]: - """ - Validate a command for security. - - Args: - command: The command string to validate - strict: If True, command must start with an allowed prefix - - Returns: - Tuple of (is_valid, error_message) - """ - if not command or not command.strip(): - return False, "Empty command" - - command = command.strip() - - # Check for dangerous patterns - for pattern in DANGEROUS_PATTERNS: - if re.search(pattern, command, re.IGNORECASE): - return False, f"Dangerous pattern detected: {pattern}" - - # Check for shell metacharacters that could enable injection - dangerous_chars = ["`", "$", "&&", "||", ";", "\n", "\r"] - for char in dangerous_chars: - if char in command: - # Allow some patterns like $(dpkg --print-architecture) - if char == "$" and "$(" in command: - # Only allow specific safe command substitutions - safe_substitutions = [ - "$(dpkg --print-architecture)", - "$(lsb_release -cs)", - "$(uname -r)", - "$(uname -m)", - "$(whoami)", - "$(hostname)", - ] - # Check if all $(...) patterns are in safe list - found_subs = re.findall(r"\$\([^)]+\)", command) - for sub in found_subs: - if sub not in safe_substitutions: - return False, f"Unsafe command substitution: {sub}" - elif char == "&&" or char == "||": - # Allow chained commands, but validate each part - continue - elif char == ";": - # Semicolon is dangerous - could chain arbitrary commands - return False, "Semicolon not allowed in commands" - elif char == "`": - return False, "Backtick command substitution not allowed" - - # Strict mode: command must start with allowed prefix - if strict: - first_word = command.split()[0] - # Handle sudo prefix - if first_word == "sudo": - parts = command.split() - if len(parts) > 1: - first_word = parts[1] - - if first_word not in ALLOWED_COMMAND_PREFIXES: - return False, f"Command '{first_word}' is not in the allowlist" - - return True, None - - -def sanitize_command(command: str) -> str: - """ - Sanitize a command by removing potentially dangerous elements. - - Args: - command: The command to sanitize - - Returns: - Sanitized command string - """ - # Remove null bytes - command = command.replace("\x00", "") - - # Remove control characters - command = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", "", command) - - # Normalize whitespace - command = " ".join(command.split()) - - return command - - -def run_command( - command: str, - timeout: int = 300, - validate: bool = True, - use_shell: bool = False, - capture_output: bool = True, - cwd: str | None = None, -) -> CommandResult: - """ - Execute a command safely with validation. - - Args: - command: The command to execute - timeout: Maximum execution time in seconds - validate: Whether to validate the command before execution - use_shell: Use shell execution (less secure, only for complex commands) - capture_output: Capture stdout/stderr - cwd: Working directory for command execution - - Returns: - CommandResult with execution details - - Raises: - CommandValidationError: If command fails validation - """ - # Sanitize input - command = sanitize_command(command) - - # Validate if requested - if validate: - is_valid, error = validate_command(command, strict=True) - if not is_valid: - raise CommandValidationError(f"Command validation failed: {error}") - - try: - if use_shell: - # Shell execution - use with caution - # Only allow if command has been validated - result = subprocess.run( - command, - shell=True, - capture_output=capture_output, - text=True, - timeout=timeout, - cwd=cwd, - ) - else: - # Safer: parse command and execute without shell - # This prevents most injection attacks - args = shlex.split(command) - result = subprocess.run( - args, capture_output=capture_output, text=True, timeout=timeout, cwd=cwd - ) - - return CommandResult( - success=result.returncode == 0, - stdout=result.stdout if capture_output else "", - stderr=result.stderr if capture_output else "", - return_code=result.returncode, - command=command, - ) - - except subprocess.TimeoutExpired: - return CommandResult( - success=False, - stdout="", - stderr=f"Command timed out after {timeout} seconds", - return_code=-1, - command=command, - ) - except FileNotFoundError as e: - return CommandResult( - success=False, - stdout="", - stderr=f"Command not found: {e}", - return_code=-1, - command=command, - ) - except Exception as e: - logger.exception(f"Error executing command: {command}") - return CommandResult( - success=False, stdout="", stderr=str(e), return_code=-1, command=command - ) - - -def run_command_chain( - commands: list[str], timeout_per_command: int = 300, stop_on_error: bool = True -) -> list[CommandResult]: - """ - Execute a chain of commands safely. - - Args: - commands: List of commands to execute - timeout_per_command: Timeout for each command - stop_on_error: Stop execution if a command fails - - Returns: - List of CommandResult for each command - """ - results = [] - - for command in commands: - result = run_command(command, timeout=timeout_per_command) - results.append(result) - - if not result.success and stop_on_error: - break - - return results +""" +Secure Command Execution Utilities + +This module provides safe command execution with validation and sandboxing. +All commands should go through these utilities to prevent shell injection. +""" + +import logging +import re +import shlex +import subprocess +from dataclasses import dataclass + +logger = logging.getLogger(__name__) + +# Dangerous patterns that should never be executed +DANGEROUS_PATTERNS = [ + # File system destruction + r"rm\s+-rf\s+[/\*]", + r"rm\s+-rf\s+\$", + r"rm\s+--no-preserve-root", + r":\s*\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}", # Fork bomb + # Disk operations + r"dd\s+if=.*of=/dev/", + r"mkfs\.", + r"wipefs", + # Network attacks + r"curl\s+.*\|\s*sh", + r"curl\s+.*\|\s*bash", + r"wget\s+.*\|\s*sh", + r"wget\s+.*\|\s*bash", + r"curl\s+-o\s+-\s+.*\|\s*", + # Code execution + r"\beval\s+", + r'python\s+-c\s+["\'].*exec', + r'python\s+-c\s+["\'].*import\s+os', + r"base64\s+-d\s+.*\|", + r"\$\(.*\)", # Command substitution (dangerous in some contexts) + # System modification + r">\s*/etc/", + r"chmod\s+777", + r"chmod\s+\+s", + r"chown\s+.*:.*\s+/", + # Privilege escalation + r"sudo\s+su\s*$", + r"sudo\s+-i\s*$", + # Environment manipulation + r"export\s+LD_PRELOAD", + r"export\s+LD_LIBRARY_PATH.*=/", +] + +# Commands that are allowed (allowlist for package management) +ALLOWED_COMMAND_PREFIXES = [ + "apt", + "apt-get", + "apt-cache", + "dpkg", + "yum", + "dnf", + "pacman", + "zypper", + "pip", + "pip3", + "npm", + "systemctl", + "service", + "docker", + "docker-compose", + "kubectl", + "git", + "curl", # Only for downloading, not piping to shell + "wget", # Only for downloading, not piping to shell + "tar", + "unzip", + "chmod", + "chown", + "mkdir", + "cp", + "mv", + "ln", + "cat", + "echo", + "tee", + "grep", + "sed", + "awk", + "head", + "tail", + "sort", + "uniq", + "wc", + "ls", + "find", + "which", + "whereis", + "id", + "whoami", + "hostname", + "uname", + "lsb_release", + "nvidia-smi", + "nvcc", + "make", + "cmake", + "gcc", + "g++", + "python", + "python3", + "node", + "java", + "go", + "rustc", + "cargo", +] + + +@dataclass +class CommandResult: + """Result of a command execution.""" + + success: bool + stdout: str + stderr: str + return_code: int + command: str + + +class CommandValidationError(Exception): + """Raised when a command fails validation.""" + + pass + + +def validate_command(command: str, strict: bool = True) -> tuple[bool, str | None]: + """ + Validate a command for security. + + Args: + command: The command string to validate + strict: If True, command must start with an allowed prefix + + Returns: + Tuple of (is_valid, error_message) + """ + if not command or not command.strip(): + return False, "Empty command" + + command = command.strip() + + # Check for dangerous patterns + for pattern in DANGEROUS_PATTERNS: + if re.search(pattern, command, re.IGNORECASE): + return False, f"Dangerous pattern detected: {pattern}" + + # Check for shell metacharacters that could enable injection + dangerous_chars = ["`", "$", "&&", "||", ";", "\n", "\r"] + for char in dangerous_chars: + if char in command: + # Allow some patterns like $(dpkg --print-architecture) + if char == "$" and "$(" in command: + # Only allow specific safe command substitutions + safe_substitutions = [ + "$(dpkg --print-architecture)", + "$(lsb_release -cs)", + "$(uname -r)", + "$(uname -m)", + "$(whoami)", + "$(hostname)", + ] + # Check if all $(...) patterns are in safe list + found_subs = re.findall(r"\$\([^)]+\)", command) + for sub in found_subs: + if sub not in safe_substitutions: + return False, f"Unsafe command substitution: {sub}" + elif char == "&&" or char == "||": + # Allow chained commands, but validate each part + continue + elif char == ";": + # Semicolon is dangerous - could chain arbitrary commands + return False, "Semicolon not allowed in commands" + elif char == "`": + return False, "Backtick command substitution not allowed" + + # Strict mode: command must start with allowed prefix + if strict: + first_word = command.split()[0] + # Handle sudo prefix + if first_word == "sudo": + parts = command.split() + if len(parts) > 1: + first_word = parts[1] + + if first_word not in ALLOWED_COMMAND_PREFIXES: + return False, f"Command '{first_word}' is not in the allowlist" + + return True, None + + +def sanitize_command(command: str) -> str: + """ + Sanitize a command by removing potentially dangerous elements. + + Args: + command: The command to sanitize + + Returns: + Sanitized command string + """ + # Remove null bytes + command = command.replace("\x00", "") + + # Remove control characters + command = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", "", command) + + # Normalize whitespace + command = " ".join(command.split()) + + return command + + +def run_command( + command: str, + timeout: int = 300, + validate: bool = True, + use_shell: bool = False, + capture_output: bool = True, + cwd: str | None = None, +) -> CommandResult: + """ + Execute a command safely with validation. + + Args: + command: The command to execute + timeout: Maximum execution time in seconds + validate: Whether to validate the command before execution + use_shell: Use shell execution (less secure, only for complex commands) + capture_output: Capture stdout/stderr + cwd: Working directory for command execution + + Returns: + CommandResult with execution details + + Raises: + CommandValidationError: If command fails validation + """ + # Sanitize input + command = sanitize_command(command) + + # Validate if requested + if validate: + is_valid, error = validate_command(command, strict=True) + if not is_valid: + raise CommandValidationError(f"Command validation failed: {error}") + + try: + if use_shell: + # Shell execution - use with caution + # Only allow if command has been validated + result = subprocess.run( + command, + shell=True, + capture_output=capture_output, + text=True, + timeout=timeout, + cwd=cwd, + ) + else: + # Safer: parse command and execute without shell + # This prevents most injection attacks + args = shlex.split(command) + result = subprocess.run( + args, capture_output=capture_output, text=True, timeout=timeout, cwd=cwd + ) + + return CommandResult( + success=result.returncode == 0, + stdout=result.stdout if capture_output else "", + stderr=result.stderr if capture_output else "", + return_code=result.returncode, + command=command, + ) + + except subprocess.TimeoutExpired: + return CommandResult( + success=False, + stdout="", + stderr=f"Command timed out after {timeout} seconds", + return_code=-1, + command=command, + ) + except FileNotFoundError as e: + return CommandResult( + success=False, + stdout="", + stderr=f"Command not found: {e}", + return_code=-1, + command=command, + ) + except Exception as e: + logger.exception(f"Error executing command: {command}") + return CommandResult( + success=False, stdout="", stderr=str(e), return_code=-1, command=command + ) + + +def run_command_chain( + commands: list[str], timeout_per_command: int = 300, stop_on_error: bool = True +) -> list[CommandResult]: + """ + Execute a chain of commands safely. + + Args: + commands: List of commands to execute + timeout_per_command: Timeout for each command + stop_on_error: Stop execution if a command fails + + Returns: + List of CommandResult for each command + """ + results = [] + + for command in commands: + result = run_command(command, timeout=timeout_per_command) + results.append(result) + + if not result.success and stop_on_error: + break + + return results diff --git a/scripts/automation/cortex-master-quarterback.sh b/scripts/automation/cortex-master-quarterback.sh old mode 100644 new mode 100755 index fec81fc2..982fc0d8 --- a/scripts/automation/cortex-master-quarterback.sh +++ b/scripts/automation/cortex-master-quarterback.sh @@ -1,712 +1,712 @@ -#!/bin/bash -# CORTEX LINUX - MASTER QUARTERBACK SCRIPT -# Manages team onboarding, issue assignment, PR reviews, and project coordination -# Created: November 17, 2025 -# Usage: bash cortex-master-quarterback.sh - -set -e - -echo "🧠 CORTEX LINUX - MASTER QUARTERBACK SCRIPT" -echo "===========================================" -echo "" -echo "This script will:" -echo " 1. Welcome new developers individually" -echo " 2. Assign issues based on expertise" -echo " 3. Review and advance ready PRs" -echo " 4. Coordinate team activities" -echo "" - -# Configuration -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") - -if [ -z "$GITHUB_TOKEN" ]; then - echo "āŒ ERROR: GITHUB_TOKEN not found in ~/.zshrc" - echo "Please add: export GITHUB_TOKEN='your_token_here'" - exit 1 -fi - -# Check if gh CLI is installed -if ! command -v gh &> /dev/null; then - echo "āŒ ERROR: GitHub CLI (gh) not installed" - echo "Install with: brew install gh" - exit 1 -fi - -# Authenticate gh CLI -export GH_TOKEN="$GITHUB_TOKEN" - -echo "āœ… Configuration loaded" -echo "šŸ“Š Repository: $REPO" -echo "" - -# ============================================================================ -# SECTION 1: WELCOME NEW DEVELOPERS -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ‘‹ SECTION 1: WELCOMING NEW DEVELOPERS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Function to welcome a developer -welcome_developer() { - local username=$1 - local name=$2 - local location=$3 - local skills=$4 - local strength=$5 - local recommended_issues=$6 - - echo "šŸ“ Welcoming @$username ($name)..." - - # Create welcome comment - welcome_msg="šŸ‘‹ **Welcome to Cortex Linux, @$username!** - -We're thrilled to have you join our mission to build the AI-native operating system! - -## šŸŽÆ Your Profile Highlights -**Location:** $location -**Primary Skills:** $skills -**Key Strength:** $strength - -## šŸ’” Recommended Issues for You -$recommended_issues - -## šŸš€ Getting Started - -1. **Join our Discord**: https://discord.gg/uCqHvxjU83 (#dev-questions channel) -2. **Review Contributing Guide**: Check repo README and CONTRIBUTING.md -3. **Comment on issues** you're interested in - we'll provide starter code to accelerate development - -## šŸ’° Compensation Structure - -- **Cash bounties** on merge: \$25-200 depending on complexity -- **2x bonus** when we close our \$2-3M seed round (February 2025) -- **Founding team opportunities** for top contributors (equity post-funding) - -## šŸ¤ Our Development Model - -We use a **hybrid approach** that's proven successful: -- Mike + Claude generate complete implementations -- Contributors test, integrate, and validate -- 63% cost savings, 80% time savings -- Everyone wins with professional baseline code - -## šŸ“‹ Next Steps - -1. Browse issues and comment on ones that interest you -2. We'll provide starter code to save you time -3. Test, integrate, and submit PR -4. Get paid on merge! šŸŽ‰ - -**Questions?** Tag @mikejmorgan-ai in any issue or drop into Discord. - -Let's build something revolutionary together! 🧠⚔ - ---- -*Automated welcome from Cortex Team Management System*" - - echo "$welcome_msg" - echo "" - echo "Would you like to post this welcome to @$username's recent activity? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - # Find their most recent issue comment or PR - recent_activity=$(gh api "/repos/$REPO/issues?state=all&creator=$username&per_page=1" 2>/dev/null | jq -r '.[0].number' 2>/dev/null) - - if [ ! -z "$recent_activity" ] && [ "$recent_activity" != "null" ]; then - echo " Posting welcome to Issue/PR #$recent_activity..." - echo "$welcome_msg" | gh issue comment $recent_activity --body-file - --repo $REPO 2>/dev/null || echo " āš ļø Could not post (may need manual posting)" - echo " āœ… Welcome posted!" - else - echo " ā„¹ļø No recent activity found - save welcome message for their first interaction" - fi - else - echo " ā­ļø Skipped posting (you can post manually later)" - fi - - echo "" -} - -# Welcome each new developer -echo "Welcoming 5 new developers..." -echo "" - -welcome_developer \ - "AbuBakar877" \ - "Abu Bakar" \ - "Turkey šŸ‡¹šŸ‡·" \ - "Node.js, React, Angular, Full-stack web development" \ - "Modern JavaScript frameworks and web UI" \ - "- **Issue #27** (Progress Notifications UI) - \$100-150 - Perfect for your frontend skills -- **Issue #26** (User Preferences UI) - \$100-150 - Web interface components -- **Issue #33** (Config Export/Import) - \$75-100 - Data handling + UI" - -welcome_developer \ - "aliraza556" \ - "Ali Raza" \ - "Global Developer šŸŒ" \ - "Full-stack (1000+ contributions), Multi-language expert" \ - "Elite-tier developer with proven track record" \ - "- **Issue #14** (Rollback System) - \$150-200 - āœ… **ALREADY ASSIGNED** - You've got this! -- **Issue #12** (Dependency Resolution) - \$150-200 - Complex logic, perfect match -- **Issue #30** (Self-Update System) - \$150-200 - Advanced architecture -- **Issue #31** (Plugin System) - \$200-300 - Architectural design challenge" - -welcome_developer \ - "anees4500" \ - "Anees" \ - "Location TBD" \ - "Java, C, Python, JavaScript, CDC/Batch processing" \ - "Multi-language capability with data processing experience" \ - "- **Issue #32** (Batch Operations) - \$100-150 - Your CDC experience is perfect here -- **Issue #28** (Requirements Check) - \$75-100 - Systems validation -- **Issue #10** (Installation Verification) - \$100-150 - Backend validation work" - -welcome_developer \ - "brymut" \ - "Bryan Mutai" \ - "Nairobi, Kenya šŸ‡°šŸ‡Ŗ" \ - "TypeScript, Python, PHP, JavaScript - Full-stack with backend focus" \ - "Architectural thinking with perfect skill stack (TypeScript + Python)" \ - "- **Issue #31** (Plugin System) - \$200-300 - **HIGHLY RECOMMENDED** - Architectural perfect match -- **Issue #26** (User Preferences) - \$100-150 - API design + backend -- **Issue #20** (Context Memory) - \$150-200 - TypeScript+Python combo ideal -- **Issue #25** (Network/Proxy Config) - \$150-200 - Backend + systems" - -welcome_developer \ - "shalinibhavi525-sudo" \ - "Shalini Bhavi" \ - "Ireland šŸ‡®šŸ‡Ŗ" \ - "Python, JavaScript, HTML - Documentation focus" \ - "Documentation specialist with web UI skills" \ - "- **Issue #15** (Documentation) - \$100-150 - āœ… **ALREADY ASSIGNED** - Perfect match! -- **Issue #27** (Progress Notifications) - \$100-150 - User-facing UI work -- Testing bounties - \$50-75 - Validate implementations from other devs" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 1 Complete: Developer welcomes prepared" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# SECTION 2: ISSUE ASSIGNMENTS -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸŽÆ SECTION 2: STRATEGIC ISSUE ASSIGNMENTS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "Analyzing current issue status..." - -# Function to assign issue -assign_issue() { - local issue_num=$1 - local developer=$2 - local reason=$3 - - echo "" - echo "šŸ“Œ Assigning Issue #$issue_num to @$developer" - echo " Reason: $reason" - - # Check if issue exists and is unassigned - issue_info=$(gh issue view $issue_num --repo $REPO --json number,title,assignees,state 2>/dev/null || echo "") - - if [ -z "$issue_info" ]; then - echo " āš ļø Issue #$issue_num not found or not accessible" - return - fi - - # Check if already assigned - assignee_count=$(echo "$issue_info" | jq '.assignees | length') - - if [ "$assignee_count" -gt 0 ]; then - current_assignee=$(echo "$issue_info" | jq -r '.assignees[0].login') - echo " ā„¹ļø Already assigned to @$current_assignee - skipping" - return - fi - - echo " Proceed with assignment? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - gh issue edit $issue_num --add-assignee $developer --repo $REPO 2>/dev/null && \ - echo " āœ… Assigned!" || \ - echo " āš ļø Could not assign (may need manual assignment)" - - # Add comment explaining assignment - assignment_comment="šŸŽÆ **Assigned to @$developer** - -**Why you're perfect for this:** $reason - -**Next Steps:** -1. Review the issue description and acceptance criteria -2. Comment if you'd like starter code from our hybrid development model -3. We can provide complete implementation for testing/integration (\$50-75) -4. Or build from scratch for full bounty - -**Questions?** Just ask! We're here to help you succeed. - ---- -*Automated assignment from Cortex Team Management*" - - echo "$assignment_comment" | gh issue comment $issue_num --body-file - --repo $REPO 2>/dev/null || true - else - echo " ā­ļø Skipped" - fi -} - -echo "" -echo "šŸ”“ CRITICAL PATH ASSIGNMENTS (MVP Blockers)" -echo "─────────────────────────────────────────" - -# Issue #7 - Already assigned to chandrapratnamar, but check if help needed -echo "" -echo "Issue #7 (Package Manager Wrapper) - THE critical blocker" -echo " Current: Assigned to @chandrapratnamar (PR #17 in progress)" -echo " Status: Check if they need assistance" -echo " Action: Monitor weekly, offer @aliraza556 or @brymut for code review" -echo "" - -# Issue #10 - Installation Verification -assign_issue 10 "aliraza556" "Elite developer, perfect for systems validation work. Code is ready from Mike." - -# Issue #12 - Dependency Resolution -assign_issue 12 "brymut" "TypeScript+Python skills ideal for complex dependency logic. Mike has complete implementation." - -# Issue #14 - Already assigned to aliraza556 -echo "" -echo "Issue #14 (Rollback System) - āœ… Already assigned to @aliraza556" -echo " Action: Check PR status, offer review assistance" -echo "" - -echo "" -echo "🟔 HIGH PRIORITY ASSIGNMENTS" -echo "─────────────────────────────" - -# Issue #20/24 - Context Memory -assign_issue 20 "brymut" "Architectural experience + TypeScript/Python combo. Mike has implementation ready." - -# Issue #29 - Logging System -assign_issue 29 "anees4500" "Backend infrastructure work, good first complex task to assess quality." - -echo "" -echo "🟢 MEDIUM PRIORITY ASSIGNMENTS" -echo "───────────────────────────────" - -# Issue #25 - Network Config -assign_issue 25 "brymut" "Backend + systems knowledge required for proxy/network configuration." - -# Issue #26 - User Preferences -assign_issue 26 "AbuBakar877" "API + UI components match your full-stack web background." - -# Issue #27 - Progress Notifications -assign_issue 27 "AbuBakar877" "Frontend UI focus, perfect for your React/Angular experience." - -# Issue #28 - Requirements Check -assign_issue 28 "anees4500" "Systems validation, good complement to your batch processing skills." - -echo "" -echo "šŸ”µ ADVANCED FEATURE ASSIGNMENTS" -echo "────────────────────────────────" - -# Issue #30 - Self-Update -assign_issue 30 "aliraza556" "Complex systems integration needs elite-tier developer." - -# Issue #31 - Plugin System -assign_issue 31 "brymut" "**HIGHEST RECOMMENDATION** - Architectural design matches your background perfectly." - -# Issue #32 - Batch Operations -assign_issue 32 "anees4500" "Your CDC/batch processing experience is ideal match." - -# Issue #33 - Config Export/Import -assign_issue 33 "shalinibhavi525-sudo" "Data handling + web UI, complements your documentation work." - -# Issue #15 - Already assigned -echo "" -echo "Issue #15 (Documentation) - āœ… Already assigned to @shalinibhavi525-sudo" -echo " Action: Check progress, offer assistance if needed" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 2 Complete: Strategic assignments made" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# SECTION 3: PULL REQUEST REVIEW -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ” SECTION 3: PULL REQUEST REVIEW & ADVANCEMENT" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "Fetching open pull requests..." - -# Get all open PRs -prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,mergeable,reviewDecision --limit 50 2>/dev/null || echo "[]") - -pr_count=$(echo "$prs" | jq 'length') - -echo "Found $pr_count open pull requests" -echo "" - -if [ "$pr_count" -eq 0 ]; then - echo "āœ… No open PRs to review" -else - echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - \(.reviewDecision // "PENDING")"' - echo "" - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "PR REVIEW PRIORITIES" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - - # Critical PRs (Issue #7 related) - echo "šŸ”“ CRITICAL - Package Manager (Issue #7)" - echo "PR #17 by @chandrapratnamar" - echo " Action: Review immediately, this is THE MVP blocker" - echo " Review criteria:" - echo " - Does it translate natural language to apt commands?" - echo " - Are tests comprehensive?" - echo " - Does it integrate with LLM layer?" - echo "" - - echo "🟔 HIGH PRIORITY - MVP Features" - echo "Check for PRs related to:" - echo " - Issue #10 (Installation Verification)" - echo " - Issue #12 (Dependency Resolution)" - echo " - Issue #14 (Rollback System)" - echo " - Issue #13 (Error Parser) - PR #23 by @AbdulKadir877" - echo "" - - echo "🟢 STANDARD PRIORITY - All other PRs" - echo "Review remaining PRs in order received" - echo "" - - echo "Would you like to review PRs interactively? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "" - echo "Opening PR review interface..." - echo "" - - # For each PR, offer review options - echo "$prs" | jq -r '.[] | .number' | while read pr_num; do - pr_info=$(gh pr view $pr_num --repo $REPO --json number,title,author,body 2>/dev/null) - pr_title=$(echo "$pr_info" | jq -r '.title') - pr_author=$(echo "$pr_info" | jq -r '.author.login') - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "Reviewing PR #$pr_num: $pr_title" - echo "Author: @$pr_author" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Actions:" - echo " [v] View PR in browser" - echo " [a] Approve PR" - echo " [c] Request changes" - echo " [m] Add comment" - echo " [s] Skip to next" - echo " [q] Quit review mode" - echo "" - echo -n "Choose action: " - read -n 1 action - echo "" - - case $action in - v|V) - gh pr view $pr_num --repo $REPO --web - ;; - a|A) - echo "āœ… Approving PR #$pr_num..." - gh pr review $pr_num --repo $REPO --approve --body "āœ… **APPROVED** - -Excellent work @$pr_author! This implementation: -- Meets acceptance criteria -- Includes comprehensive tests -- Integrates well with existing architecture -- Documentation is clear - -**Next Steps:** -1. Merging this PR -2. Bounty will be processed -3. Thank you for your contribution! - -šŸŽ‰ Welcome to the Cortex Linux contributor team!" - echo "Would you like to merge now? (y/n)" - read -n 1 merge_now - echo "" - if [[ $merge_now =~ ^[Yy]$ ]]; then - gh pr merge $pr_num --repo $REPO --squash --delete-branch - echo "āœ… Merged and branch deleted!" - fi - ;; - c|C) - echo "Enter feedback (press Ctrl+D when done):" - feedback=$(cat) - gh pr review $pr_num --repo $REPO --request-changes --body "šŸ”„ **Changes Requested** - -Thanks for your work @$pr_author! Here's what needs attention: - -$feedback - -**Please update and let me know when ready for re-review.** - -We're here to help if you have questions!" - ;; - m|M) - echo "Enter comment (press Ctrl+D when done):" - comment=$(cat) - gh pr comment $pr_num --repo $REPO --body "$comment" - echo "āœ… Comment added" - ;; - q|Q) - echo "Exiting review mode..." - break - ;; - *) - echo "Skipping..." - ;; - esac - echo "" - done - else - echo "ā­ļø Skipped interactive review" - echo " You can review PRs manually at: https://github.com/$REPO/pulls" - fi -fi - -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 3 Complete: PR review assistance provided" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# SECTION 4: TEAM COORDINATION -# ============================================================================ - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ¤ SECTION 4: TEAM COORDINATION & NEXT ACTIONS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ“Š CURRENT PROJECT STATUS" -echo "─────────────────────────" -echo "" - -# Count issues by status -total_issues=$(gh issue list --repo $REPO --limit 1000 --json number 2>/dev/null | jq 'length') -open_issues=$(gh issue list --repo $REPO --state open --limit 1000 --json number 2>/dev/null | jq 'length') -closed_issues=$(gh issue list --repo $REPO --state closed --limit 1000 --json number 2>/dev/null | jq 'length') - -echo "Issues:" -echo " Total: $total_issues" -echo " Open: $open_issues" -echo " Closed: $closed_issues" -echo "" - -# Count PRs -open_prs=$(gh pr list --repo $REPO --state open --json number 2>/dev/null | jq 'length') -merged_prs=$(gh pr list --repo $REPO --state merged --limit 100 --json number 2>/dev/null | jq 'length') - -echo "Pull Requests:" -echo " Open: $open_prs" -echo " Merged (recent): $merged_prs" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸŽÆ IMMEDIATE ACTION ITEMS (Priority Order)" -echo "──────────────────────────────────────────" -echo "" - -echo "1. šŸ”“ CRITICAL - Check Issue #7 Progress" -echo " - PR #17 by @chandrapratnamar" -echo " - This is THE MVP blocker" -echo " - Review weekly, offer assistance" -echo " - Command: gh pr view 17 --repo $REPO --web" -echo "" - -echo "2. 🟔 HIGH - Review Ready PRs" -echo " - PR #23 (Error Parser) by @AbdulKadir877" -echo " - Any PRs marked 'ready-for-review'" -echo " - Command: gh pr list --repo $REPO --label ready-for-review" -echo "" - -echo "3. 🟢 MEDIUM - Upload Complete Implementations" -echo " - Issue #10 (Installation Verification) - Code ready" -echo " - Issue #12 (Dependency Resolution) - Code ready" -echo " - Issue #14 (Rollback System) - Code ready with @aliraza556" -echo " - Use: ~/cortex/cortex-master-pr-creator.sh" -echo "" - -echo "4. šŸ”µ ENGAGE NEW DEVELOPERS" -echo " - Post welcome messages (generated above)" -echo " - Monitor their first comments/PRs" -echo " - Offer starter code to accelerate" -echo "" - -echo "5. šŸ’° PROCESS BOUNTIES" -echo " - Track merged PRs" -echo " - Calculate owed bounties" -echo " - Process payments (crypto for international)" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ“‹ RECOMMENDED WEEKLY ROUTINE" -echo "─────────────────────────────" -echo "" -echo "Monday:" -echo " - Run this quarterback script" -echo " - Review critical path (Issue #7)" -echo " - Merge ready PRs" -echo "" -echo "Wednesday:" -echo " - Check new issues/comments" -echo " - Respond to developer questions" -echo " - Upload any ready implementations" -echo "" -echo "Friday:" -echo " - Process bounty payments" -echo " - Update team on Discord" -echo " - Plan next week priorities" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ”— QUICK LINKS" -echo "──────────────" -echo "" -echo "Repository: https://github.com/$REPO" -echo "Open Issues: https://github.com/$REPO/issues" -echo "Open PRs: https://github.com/$REPO/pulls" -echo "Discord: https://discord.gg/uCqHvxjU83" -echo "Project Board: https://github.com/orgs/cortexlinux/projects" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "šŸ“± POST TO DISCORD" -echo "──────────────────" -echo "" - -discord_announcement="šŸŽ‰ **Team Update - November 17, 2025** - -**Welcome 5 New Developers!** -- @AbuBakar877 (Turkey) - Full-stack web specialist -- @aliraza556 (Global) - Elite tier, 1000+ contributions -- @anees4500 - Multi-language backend expert -- @brymut (Kenya) - TypeScript + Python architect -- @shalinibhavi525-sudo (Ireland) - Documentation specialist - -**Strategic Assignments Made:** -- Issue #31 (Plugin System) → @brymut (architectural perfect match) -- Issue #10 (Installation Verification) → @aliraza556 -- Issue #32 (Batch Operations) → @anees4500 -- Issue #27 (Progress UI) → @AbuBakar877 -- Issue #15 (Documentation) → @shalinibhavi525-sudo āœ… - -**Critical Path:** -- Issue #7 (Package Manager) - THE blocker - @chandrapratnamar working PR #17 -- Monitoring weekly, need completion for MVP - -**Ready to Review:** -- Multiple PRs waiting for review -- Bounties ready to process on merge - -**The Hybrid Model Works:** -- 63% cost savings -- 80% time savings -- Professional baseline + contributor validation -- Win-win for everyone - -šŸ’° **Bounties:** \$25-200 on merge + 2x bonus at funding -šŸŽÆ **Goal:** MVP complete for February 2025 seed round -šŸ’¼ **Opportunities:** Founding team roles for top contributors - -Browse issues: https://github.com/$REPO/issues -Questions? #dev-questions channel - -Let's build the future of Linux! 🧠⚔" - -echo "$discord_announcement" -echo "" -echo "Copy the above message and post to Discord #announcements" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Section 4 Complete: Team coordination completed" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# ============================================================================ -# FINAL SUMMARY -# ============================================================================ - -echo "" -echo "════════════════════════════════════════════════════════" -echo "šŸ† CORTEX QUARTERBACK SCRIPT - EXECUTION COMPLETE" -echo "════════════════════════════════════════════════════════" -echo "" - -echo "šŸ“Š EXECUTION SUMMARY" -echo "────────────────────" -echo "" -echo "āœ… 5 developers welcomed with personalized messages" -echo "āœ… 10+ strategic issue assignments made" -echo "āœ… PR review guidance provided" -echo "āœ… Team coordination plan established" -echo "āœ… Discord announcement prepared" -echo "" - -echo "šŸŽÆ YOUR NEXT STEPS (Priority Order)" -echo "────────────────────────────────────" -echo "" -echo "1. Post Discord announcement (message above)" -echo "2. Review PR #17 (Issue #7 - THE BLOCKER)" -echo "3. Check for new developer comments" -echo "4. Upload ready implementations (Issues #10, #12, #14)" -echo "5. Process any merged PR bounties" -echo "" - -echo "šŸ’” STRATEGIC RECOMMENDATIONS" -echo "─────────────────────────────" -echo "" -echo "āœ… aliraza556 - Elite tier, consider for senior role/CTO discussion" -echo "āœ… brymut - Perfect skills for Plugin System (#31), high potential" -echo "āš ļø anees4500 - New, monitor first contribution quality" -echo "āœ… AbuBakar877 - Keep on web UI work, avoid core systems" -echo "āœ… shalinibhavi525-sudo - Perfect for docs, complement with testing" -echo "" - -echo "šŸ”„ CRITICAL PATH REMINDER" -echo "──────────────────────────" -echo "" -echo "Issue #7 (Package Manager Wrapper) is THE BLOCKER for MVP." -echo "Everything else can proceed in parallel, but #7 must complete." -echo "Check PR #17 weekly, offer assistance to @chandrapratnamar." -echo "" - -echo "════════════════════════════════════════════════════════" -echo "āœ… Ready for next session!" -echo "════════════════════════════════════════════════════════" -echo "" - -echo "Run this script weekly to quarterback your growing team." -echo "The Cortex Linux revolution is accelerating! 🧠⚔" -echo "" +#!/bin/bash +# CORTEX LINUX - MASTER QUARTERBACK SCRIPT +# Manages team onboarding, issue assignment, PR reviews, and project coordination +# Created: November 17, 2025 +# Usage: bash cortex-master-quarterback.sh + +set -e + +echo "🧠 CORTEX LINUX - MASTER QUARTERBACK SCRIPT" +echo "===========================================" +echo "" +echo "This script will:" +echo " 1. Welcome new developers individually" +echo " 2. Assign issues based on expertise" +echo " 3. Review and advance ready PRs" +echo " 4. Coordinate team activities" +echo "" + +# Configuration +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") + +if [ -z "$GITHUB_TOKEN" ]; then + echo "āŒ ERROR: GITHUB_TOKEN not found in ~/.zshrc" + echo "Please add: export GITHUB_TOKEN='your_token_here'" + exit 1 +fi + +# Check if gh CLI is installed +if ! command -v gh &> /dev/null; then + echo "āŒ ERROR: GitHub CLI (gh) not installed" + echo "Install with: brew install gh" + exit 1 +fi + +# Authenticate gh CLI +export GH_TOKEN="$GITHUB_TOKEN" + +echo "āœ… Configuration loaded" +echo "šŸ“Š Repository: $REPO" +echo "" + +# ============================================================================ +# SECTION 1: WELCOME NEW DEVELOPERS +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ‘‹ SECTION 1: WELCOMING NEW DEVELOPERS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Function to welcome a developer +welcome_developer() { + local username=$1 + local name=$2 + local location=$3 + local skills=$4 + local strength=$5 + local recommended_issues=$6 + + echo "šŸ“ Welcoming @$username ($name)..." + + # Create welcome comment + welcome_msg="šŸ‘‹ **Welcome to Cortex Linux, @$username!** + +We're thrilled to have you join our mission to build the AI-native operating system! + +## šŸŽÆ Your Profile Highlights +**Location:** $location +**Primary Skills:** $skills +**Key Strength:** $strength + +## šŸ’” Recommended Issues for You +$recommended_issues + +## šŸš€ Getting Started + +1. **Join our Discord**: https://discord.gg/uCqHvxjU83 (#dev-questions channel) +2. **Review Contributing Guide**: Check repo README and CONTRIBUTING.md +3. **Comment on issues** you're interested in - we'll provide starter code to accelerate development + +## šŸ’° Compensation Structure + +- **Cash bounties** on merge: \$25-200 depending on complexity +- **2x bonus** when we close our \$2-3M seed round (February 2025) +- **Founding team opportunities** for top contributors (equity post-funding) + +## šŸ¤ Our Development Model + +We use a **hybrid approach** that's proven successful: +- Mike + Claude generate complete implementations +- Contributors test, integrate, and validate +- 63% cost savings, 80% time savings +- Everyone wins with professional baseline code + +## šŸ“‹ Next Steps + +1. Browse issues and comment on ones that interest you +2. We'll provide starter code to save you time +3. Test, integrate, and submit PR +4. Get paid on merge! šŸŽ‰ + +**Questions?** Tag @mikejmorgan-ai in any issue or drop into Discord. + +Let's build something revolutionary together! 🧠⚔ + +--- +*Automated welcome from Cortex Team Management System*" + + echo "$welcome_msg" + echo "" + echo "Would you like to post this welcome to @$username's recent activity? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + # Find their most recent issue comment or PR + recent_activity=$(gh api "/repos/$REPO/issues?state=all&creator=$username&per_page=1" 2>/dev/null | jq -r '.[0].number' 2>/dev/null) + + if [ ! -z "$recent_activity" ] && [ "$recent_activity" != "null" ]; then + echo " Posting welcome to Issue/PR #$recent_activity..." + echo "$welcome_msg" | gh issue comment $recent_activity --body-file - --repo $REPO 2>/dev/null || echo " āš ļø Could not post (may need manual posting)" + echo " āœ… Welcome posted!" + else + echo " ā„¹ļø No recent activity found - save welcome message for their first interaction" + fi + else + echo " ā­ļø Skipped posting (you can post manually later)" + fi + + echo "" +} + +# Welcome each new developer +echo "Welcoming 5 new developers..." +echo "" + +welcome_developer \ + "AbuBakar877" \ + "Abu Bakar" \ + "Turkey šŸ‡¹šŸ‡·" \ + "Node.js, React, Angular, Full-stack web development" \ + "Modern JavaScript frameworks and web UI" \ + "- **Issue #27** (Progress Notifications UI) - \$100-150 - Perfect for your frontend skills +- **Issue #26** (User Preferences UI) - \$100-150 - Web interface components +- **Issue #33** (Config Export/Import) - \$75-100 - Data handling + UI" + +welcome_developer \ + "aliraza556" \ + "Ali Raza" \ + "Global Developer šŸŒ" \ + "Full-stack (1000+ contributions), Multi-language expert" \ + "Elite-tier developer with proven track record" \ + "- **Issue #14** (Rollback System) - \$150-200 - āœ… **ALREADY ASSIGNED** - You've got this! +- **Issue #12** (Dependency Resolution) - \$150-200 - Complex logic, perfect match +- **Issue #30** (Self-Update System) - \$150-200 - Advanced architecture +- **Issue #31** (Plugin System) - \$200-300 - Architectural design challenge" + +welcome_developer \ + "anees4500" \ + "Anees" \ + "Location TBD" \ + "Java, C, Python, JavaScript, CDC/Batch processing" \ + "Multi-language capability with data processing experience" \ + "- **Issue #32** (Batch Operations) - \$100-150 - Your CDC experience is perfect here +- **Issue #28** (Requirements Check) - \$75-100 - Systems validation +- **Issue #10** (Installation Verification) - \$100-150 - Backend validation work" + +welcome_developer \ + "brymut" \ + "Bryan Mutai" \ + "Nairobi, Kenya šŸ‡°šŸ‡Ŗ" \ + "TypeScript, Python, PHP, JavaScript - Full-stack with backend focus" \ + "Architectural thinking with perfect skill stack (TypeScript + Python)" \ + "- **Issue #31** (Plugin System) - \$200-300 - **HIGHLY RECOMMENDED** - Architectural perfect match +- **Issue #26** (User Preferences) - \$100-150 - API design + backend +- **Issue #20** (Context Memory) - \$150-200 - TypeScript+Python combo ideal +- **Issue #25** (Network/Proxy Config) - \$150-200 - Backend + systems" + +welcome_developer \ + "shalinibhavi525-sudo" \ + "Shalini Bhavi" \ + "Ireland šŸ‡®šŸ‡Ŗ" \ + "Python, JavaScript, HTML - Documentation focus" \ + "Documentation specialist with web UI skills" \ + "- **Issue #15** (Documentation) - \$100-150 - āœ… **ALREADY ASSIGNED** - Perfect match! +- **Issue #27** (Progress Notifications) - \$100-150 - User-facing UI work +- Testing bounties - \$50-75 - Validate implementations from other devs" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 1 Complete: Developer welcomes prepared" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# SECTION 2: ISSUE ASSIGNMENTS +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸŽÆ SECTION 2: STRATEGIC ISSUE ASSIGNMENTS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "Analyzing current issue status..." + +# Function to assign issue +assign_issue() { + local issue_num=$1 + local developer=$2 + local reason=$3 + + echo "" + echo "šŸ“Œ Assigning Issue #$issue_num to @$developer" + echo " Reason: $reason" + + # Check if issue exists and is unassigned + issue_info=$(gh issue view $issue_num --repo $REPO --json number,title,assignees,state 2>/dev/null || echo "") + + if [ -z "$issue_info" ]; then + echo " āš ļø Issue #$issue_num not found or not accessible" + return + fi + + # Check if already assigned + assignee_count=$(echo "$issue_info" | jq '.assignees | length') + + if [ "$assignee_count" -gt 0 ]; then + current_assignee=$(echo "$issue_info" | jq -r '.assignees[0].login') + echo " ā„¹ļø Already assigned to @$current_assignee - skipping" + return + fi + + echo " Proceed with assignment? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + gh issue edit $issue_num --add-assignee $developer --repo $REPO 2>/dev/null && \ + echo " āœ… Assigned!" || \ + echo " āš ļø Could not assign (may need manual assignment)" + + # Add comment explaining assignment + assignment_comment="šŸŽÆ **Assigned to @$developer** + +**Why you're perfect for this:** $reason + +**Next Steps:** +1. Review the issue description and acceptance criteria +2. Comment if you'd like starter code from our hybrid development model +3. We can provide complete implementation for testing/integration (\$50-75) +4. Or build from scratch for full bounty + +**Questions?** Just ask! We're here to help you succeed. + +--- +*Automated assignment from Cortex Team Management*" + + echo "$assignment_comment" | gh issue comment $issue_num --body-file - --repo $REPO 2>/dev/null || true + else + echo " ā­ļø Skipped" + fi +} + +echo "" +echo "šŸ”“ CRITICAL PATH ASSIGNMENTS (MVP Blockers)" +echo "─────────────────────────────────────────" + +# Issue #7 - Already assigned to chandrapratnamar, but check if help needed +echo "" +echo "Issue #7 (Package Manager Wrapper) - THE critical blocker" +echo " Current: Assigned to @chandrapratnamar (PR #17 in progress)" +echo " Status: Check if they need assistance" +echo " Action: Monitor weekly, offer @aliraza556 or @brymut for code review" +echo "" + +# Issue #10 - Installation Verification +assign_issue 10 "aliraza556" "Elite developer, perfect for systems validation work. Code is ready from Mike." + +# Issue #12 - Dependency Resolution +assign_issue 12 "brymut" "TypeScript+Python skills ideal for complex dependency logic. Mike has complete implementation." + +# Issue #14 - Already assigned to aliraza556 +echo "" +echo "Issue #14 (Rollback System) - āœ… Already assigned to @aliraza556" +echo " Action: Check PR status, offer review assistance" +echo "" + +echo "" +echo "🟔 HIGH PRIORITY ASSIGNMENTS" +echo "─────────────────────────────" + +# Issue #20/24 - Context Memory +assign_issue 20 "brymut" "Architectural experience + TypeScript/Python combo. Mike has implementation ready." + +# Issue #29 - Logging System +assign_issue 29 "anees4500" "Backend infrastructure work, good first complex task to assess quality." + +echo "" +echo "🟢 MEDIUM PRIORITY ASSIGNMENTS" +echo "───────────────────────────────" + +# Issue #25 - Network Config +assign_issue 25 "brymut" "Backend + systems knowledge required for proxy/network configuration." + +# Issue #26 - User Preferences +assign_issue 26 "AbuBakar877" "API + UI components match your full-stack web background." + +# Issue #27 - Progress Notifications +assign_issue 27 "AbuBakar877" "Frontend UI focus, perfect for your React/Angular experience." + +# Issue #28 - Requirements Check +assign_issue 28 "anees4500" "Systems validation, good complement to your batch processing skills." + +echo "" +echo "šŸ”µ ADVANCED FEATURE ASSIGNMENTS" +echo "────────────────────────────────" + +# Issue #30 - Self-Update +assign_issue 30 "aliraza556" "Complex systems integration needs elite-tier developer." + +# Issue #31 - Plugin System +assign_issue 31 "brymut" "**HIGHEST RECOMMENDATION** - Architectural design matches your background perfectly." + +# Issue #32 - Batch Operations +assign_issue 32 "anees4500" "Your CDC/batch processing experience is ideal match." + +# Issue #33 - Config Export/Import +assign_issue 33 "shalinibhavi525-sudo" "Data handling + web UI, complements your documentation work." + +# Issue #15 - Already assigned +echo "" +echo "Issue #15 (Documentation) - āœ… Already assigned to @shalinibhavi525-sudo" +echo " Action: Check progress, offer assistance if needed" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 2 Complete: Strategic assignments made" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# SECTION 3: PULL REQUEST REVIEW +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ” SECTION 3: PULL REQUEST REVIEW & ADVANCEMENT" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "Fetching open pull requests..." + +# Get all open PRs +prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,mergeable,reviewDecision --limit 50 2>/dev/null || echo "[]") + +pr_count=$(echo "$prs" | jq 'length') + +echo "Found $pr_count open pull requests" +echo "" + +if [ "$pr_count" -eq 0 ]; then + echo "āœ… No open PRs to review" +else + echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - \(.reviewDecision // "PENDING")"' + echo "" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "PR REVIEW PRIORITIES" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + # Critical PRs (Issue #7 related) + echo "šŸ”“ CRITICAL - Package Manager (Issue #7)" + echo "PR #17 by @chandrapratnamar" + echo " Action: Review immediately, this is THE MVP blocker" + echo " Review criteria:" + echo " - Does it translate natural language to apt commands?" + echo " - Are tests comprehensive?" + echo " - Does it integrate with LLM layer?" + echo "" + + echo "🟔 HIGH PRIORITY - MVP Features" + echo "Check for PRs related to:" + echo " - Issue #10 (Installation Verification)" + echo " - Issue #12 (Dependency Resolution)" + echo " - Issue #14 (Rollback System)" + echo " - Issue #13 (Error Parser) - PR #23 by @AbdulKadir877" + echo "" + + echo "🟢 STANDARD PRIORITY - All other PRs" + echo "Review remaining PRs in order received" + echo "" + + echo "Would you like to review PRs interactively? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "" + echo "Opening PR review interface..." + echo "" + + # For each PR, offer review options + echo "$prs" | jq -r '.[] | .number' | while read pr_num; do + pr_info=$(gh pr view $pr_num --repo $REPO --json number,title,author,body 2>/dev/null) + pr_title=$(echo "$pr_info" | jq -r '.title') + pr_author=$(echo "$pr_info" | jq -r '.author.login') + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Reviewing PR #$pr_num: $pr_title" + echo "Author: @$pr_author" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Actions:" + echo " [v] View PR in browser" + echo " [a] Approve PR" + echo " [c] Request changes" + echo " [m] Add comment" + echo " [s] Skip to next" + echo " [q] Quit review mode" + echo "" + echo -n "Choose action: " + read -n 1 action + echo "" + + case $action in + v|V) + gh pr view $pr_num --repo $REPO --web + ;; + a|A) + echo "āœ… Approving PR #$pr_num..." + gh pr review $pr_num --repo $REPO --approve --body "āœ… **APPROVED** + +Excellent work @$pr_author! This implementation: +- Meets acceptance criteria +- Includes comprehensive tests +- Integrates well with existing architecture +- Documentation is clear + +**Next Steps:** +1. Merging this PR +2. Bounty will be processed +3. Thank you for your contribution! + +šŸŽ‰ Welcome to the Cortex Linux contributor team!" + echo "Would you like to merge now? (y/n)" + read -n 1 merge_now + echo "" + if [[ $merge_now =~ ^[Yy]$ ]]; then + gh pr merge $pr_num --repo $REPO --squash --delete-branch + echo "āœ… Merged and branch deleted!" + fi + ;; + c|C) + echo "Enter feedback (press Ctrl+D when done):" + feedback=$(cat) + gh pr review $pr_num --repo $REPO --request-changes --body "šŸ”„ **Changes Requested** + +Thanks for your work @$pr_author! Here's what needs attention: + +$feedback + +**Please update and let me know when ready for re-review.** + +We're here to help if you have questions!" + ;; + m|M) + echo "Enter comment (press Ctrl+D when done):" + comment=$(cat) + gh pr comment $pr_num --repo $REPO --body "$comment" + echo "āœ… Comment added" + ;; + q|Q) + echo "Exiting review mode..." + break + ;; + *) + echo "Skipping..." + ;; + esac + echo "" + done + else + echo "ā­ļø Skipped interactive review" + echo " You can review PRs manually at: https://github.com/$REPO/pulls" + fi +fi + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 3 Complete: PR review assistance provided" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# SECTION 4: TEAM COORDINATION +# ============================================================================ + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ¤ SECTION 4: TEAM COORDINATION & NEXT ACTIONS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ“Š CURRENT PROJECT STATUS" +echo "─────────────────────────" +echo "" + +# Count issues by status +total_issues=$(gh issue list --repo $REPO --limit 1000 --json number 2>/dev/null | jq 'length') +open_issues=$(gh issue list --repo $REPO --state open --limit 1000 --json number 2>/dev/null | jq 'length') +closed_issues=$(gh issue list --repo $REPO --state closed --limit 1000 --json number 2>/dev/null | jq 'length') + +echo "Issues:" +echo " Total: $total_issues" +echo " Open: $open_issues" +echo " Closed: $closed_issues" +echo "" + +# Count PRs +open_prs=$(gh pr list --repo $REPO --state open --json number 2>/dev/null | jq 'length') +merged_prs=$(gh pr list --repo $REPO --state merged --limit 100 --json number 2>/dev/null | jq 'length') + +echo "Pull Requests:" +echo " Open: $open_prs" +echo " Merged (recent): $merged_prs" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸŽÆ IMMEDIATE ACTION ITEMS (Priority Order)" +echo "──────────────────────────────────────────" +echo "" + +echo "1. šŸ”“ CRITICAL - Check Issue #7 Progress" +echo " - PR #17 by @chandrapratnamar" +echo " - This is THE MVP blocker" +echo " - Review weekly, offer assistance" +echo " - Command: gh pr view 17 --repo $REPO --web" +echo "" + +echo "2. 🟔 HIGH - Review Ready PRs" +echo " - PR #23 (Error Parser) by @AbdulKadir877" +echo " - Any PRs marked 'ready-for-review'" +echo " - Command: gh pr list --repo $REPO --label ready-for-review" +echo "" + +echo "3. 🟢 MEDIUM - Upload Complete Implementations" +echo " - Issue #10 (Installation Verification) - Code ready" +echo " - Issue #12 (Dependency Resolution) - Code ready" +echo " - Issue #14 (Rollback System) - Code ready with @aliraza556" +echo " - Use: ~/cortex/cortex-master-pr-creator.sh" +echo "" + +echo "4. šŸ”µ ENGAGE NEW DEVELOPERS" +echo " - Post welcome messages (generated above)" +echo " - Monitor their first comments/PRs" +echo " - Offer starter code to accelerate" +echo "" + +echo "5. šŸ’° PROCESS BOUNTIES" +echo " - Track merged PRs" +echo " - Calculate owed bounties" +echo " - Process payments (crypto for international)" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ“‹ RECOMMENDED WEEKLY ROUTINE" +echo "─────────────────────────────" +echo "" +echo "Monday:" +echo " - Run this quarterback script" +echo " - Review critical path (Issue #7)" +echo " - Merge ready PRs" +echo "" +echo "Wednesday:" +echo " - Check new issues/comments" +echo " - Respond to developer questions" +echo " - Upload any ready implementations" +echo "" +echo "Friday:" +echo " - Process bounty payments" +echo " - Update team on Discord" +echo " - Plan next week priorities" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ”— QUICK LINKS" +echo "──────────────" +echo "" +echo "Repository: https://github.com/$REPO" +echo "Open Issues: https://github.com/$REPO/issues" +echo "Open PRs: https://github.com/$REPO/pulls" +echo "Discord: https://discord.gg/uCqHvxjU83" +echo "Project Board: https://github.com/orgs/cortexlinux/projects" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "šŸ“± POST TO DISCORD" +echo "──────────────────" +echo "" + +discord_announcement="šŸŽ‰ **Team Update - November 17, 2025** + +**Welcome 5 New Developers!** +- @AbuBakar877 (Turkey) - Full-stack web specialist +- @aliraza556 (Global) - Elite tier, 1000+ contributions +- @anees4500 - Multi-language backend expert +- @brymut (Kenya) - TypeScript + Python architect +- @shalinibhavi525-sudo (Ireland) - Documentation specialist + +**Strategic Assignments Made:** +- Issue #31 (Plugin System) → @brymut (architectural perfect match) +- Issue #10 (Installation Verification) → @aliraza556 +- Issue #32 (Batch Operations) → @anees4500 +- Issue #27 (Progress UI) → @AbuBakar877 +- Issue #15 (Documentation) → @shalinibhavi525-sudo āœ… + +**Critical Path:** +- Issue #7 (Package Manager) - THE blocker - @chandrapratnamar working PR #17 +- Monitoring weekly, need completion for MVP + +**Ready to Review:** +- Multiple PRs waiting for review +- Bounties ready to process on merge + +**The Hybrid Model Works:** +- 63% cost savings +- 80% time savings +- Professional baseline + contributor validation +- Win-win for everyone + +šŸ’° **Bounties:** \$25-200 on merge + 2x bonus at funding +šŸŽÆ **Goal:** MVP complete for February 2025 seed round +šŸ’¼ **Opportunities:** Founding team roles for top contributors + +Browse issues: https://github.com/$REPO/issues +Questions? #dev-questions channel + +Let's build the future of Linux! 🧠⚔" + +echo "$discord_announcement" +echo "" +echo "Copy the above message and post to Discord #announcements" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Section 4 Complete: Team coordination completed" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# ============================================================================ +# FINAL SUMMARY +# ============================================================================ + +echo "" +echo "════════════════════════════════════════════════════════" +echo "šŸ† CORTEX QUARTERBACK SCRIPT - EXECUTION COMPLETE" +echo "════════════════════════════════════════════════════════" +echo "" + +echo "šŸ“Š EXECUTION SUMMARY" +echo "────────────────────" +echo "" +echo "āœ… 5 developers welcomed with personalized messages" +echo "āœ… 10+ strategic issue assignments made" +echo "āœ… PR review guidance provided" +echo "āœ… Team coordination plan established" +echo "āœ… Discord announcement prepared" +echo "" + +echo "šŸŽÆ YOUR NEXT STEPS (Priority Order)" +echo "────────────────────────────────────" +echo "" +echo "1. Post Discord announcement (message above)" +echo "2. Review PR #17 (Issue #7 - THE BLOCKER)" +echo "3. Check for new developer comments" +echo "4. Upload ready implementations (Issues #10, #12, #14)" +echo "5. Process any merged PR bounties" +echo "" + +echo "šŸ’” STRATEGIC RECOMMENDATIONS" +echo "─────────────────────────────" +echo "" +echo "āœ… aliraza556 - Elite tier, consider for senior role/CTO discussion" +echo "āœ… brymut - Perfect skills for Plugin System (#31), high potential" +echo "āš ļø anees4500 - New, monitor first contribution quality" +echo "āœ… AbuBakar877 - Keep on web UI work, avoid core systems" +echo "āœ… shalinibhavi525-sudo - Perfect for docs, complement with testing" +echo "" + +echo "šŸ”„ CRITICAL PATH REMINDER" +echo "──────────────────────────" +echo "" +echo "Issue #7 (Package Manager Wrapper) is THE BLOCKER for MVP." +echo "Everything else can proceed in parallel, but #7 must complete." +echo "Check PR #17 weekly, offer assistance to @chandrapratnamar." +echo "" + +echo "════════════════════════════════════════════════════════" +echo "āœ… Ready for next session!" +echo "════════════════════════════════════════════════════════" +echo "" + +echo "Run this script weekly to quarterback your growing team." +echo "The Cortex Linux revolution is accelerating! 🧠⚔" +echo "" diff --git a/scripts/automation/cortex-master-update.sh b/scripts/automation/cortex-master-update.sh old mode 100644 new mode 100755 index 44252b3e..f5afb060 --- a/scripts/automation/cortex-master-update.sh +++ b/scripts/automation/cortex-master-update.sh @@ -1,301 +1,301 @@ -#!/bin/bash -# CORTEX LINUX - MASTER REPOSITORY UPDATE SCRIPT -# Analyzes PRs, merges ready ones, assigns issues, tracks bounties - -set -e - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") -export GH_TOKEN="$GITHUB_TOKEN" - -echo "🧠 CORTEX LINUX - MASTER UPDATE" -echo "================================" -echo "" - -# ============================================================================ -# STEP 1: MERGE READY PRS -# ============================================================================ - -echo "šŸ“Š STEP 1: REVIEWING & MERGING READY PRS" -echo "─────────────────────────────────────────" -echo "" - -# PR #195: Package Manager (dhvll) - REPLACES PR #17 -echo "šŸ”“ PR #195: Package Manager Wrapper (@dhvll)" -echo " Status: MERGEABLE āœ…" -echo " Action: MERGE NOW - This is THE MVP blocker" -echo "" - -gh pr review 195 --repo $REPO --approve --body "āœ… APPROVED - Excellent package manager implementation! This replaces PR #17 and unblocks the entire MVP. Outstanding work @dhvll!" - -gh pr merge 195 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #195 MERGED - MVP BLOCKER CLEARED!" - echo "" - - # Close Issue #7 - gh issue close 7 --repo $REPO --comment "āœ… Completed in PR #195 by @dhvll. Package manager wrapper is live and working!" - - # Close old PR #17 - gh pr close 17 --repo $REPO --comment "Closing in favor of PR #195 which has a cleaner implementation. Thank you @chandrapratamar for the original work - you'll still receive the $100 bounty for your contribution." - - echo "āœ… Issue #7 closed" - echo "āœ… PR #17 closed (superseded)" - echo "" -} || { - echo "āš ļø PR #195 merge failed - check manually" - echo "" -} - -# PR #198: Rollback System (aliraza556) -echo "🟢 PR #198: Installation History & Rollback (@aliraza556)" -echo " Status: MERGEABLE āœ…" -echo " Bounty: $150" -echo "" - -gh pr review 198 --repo $REPO --approve --body "āœ… APPROVED - Comprehensive rollback system! $150 bounty within 48 hours. Outstanding work @aliraza556!" - -gh pr merge 198 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #198 MERGED" - gh issue close 14 --repo $REPO --comment "āœ… Completed in PR #198 by @aliraza556. Rollback system is live!" - echo " šŸ’° Bounty owed: $150 to @aliraza556" - echo "" -} || { - echo "āš ļø PR #198 merge failed" - echo "" -} - -# PR #197: Cleanup (mikejmorgan-ai) -echo "🟢 PR #197: Remove Duplicate Workflow" -echo " Status: MERGEABLE āœ…" -echo "" - -gh pr merge 197 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #197 MERGED" - echo "" -} || { - echo "āš ļø PR #197 merge failed" - echo "" -} - -# PR #21: Config Templates (aliraza556) -echo "🟔 PR #21: Configuration Templates (@aliraza556)" -echo " Status: MERGEABLE āœ…" -echo " Bounty: $150" -echo "" - -gh pr review 21 --repo $REPO --approve --body "āœ… APPROVED - Production-ready config templates! $150 bounty within 48 hours." - -gh pr merge 21 --repo $REPO --squash --delete-branch --admin && { - echo "āœ… PR #21 MERGED" - gh issue close 9 --repo $REPO --comment "āœ… Completed in PR #21. Config templates are live!" - echo " šŸ’° Bounty owed: $150 to @aliraza556" - echo "" -} || { - echo "āš ļø PR #21 merge failed" - echo "" -} - -# PR #38: Requirements Check (AlexanderLuzDH) - HAS CONFLICTS -echo "ā­ļø PR #38: Requirements Checker (@AlexanderLuzDH)" -echo " Status: CONFLICTING āŒ" -echo " Action: Skip - needs contributor to fix conflicts" -echo " Bounty: $100 pending" -echo "" - -# PR #18: CLI Interface (Sahilbhatane) - DRAFT -echo "ā­ļø PR #18: CLI Interface (@Sahilbhatane)" -echo " Status: DRAFT - not ready yet" -echo " Action: Skip" -echo "" - -# ============================================================================ -# STEP 2: ASSIGN UNASSIGNED MVP ISSUES -# ============================================================================ - -echo "" -echo "šŸ“‹ STEP 2: ASSIGNING UNASSIGNED MVP ISSUES" -echo "───────────────────────────────────────────" -echo "" - -# High-value issues that need assignment -MVP_ISSUES=(144 135 131 128 126 125 119 117 112 103 44 25) - -echo "Unassigned MVP issues ready for contributors:" -echo "" - -for issue in "${MVP_ISSUES[@]}"; do - issue_info=$(gh issue view $issue --repo $REPO --json title,assignees,labels 2>/dev/null) - issue_title=$(echo "$issue_info" | jq -r '.title') - assignee_count=$(echo "$issue_info" | jq '.assignees | length') - - if [ "$assignee_count" -eq 0 ]; then - echo " #$issue: $issue_title" - fi -done - -echo "" -echo "These issues are ready for contributors to claim." -echo "Post to Discord: 'MVP issues available - claim in comments!'" -echo "" - -# ============================================================================ -# STEP 3: BOUNTY TRACKING -# ============================================================================ - -echo "" -echo "šŸ’° STEP 3: BOUNTY TRACKING UPDATE" -echo "─────────────────────────────────" -echo "" - -BOUNTY_FILE="$HOME/cortex/bounties_owed.csv" - -if [ ! -f "$BOUNTY_FILE" ]; then - echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTY_FILE" -fi - -# Add new bounties from today's merges -echo "195,dhvll,Package Manager Wrapper,100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" -echo "198,aliraza556,Installation Rollback,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" -echo "21,aliraza556,Config Templates,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" -echo "17,chandrapratamar,Package Manager (original),100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" - -echo "Updated: $BOUNTY_FILE" -echo "" - -echo "BOUNTIES OWED:" -echo "──────────────" -tail -n +2 "$BOUNTY_FILE" | while IFS=',' read -r pr dev feature amount date status; do - if [ "$status" = "PENDING" ]; then - echo " PR #$pr - @$dev: \$$amount ($feature)" - fi -done - -echo "" - -# Calculate totals -total_owed=$(tail -n +2 "$BOUNTY_FILE" | awk -F',' '$6=="PENDING" {sum+=$4} END {print sum}') -echo " Total pending: \$$total_owed" -echo " At 2x bonus (funding): \$$(($total_owed * 2))" -echo "" - -# ============================================================================ -# STEP 4: GENERATE STATUS REPORT -# ============================================================================ - -echo "" -echo "šŸ“Š STEP 4: FINAL STATUS REPORT" -echo "───────────────────────────────" -echo "" - -echo "=== CORTEX REPOSITORY STATUS ===" -echo "" - -# Count current state -open_prs=$(gh pr list --repo $REPO --state open --json number | jq 'length') -open_issues=$(gh issue list --repo $REPO --state open --json number | jq 'length') - -echo "PRs:" -echo " Open: $open_prs" -echo " Merged today: 4 (PRs #195, #198, #197, #21)" -echo "" - -echo "Issues:" -echo " Open: $open_issues" -echo " Closed today: 2 (Issues #7, #14)" -echo "" - -echo "MVP Status:" -echo " āœ… Package Manager: COMPLETE (PR #195)" -echo " āœ… Rollback System: COMPLETE (PR #198)" -echo " āœ… Config Templates: COMPLETE (PR #21)" -echo " āœ… Hardware Detection: COMPLETE" -echo " āœ… Dependencies: COMPLETE" -echo " āœ… Verification: COMPLETE" -echo " āœ… Error Parsing: COMPLETE" -echo " āœ… Context Memory: COMPLETE" -echo " āœ… Logging: COMPLETE" -echo " āœ… Progress UI: COMPLETE" -echo " ā³ Requirements Check: Conflicts (PR #38)" -echo "" -echo " MVP COMPLETE: 95%" -echo "" - -echo "Bounties:" -echo " Owed: \$$total_owed" -echo " Contributors to pay: @dhvll, @aliraza556 (x2), @chandrapratamar" -echo "" - -# ============================================================================ -# STEP 5: DISCORD ANNOUNCEMENT -# ============================================================================ - -echo "" -echo "šŸ“± STEP 5: DISCORD ANNOUNCEMENT (COPY & POST)" -echo "─────────────────────────────────────────────" -echo "" - -cat << 'DISCORD' -šŸŽ‰ **MAJOR MVP MILESTONE - November 17, 2025** - -**BREAKTHROUGH: Package Manager MERGED! šŸš€** - -PR #195 by @dhvll just merged - THE critical MVP blocker is cleared! - -**Today's Merges:** -āœ… PR #195 - Package Manager Wrapper (@dhvll) -āœ… PR #198 - Installation Rollback (@aliraza556) -āœ… PR #21 - Config File Templates (@aliraza556) -āœ… PR #197 - Workflow Cleanup - -**Issues Closed:** -āœ… #7 - Package Manager (9 days → DONE!) -āœ… #14 - Rollback System - -**MVP Status: 95% COMPLETE** šŸŽÆ - -**What This Means:** -- Core "cortex install" functionality working -- Natural language → apt commands = LIVE -- Rollback safety net = LIVE -- Production-ready config templates = LIVE - -**Bounties Being Processed:** -- @dhvll: $100 -- @aliraza556: $300 ($150 x 2 PRs!) -- @chandrapratamar: $100 -Total: $500 (+ 2x at funding = $1000) - -**Available Issues:** -10+ MVP features ready to claim - check GitHub issues! - -**Next: Demo preparation for February 2025 funding round** - -We're making history! 🧠⚔ - -https://github.com/cortexlinux/cortex -DISCORD - -echo "" -echo "─────────────────────────────────────────────" -echo "" - -# ============================================================================ -# STEP 6: NEXT STEPS -# ============================================================================ - -echo "šŸŽÆ NEXT STEPS" -echo "─────────────" -echo "" -echo "1. Post Discord announcement above to #announcements" -echo "2. Coordinate payments with:" -echo " - @dhvll ($100)" -echo " - @aliraza556 ($300)" -echo " - @chandrapratamar ($100)" -echo "3. Wait for PR #38 conflict resolution" -echo "4. Create demo script: 'cortex install oracle-23-ai'" -echo "5. Prepare investor presentation materials" -echo "" - -echo "āœ… MASTER UPDATE COMPLETE" -echo "" -echo "Repository is MVP-ready for February 2025 funding!" +#!/bin/bash +# CORTEX LINUX - MASTER REPOSITORY UPDATE SCRIPT +# Analyzes PRs, merges ready ones, assigns issues, tracks bounties + +set -e + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") +export GH_TOKEN="$GITHUB_TOKEN" + +echo "🧠 CORTEX LINUX - MASTER UPDATE" +echo "================================" +echo "" + +# ============================================================================ +# STEP 1: MERGE READY PRS +# ============================================================================ + +echo "šŸ“Š STEP 1: REVIEWING & MERGING READY PRS" +echo "─────────────────────────────────────────" +echo "" + +# PR #195: Package Manager (dhvll) - REPLACES PR #17 +echo "šŸ”“ PR #195: Package Manager Wrapper (@dhvll)" +echo " Status: MERGEABLE āœ…" +echo " Action: MERGE NOW - This is THE MVP blocker" +echo "" + +gh pr review 195 --repo $REPO --approve --body "āœ… APPROVED - Excellent package manager implementation! This replaces PR #17 and unblocks the entire MVP. Outstanding work @dhvll!" + +gh pr merge 195 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #195 MERGED - MVP BLOCKER CLEARED!" + echo "" + + # Close Issue #7 + gh issue close 7 --repo $REPO --comment "āœ… Completed in PR #195 by @dhvll. Package manager wrapper is live and working!" + + # Close old PR #17 + gh pr close 17 --repo $REPO --comment "Closing in favor of PR #195 which has a cleaner implementation. Thank you @chandrapratamar for the original work - you'll still receive the $100 bounty for your contribution." + + echo "āœ… Issue #7 closed" + echo "āœ… PR #17 closed (superseded)" + echo "" +} || { + echo "āš ļø PR #195 merge failed - check manually" + echo "" +} + +# PR #198: Rollback System (aliraza556) +echo "🟢 PR #198: Installation History & Rollback (@aliraza556)" +echo " Status: MERGEABLE āœ…" +echo " Bounty: $150" +echo "" + +gh pr review 198 --repo $REPO --approve --body "āœ… APPROVED - Comprehensive rollback system! $150 bounty within 48 hours. Outstanding work @aliraza556!" + +gh pr merge 198 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #198 MERGED" + gh issue close 14 --repo $REPO --comment "āœ… Completed in PR #198 by @aliraza556. Rollback system is live!" + echo " šŸ’° Bounty owed: $150 to @aliraza556" + echo "" +} || { + echo "āš ļø PR #198 merge failed" + echo "" +} + +# PR #197: Cleanup (mikejmorgan-ai) +echo "🟢 PR #197: Remove Duplicate Workflow" +echo " Status: MERGEABLE āœ…" +echo "" + +gh pr merge 197 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #197 MERGED" + echo "" +} || { + echo "āš ļø PR #197 merge failed" + echo "" +} + +# PR #21: Config Templates (aliraza556) +echo "🟔 PR #21: Configuration Templates (@aliraza556)" +echo " Status: MERGEABLE āœ…" +echo " Bounty: $150" +echo "" + +gh pr review 21 --repo $REPO --approve --body "āœ… APPROVED - Production-ready config templates! $150 bounty within 48 hours." + +gh pr merge 21 --repo $REPO --squash --delete-branch --admin && { + echo "āœ… PR #21 MERGED" + gh issue close 9 --repo $REPO --comment "āœ… Completed in PR #21. Config templates are live!" + echo " šŸ’° Bounty owed: $150 to @aliraza556" + echo "" +} || { + echo "āš ļø PR #21 merge failed" + echo "" +} + +# PR #38: Requirements Check (AlexanderLuzDH) - HAS CONFLICTS +echo "ā­ļø PR #38: Requirements Checker (@AlexanderLuzDH)" +echo " Status: CONFLICTING āŒ" +echo " Action: Skip - needs contributor to fix conflicts" +echo " Bounty: $100 pending" +echo "" + +# PR #18: CLI Interface (Sahilbhatane) - DRAFT +echo "ā­ļø PR #18: CLI Interface (@Sahilbhatane)" +echo " Status: DRAFT - not ready yet" +echo " Action: Skip" +echo "" + +# ============================================================================ +# STEP 2: ASSIGN UNASSIGNED MVP ISSUES +# ============================================================================ + +echo "" +echo "šŸ“‹ STEP 2: ASSIGNING UNASSIGNED MVP ISSUES" +echo "───────────────────────────────────────────" +echo "" + +# High-value issues that need assignment +MVP_ISSUES=(144 135 131 128 126 125 119 117 112 103 44 25) + +echo "Unassigned MVP issues ready for contributors:" +echo "" + +for issue in "${MVP_ISSUES[@]}"; do + issue_info=$(gh issue view $issue --repo $REPO --json title,assignees,labels 2>/dev/null) + issue_title=$(echo "$issue_info" | jq -r '.title') + assignee_count=$(echo "$issue_info" | jq '.assignees | length') + + if [ "$assignee_count" -eq 0 ]; then + echo " #$issue: $issue_title" + fi +done + +echo "" +echo "These issues are ready for contributors to claim." +echo "Post to Discord: 'MVP issues available - claim in comments!'" +echo "" + +# ============================================================================ +# STEP 3: BOUNTY TRACKING +# ============================================================================ + +echo "" +echo "šŸ’° STEP 3: BOUNTY TRACKING UPDATE" +echo "─────────────────────────────────" +echo "" + +BOUNTY_FILE="$HOME/cortex/bounties_owed.csv" + +if [ ! -f "$BOUNTY_FILE" ]; then + echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTY_FILE" +fi + +# Add new bounties from today's merges +echo "195,dhvll,Package Manager Wrapper,100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" +echo "198,aliraza556,Installation Rollback,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" +echo "21,aliraza556,Config Templates,150,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" +echo "17,chandrapratamar,Package Manager (original),100,$(date +%Y-%m-%d),PENDING" >> "$BOUNTY_FILE" + +echo "Updated: $BOUNTY_FILE" +echo "" + +echo "BOUNTIES OWED:" +echo "──────────────" +tail -n +2 "$BOUNTY_FILE" | while IFS=',' read -r pr dev feature amount date status; do + if [ "$status" = "PENDING" ]; then + echo " PR #$pr - @$dev: \$$amount ($feature)" + fi +done + +echo "" + +# Calculate totals +total_owed=$(tail -n +2 "$BOUNTY_FILE" | awk -F',' '$6=="PENDING" {sum+=$4} END {print sum}') +echo " Total pending: \$$total_owed" +echo " At 2x bonus (funding): \$$(($total_owed * 2))" +echo "" + +# ============================================================================ +# STEP 4: GENERATE STATUS REPORT +# ============================================================================ + +echo "" +echo "šŸ“Š STEP 4: FINAL STATUS REPORT" +echo "───────────────────────────────" +echo "" + +echo "=== CORTEX REPOSITORY STATUS ===" +echo "" + +# Count current state +open_prs=$(gh pr list --repo $REPO --state open --json number | jq 'length') +open_issues=$(gh issue list --repo $REPO --state open --json number | jq 'length') + +echo "PRs:" +echo " Open: $open_prs" +echo " Merged today: 4 (PRs #195, #198, #197, #21)" +echo "" + +echo "Issues:" +echo " Open: $open_issues" +echo " Closed today: 2 (Issues #7, #14)" +echo "" + +echo "MVP Status:" +echo " āœ… Package Manager: COMPLETE (PR #195)" +echo " āœ… Rollback System: COMPLETE (PR #198)" +echo " āœ… Config Templates: COMPLETE (PR #21)" +echo " āœ… Hardware Detection: COMPLETE" +echo " āœ… Dependencies: COMPLETE" +echo " āœ… Verification: COMPLETE" +echo " āœ… Error Parsing: COMPLETE" +echo " āœ… Context Memory: COMPLETE" +echo " āœ… Logging: COMPLETE" +echo " āœ… Progress UI: COMPLETE" +echo " ā³ Requirements Check: Conflicts (PR #38)" +echo "" +echo " MVP COMPLETE: 95%" +echo "" + +echo "Bounties:" +echo " Owed: \$$total_owed" +echo " Contributors to pay: @dhvll, @aliraza556 (x2), @chandrapratamar" +echo "" + +# ============================================================================ +# STEP 5: DISCORD ANNOUNCEMENT +# ============================================================================ + +echo "" +echo "šŸ“± STEP 5: DISCORD ANNOUNCEMENT (COPY & POST)" +echo "─────────────────────────────────────────────" +echo "" + +cat << 'DISCORD' +šŸŽ‰ **MAJOR MVP MILESTONE - November 17, 2025** + +**BREAKTHROUGH: Package Manager MERGED! šŸš€** + +PR #195 by @dhvll just merged - THE critical MVP blocker is cleared! + +**Today's Merges:** +āœ… PR #195 - Package Manager Wrapper (@dhvll) +āœ… PR #198 - Installation Rollback (@aliraza556) +āœ… PR #21 - Config File Templates (@aliraza556) +āœ… PR #197 - Workflow Cleanup + +**Issues Closed:** +āœ… #7 - Package Manager (9 days → DONE!) +āœ… #14 - Rollback System + +**MVP Status: 95% COMPLETE** šŸŽÆ + +**What This Means:** +- Core "cortex install" functionality working +- Natural language → apt commands = LIVE +- Rollback safety net = LIVE +- Production-ready config templates = LIVE + +**Bounties Being Processed:** +- @dhvll: $100 +- @aliraza556: $300 ($150 x 2 PRs!) +- @chandrapratamar: $100 +Total: $500 (+ 2x at funding = $1000) + +**Available Issues:** +10+ MVP features ready to claim - check GitHub issues! + +**Next: Demo preparation for February 2025 funding round** + +We're making history! 🧠⚔ + +https://github.com/cortexlinux/cortex +DISCORD + +echo "" +echo "─────────────────────────────────────────────" +echo "" + +# ============================================================================ +# STEP 6: NEXT STEPS +# ============================================================================ + +echo "šŸŽÆ NEXT STEPS" +echo "─────────────" +echo "" +echo "1. Post Discord announcement above to #announcements" +echo "2. Coordinate payments with:" +echo " - @dhvll ($100)" +echo " - @aliraza556 ($300)" +echo " - @chandrapratamar ($100)" +echo "3. Wait for PR #38 conflict resolution" +echo "4. Create demo script: 'cortex install oracle-23-ai'" +echo "5. Prepare investor presentation materials" +echo "" + +echo "āœ… MASTER UPDATE COMPLETE" +echo "" +echo "Repository is MVP-ready for February 2025 funding!" diff --git a/scripts/automation/cortex-master.sh b/scripts/automation/cortex-master.sh old mode 100644 new mode 100755 index 3b2d6ff7..94e485b0 --- a/scripts/automation/cortex-master.sh +++ b/scripts/automation/cortex-master.sh @@ -1,194 +1,194 @@ -#!/bin/bash -# Cortex Linux - Master MVP Automation System -# One script to rule them all - -set -e - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -REPO_DIR="$HOME/cortex" -WORK_DIR="$HOME/Downloads/cortex-work" -mkdir -p "$WORK_DIR" - -print_banner() { - echo -e "${BLUE}" - echo "╔════════════════════════════════════════════════╗" - echo "ā•‘ CORTEX LINUX - MVP MASTER AUTOMATION ā•‘" - echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" - echo -e "${NC}" -} - -show_menu() { - echo "" - echo -e "${GREEN}═══ MAIN MENU ═══${NC}" - echo "" - echo "1. Show MVP dashboard" - echo "2. List MVP-critical issues" - echo "3. Create PR for issue #10" - echo "4. Review pending PRs" - echo "5. Merge PR" - echo "6. List contributors" - echo "7. Assign issue to contributor" - echo "8. Process bounty payment" - echo "9. Generate weekly report" - echo "10. Full repository audit" - echo "" - echo "0. Exit" - echo "" - echo -n "Select: " -} - -show_dashboard() { - cd "$REPO_DIR" - echo -e "${BLUE}═══ CORTEX MVP DASHBOARD ═══${NC}" - echo "" - echo "šŸ“Š Issues:" - echo " Total: $(gh issue list --limit 1000 --json number | jq '. | length')" - echo " MVP Critical: $(gh issue list --label 'mvp-critical' --json number | jq '. | length')" - echo "" - echo "šŸ”€ Pull Requests:" - echo " Open: $(gh pr list --json number | jq '. | length')" - echo "" - echo "šŸ‘„ Recent activity:" - gh pr list --state all --limit 5 --json number,title,author | \ - jq -r '.[] | " PR #\(.number): \(.title) (@\(.author.login))"' -} - -list_mvp() { - cd "$REPO_DIR" - echo -e "${GREEN}šŸ“‹ MVP-Critical Issues:${NC}" - gh issue list --label "mvp-critical" --limit 20 --json number,title,assignees | \ - jq -r '.[] | " #\(.number): \(.title)"' -} - -create_pr_issue10() { - cd "$REPO_DIR" - git checkout feature/issue-10 2>/dev/null || { - echo "Branch feature/issue-10 not found" - return 1 - } - - gh pr create \ - --title "Add Installation Verification System - Fixes #10" \ - --body "Complete implementation: 918 lines (code+tests+docs). Ready for review." \ - --label "enhancement,ready-for-review,priority: critical" - - git checkout main - echo "āœ… PR created!" -} - -review_prs() { - cd "$REPO_DIR" - echo -e "${GREEN}šŸ“‹ Open Pull Requests:${NC}" - gh pr list --json number,title,author,createdAt | \ - jq -r '.[] | " PR #\(.number): \(.title)\n Author: @\(.author.login)\n Created: \(.createdAt)\n"' -} - -merge_pr() { - echo -n "PR number to merge: " - read pr_num - cd "$REPO_DIR" - gh pr merge $pr_num --squash --delete-branch - echo "āœ… Merged!" -} - -list_contributors() { - cd "$REPO_DIR" - echo -e "${GREEN}šŸ‘„ Active Contributors:${NC}" - gh pr list --state all --limit 50 --json author | \ - jq -r '.[].author.login' | sort | uniq -c | sort -rn | head -10 -} - -assign_issue() { - echo -n "Issue #: " - read issue - echo -n "Assign to (username): " - read user - cd "$REPO_DIR" - gh issue edit $issue --add-assignee "$user" - gh issue comment $issue --body "šŸ‘‹ @$user - This is assigned to you! Questions? Ask in Discord." - echo "āœ… Assigned!" -} - -process_bounty() { - echo -n "PR #: " - read pr - echo -n "Username: " - read user - echo -n "Amount $: " - read amount - - cd "$REPO_DIR" - gh pr comment $pr --body "šŸ’° **Bounty Approved: \$$amount** - -@$user - DM me your payment method. Payment Friday. Plus 2x bonus at funding! - -Thanks! šŸŽ‰" - - echo "āœ… Bounty processed!" -} - -weekly_report() { - cd "$REPO_DIR" - echo "# Cortex Linux - Weekly Report" - echo "Week of $(date +%Y-%m-%d)" - echo "" - echo "## PRs This Week" - gh pr list --state merged --limit 10 --json number,title | \ - jq -r '.[] | "- PR #\(.number): \(.title)"' - echo "" - echo "## Metrics" - echo "- Open Issues: $(gh issue list --json number | jq '. | length')" - echo "- Open PRs: $(gh pr list --json number | jq '. | length')" -} - -audit_repo() { - cd "$REPO_DIR" - echo "Repository: cortexlinux/cortex" - echo "Branch: $(git branch --show-current)" - echo "Last commit: $(git log -1 --oneline)" - echo "" - echo "Issues: $(gh issue list --json number | jq '. | length') open" - echo "PRs: $(gh pr list --json number | jq '. | length') open" - echo "" - echo "Recent activity:" - gh run list --limit 3 -} - -main() { - print_banner - - cd "$REPO_DIR" 2>/dev/null || { - echo "āŒ Repo not found at $REPO_DIR" - exit 1 - } - - while true; do - show_menu - read choice - - case $choice in - 1) show_dashboard ;; - 2) list_mvp ;; - 3) create_pr_issue10 ;; - 4) review_prs ;; - 5) merge_pr ;; - 6) list_contributors ;; - 7) assign_issue ;; - 8) process_bounty ;; - 9) weekly_report ;; - 10) audit_repo ;; - 0) echo "Goodbye!"; exit 0 ;; - *) echo "Invalid option" ;; - esac - - echo "" - read -p "Press Enter..." - done -} - -main +#!/bin/bash +# Cortex Linux - Master MVP Automation System +# One script to rule them all + +set -e + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +REPO_DIR="$HOME/cortex" +WORK_DIR="$HOME/Downloads/cortex-work" +mkdir -p "$WORK_DIR" + +print_banner() { + echo -e "${BLUE}" + echo "╔════════════════════════════════════════════════╗" + echo "ā•‘ CORTEX LINUX - MVP MASTER AUTOMATION ā•‘" + echo "ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•" + echo -e "${NC}" +} + +show_menu() { + echo "" + echo -e "${GREEN}═══ MAIN MENU ═══${NC}" + echo "" + echo "1. Show MVP dashboard" + echo "2. List MVP-critical issues" + echo "3. Create PR for issue #10" + echo "4. Review pending PRs" + echo "5. Merge PR" + echo "6. List contributors" + echo "7. Assign issue to contributor" + echo "8. Process bounty payment" + echo "9. Generate weekly report" + echo "10. Full repository audit" + echo "" + echo "0. Exit" + echo "" + echo -n "Select: " +} + +show_dashboard() { + cd "$REPO_DIR" + echo -e "${BLUE}═══ CORTEX MVP DASHBOARD ═══${NC}" + echo "" + echo "šŸ“Š Issues:" + echo " Total: $(gh issue list --limit 1000 --json number | jq '. | length')" + echo " MVP Critical: $(gh issue list --label 'mvp-critical' --json number | jq '. | length')" + echo "" + echo "šŸ”€ Pull Requests:" + echo " Open: $(gh pr list --json number | jq '. | length')" + echo "" + echo "šŸ‘„ Recent activity:" + gh pr list --state all --limit 5 --json number,title,author | \ + jq -r '.[] | " PR #\(.number): \(.title) (@\(.author.login))"' +} + +list_mvp() { + cd "$REPO_DIR" + echo -e "${GREEN}šŸ“‹ MVP-Critical Issues:${NC}" + gh issue list --label "mvp-critical" --limit 20 --json number,title,assignees | \ + jq -r '.[] | " #\(.number): \(.title)"' +} + +create_pr_issue10() { + cd "$REPO_DIR" + git checkout feature/issue-10 2>/dev/null || { + echo "Branch feature/issue-10 not found" + return 1 + } + + gh pr create \ + --title "Add Installation Verification System - Fixes #10" \ + --body "Complete implementation: 918 lines (code+tests+docs). Ready for review." \ + --label "enhancement,ready-for-review,priority: critical" + + git checkout main + echo "āœ… PR created!" +} + +review_prs() { + cd "$REPO_DIR" + echo -e "${GREEN}šŸ“‹ Open Pull Requests:${NC}" + gh pr list --json number,title,author,createdAt | \ + jq -r '.[] | " PR #\(.number): \(.title)\n Author: @\(.author.login)\n Created: \(.createdAt)\n"' +} + +merge_pr() { + echo -n "PR number to merge: " + read pr_num + cd "$REPO_DIR" + gh pr merge $pr_num --squash --delete-branch + echo "āœ… Merged!" +} + +list_contributors() { + cd "$REPO_DIR" + echo -e "${GREEN}šŸ‘„ Active Contributors:${NC}" + gh pr list --state all --limit 50 --json author | \ + jq -r '.[].author.login' | sort | uniq -c | sort -rn | head -10 +} + +assign_issue() { + echo -n "Issue #: " + read issue + echo -n "Assign to (username): " + read user + cd "$REPO_DIR" + gh issue edit $issue --add-assignee "$user" + gh issue comment $issue --body "šŸ‘‹ @$user - This is assigned to you! Questions? Ask in Discord." + echo "āœ… Assigned!" +} + +process_bounty() { + echo -n "PR #: " + read pr + echo -n "Username: " + read user + echo -n "Amount $: " + read amount + + cd "$REPO_DIR" + gh pr comment $pr --body "šŸ’° **Bounty Approved: \$$amount** + +@$user - DM me your payment method. Payment Friday. Plus 2x bonus at funding! + +Thanks! šŸŽ‰" + + echo "āœ… Bounty processed!" +} + +weekly_report() { + cd "$REPO_DIR" + echo "# Cortex Linux - Weekly Report" + echo "Week of $(date +%Y-%m-%d)" + echo "" + echo "## PRs This Week" + gh pr list --state merged --limit 10 --json number,title | \ + jq -r '.[] | "- PR #\(.number): \(.title)"' + echo "" + echo "## Metrics" + echo "- Open Issues: $(gh issue list --json number | jq '. | length')" + echo "- Open PRs: $(gh pr list --json number | jq '. | length')" +} + +audit_repo() { + cd "$REPO_DIR" + echo "Repository: cortexlinux/cortex" + echo "Branch: $(git branch --show-current)" + echo "Last commit: $(git log -1 --oneline)" + echo "" + echo "Issues: $(gh issue list --json number | jq '. | length') open" + echo "PRs: $(gh pr list --json number | jq '. | length') open" + echo "" + echo "Recent activity:" + gh run list --limit 3 +} + +main() { + print_banner + + cd "$REPO_DIR" 2>/dev/null || { + echo "āŒ Repo not found at $REPO_DIR" + exit 1 + } + + while true; do + show_menu + read choice + + case $choice in + 1) show_dashboard ;; + 2) list_mvp ;; + 3) create_pr_issue10 ;; + 4) review_prs ;; + 5) merge_pr ;; + 6) list_contributors ;; + 7) assign_issue ;; + 8) process_bounty ;; + 9) weekly_report ;; + 10) audit_repo ;; + 0) echo "Goodbye!"; exit 0 ;; + *) echo "Invalid option" ;; + esac + + echo "" + read -p "Press Enter..." + done +} + +main diff --git a/scripts/automation/cortex-pr-dashboard.sh b/scripts/automation/cortex-pr-dashboard.sh old mode 100644 new mode 100755 index b7a33ed9..df0b42df --- a/scripts/automation/cortex-pr-dashboard.sh +++ b/scripts/automation/cortex-pr-dashboard.sh @@ -1,362 +1,362 @@ -#!/bin/bash -# CORTEX - MASTER PR DASHBOARD & MANAGEMENT -# Complete PR overview, batch operations, and bounty tracking - -set -e - -echo "šŸŽ›ļø CORTEX - MASTER PR DASHBOARD" -echo "================================" -echo "" - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") -export GH_TOKEN="$GITHUB_TOKEN" - -# Colors for terminal output -RED='\033[0;31m' -YELLOW='\033[1;33m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ“Š PR STATUS OVERVIEW" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Get all open PRs -prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,isDraft,reviewDecision --limit 50 2>/dev/null) - -total_prs=$(echo "$prs" | jq 'length') -contributor_prs=$(echo "$prs" | jq '[.[] | select(.author.login != "mikejmorgan-ai")] | length') -mike_prs=$(echo "$prs" | jq '[.[] | select(.author.login == "mikejmorgan-ai")] | length') - -echo "Total Open PRs: $total_prs" -echo " ā”œā”€ From Contributors: $contributor_prs (šŸ”„ Need review)" -echo " └─ From Mike: $mike_prs (Can merge anytime)" -echo "" - -# Calculate bounties at stake -echo "šŸ’° ESTIMATED BOUNTIES AT STAKE" -echo "────────────────────────────────" -echo "" - -declare -A BOUNTY_MAP -BOUNTY_MAP[17]=100 # Package Manager -BOUNTY_MAP[37]=125 # Progress Notifications -BOUNTY_MAP[38]=100 # Requirements Check -BOUNTY_MAP[21]=150 # Config Templates -BOUNTY_MAP[18]=100 # CLI Interface - -total_contributor_bounties=0 - -for pr in 17 37 38 21 18; do - bounty=${BOUNTY_MAP[$pr]} - total_contributor_bounties=$((total_contributor_bounties + bounty)) -done - -echo "Contributor PRs: \$$total_contributor_bounties" -echo "At 2x bonus (funding): \$$((total_contributor_bounties * 2))" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ”“ CRITICAL PRIORITY" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -pr17_info=$(gh pr view 17 --repo $REPO --json number,title,author,createdAt,state 2>/dev/null) -pr17_title=$(echo "$pr17_info" | jq -r '.title') -pr17_author=$(echo "$pr17_info" | jq -r '.author.login') -pr17_created=$(echo "$pr17_info" | jq -r '.createdAt' | cut -d'T' -f1) -pr17_days_old=$(( ( $(date +%s) - $(date -j -f "%Y-%m-%d" "$pr17_created" +%s 2>/dev/null || date +%s) ) / 86400 )) - -echo "PR #17: $pr17_title" -echo "Author: @$pr17_author" -echo "Age: $pr17_days_old days old" -echo "Bounty: \$100" -echo "Impact: āš ļø MVP BLOCKER - Everything waits on this" -echo "" -echo -e "${RED}ā–¶ ACTION REQUIRED: Review this PR FIRST${NC}" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "🟔 HIGH PRIORITY (Contributors Waiting)" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -for pr in 37 38 21; do - pr_info=$(gh pr view $pr --repo $REPO --json number,title,author,createdAt 2>/dev/null) - pr_title=$(echo "$pr_info" | jq -r '.title') - pr_author=$(echo "$pr_info" | jq -r '.author.login') - pr_bounty=${BOUNTY_MAP[$pr]} - - echo "PR #$pr: $pr_title" - echo " Author: @$pr_author | Bounty: \$$pr_bounty" -done - -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "🟢 MIKE'S PRs (Ready to Merge)" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -mike_pr_list=$(echo "$prs" | jq -r '.[] | select(.author.login == "mikejmorgan-ai") | .number') - -for pr in $mike_pr_list; do - pr_info=$(gh pr view $pr --repo $REPO --json number,title 2>/dev/null) - pr_title=$(echo "$pr_info" | jq -r '.title') - echo "PR #$pr: $pr_title" -done - -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸŽÆ QUICK ACTIONS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -echo "What would you like to do?" -echo "" -echo " [1] Review PR #17 (THE CRITICAL BLOCKER) šŸ”“" -echo " [2] Review ALL contributor PRs (guided workflow) 🟔" -echo " [3] Merge ALL of Mike's PRs (batch operation) 🟢" -echo " [4] View detailed PR list in browser" -echo " [5] Generate bounty payment report" -echo " [6] Post Discord update" -echo " [q] Quit" -echo "" -echo -n "Choose action: " -read -n 1 choice -echo "" -echo "" - -case $choice in - 1) - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸ”“ REVIEWING PR #17 - PACKAGE MANAGER WRAPPER" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "This is THE MVP blocker. Everything depends on this." - echo "" - echo "Opening in browser for review..." - echo "" - - gh pr view 17 --repo $REPO --web - - echo "" - echo "After reviewing the code, what's your decision?" - echo "" - echo " [a] Approve & Merge (\$100 bounty to @chandrapratamar)" - echo " [c] Request Changes (specify what needs fixing)" - echo " [s] Skip for now (review later)" - echo "" - echo -n "Decision: " - read -n 1 decision - echo "" - echo "" - - case $decision in - a|A) - echo "āœ… Approving PR #17..." - - approval="āœ… **APPROVED - OUTSTANDING WORK!** - -@chandrapratamar - You just unblocked the entire MVP! šŸŽ‰šŸŽ‰šŸŽ‰ - -**This is THE critical feature** that everything else depends on. Your implementation: -- āœ… Translates natural language to apt commands perfectly -- āœ… Integrates seamlessly with our LLM layer -- āœ… Includes comprehensive tests -- āœ… Documentation is clear and complete - -**Payment Details:** -- **Bounty: \$100 USD** -- **Processing: Within 48 hours** -- **Method: Crypto (Bitcoin/USDC) or PayPal** -- **Bonus: 2x at funding (Feb 2025) = \$200 total** - -**You're now a core Cortex contributor!** 🧠⚔ - -We'll coordinate payment via your preferred method in the next comment. - -**Thank you for making history with us!** - ---- -*Automated approval from Cortex PR Management System*" - - echo "$approval" | gh pr review 17 --repo $REPO --approve --body-file - - - echo "" - echo "Merging PR #17..." - - gh pr merge 17 --repo $REPO --squash --delete-branch && { - echo "" - echo "šŸŽ‰šŸŽ‰šŸŽ‰ PR #17 MERGED! šŸŽ‰šŸŽ‰šŸŽ‰" - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸš€ MVP BLOCKER CLEARED!" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "This unblocks:" - echo " āœ… Issue #12 (Dependency Resolution)" - echo " āœ… Issue #10 (Installation Verification)" - echo " āœ… Issue #14 (Rollback System)" - echo " āœ… MVP demonstration" - echo " āœ… February funding timeline" - echo "" - echo "šŸ’° Bounty owed: \$100 to @chandrapratamar" - echo "" - echo "IMMEDIATELY post to Discord #announcements!" - echo "" - } || { - echo "āŒ Merge failed - needs manual intervention" - } - ;; - c|C) - echo "Requesting changes on PR #17..." - echo "" - echo "Enter what needs to change:" - echo "(Press Ctrl+D when done)" - echo "---" - feedback=$(cat) - - change_request="šŸ”„ **Changes Requested** - -Thank you @chandrapratamar for tackling this critical feature! - -Before we can merge, please address: - -$feedback - -**This is THE MVP blocker**, so I'll prioritize re-review once you update. - -Questions? Ping me here or in Discord (#dev-questions). - -We're close! šŸ’Ŗ" - - echo "$change_request" | gh pr review 17 --repo $REPO --request-changes --body-file - - echo "" - echo "āœ… Change request posted" - ;; - *) - echo "ā­ļø Skipped PR #17" - ;; - esac - ;; - - 2) - echo "🟔 LAUNCHING CONTRIBUTOR PR REVIEW WORKFLOW..." - echo "" - - # Check if review script exists - if [ -f "$HOME/cortex/review-contributor-prs.sh" ]; then - bash "$HOME/cortex/review-contributor-prs.sh" - else - echo "Review script not found. Download it first:" - echo " review-contributor-prs.sh" - fi - ;; - - 3) - echo "🟢 BATCH MERGING MIKE'S PRs..." - echo "" - - # Check if merge script exists - if [ -f "$HOME/cortex/merge-mike-prs.sh" ]; then - bash "$HOME/cortex/merge-mike-prs.sh" - else - echo "Merge script not found. Download it first:" - echo " merge-mike-prs.sh" - fi - ;; - - 4) - echo "🌐 Opening PR list in browser..." - gh pr list --repo $REPO --web - ;; - - 5) - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸ’° BOUNTY PAYMENT REPORT" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - - echo "PENDING BOUNTIES (if merged):" - echo "──────────────────────────────" - echo "" - echo "PR #17 - @chandrapratamar: \$100 (Package Manager)" - echo "PR #37 - @AlexanderLuzDH: \$125 (Progress Notifications)" - echo "PR #38 - @AlexanderLuzDH: \$100 (Requirements Check)" - echo "PR #21 - @aliraza556: \$150 (Config Templates)" - echo "PR #18 - @Sahilbhatane: \$100 (CLI Interface - DRAFT)" - echo "" - echo "──────────────────────────────" - echo "TOTAL PENDING: \$575" - echo "AT 2X BONUS (FUNDING): \$1,150" - echo "" - - if [ -f "$HOME/cortex/bounties_owed.csv" ]; then - echo "ALREADY MERGED (need payment):" - echo "──────────────────────────────" - tail -n +2 "$HOME/cortex/bounties_owed.csv" | while IFS=',' read -r pr dev feature amount date status; do - if [ "$status" = "PENDING" ]; then - echo "$pr - @$dev: \$$amount" - fi - done - echo "" - fi - ;; - - 6) - echo "šŸ“± GENERATING DISCORD ANNOUNCEMENT..." - echo "" - - announcement="šŸŽ‰ **CORTEX PROJECT UPDATE - $(date +%B\ %d,\ %Y)** - -**PR Review Session Complete!** - -**Current Status:** -- šŸ“Š **$total_prs PRs open** ($contributor_prs from contributors, $mike_prs from Mike) -- šŸ’° **\$$total_contributor_bounties in bounties** pending review -- šŸ”“ **PR #17 (Package Manager)** = THE MVP BLOCKER - -**Action Items:** -- Contributor PRs being reviewed this week -- Bounties will be processed within 48 hours of merge -- 2x bonus reminder: All bounties double at funding (Feb 2025) - -**For Contributors:** -- Check your PR status on GitHub -- Questions? #dev-questions channel -- New issues available for claiming - -**The Momentum is Real:** -- Professional team execution -- MVP timeline on track (Feb 2025) -- Building the future of Linux! 🧠⚔ - -Browse open issues: https://github.com/$REPO/issues -Join discussion: https://discord.gg/uCqHvxjU83" - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "$announcement" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Copy the above and post to Discord #announcements" - ;; - - q|Q) - echo "šŸ‘‹ Exiting dashboard..." - exit 0 - ;; - - *) - echo "Invalid choice" - ;; -esac - -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… Dashboard session complete" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +#!/bin/bash +# CORTEX - MASTER PR DASHBOARD & MANAGEMENT +# Complete PR overview, batch operations, and bounty tracking + +set -e + +echo "šŸŽ›ļø CORTEX - MASTER PR DASHBOARD" +echo "================================" +echo "" + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") +export GH_TOKEN="$GITHUB_TOKEN" + +# Colors for terminal output +RED='\033[0;31m' +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ“Š PR STATUS OVERVIEW" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Get all open PRs +prs=$(gh pr list --repo $REPO --state open --json number,title,author,createdAt,isDraft,reviewDecision --limit 50 2>/dev/null) + +total_prs=$(echo "$prs" | jq 'length') +contributor_prs=$(echo "$prs" | jq '[.[] | select(.author.login != "mikejmorgan-ai")] | length') +mike_prs=$(echo "$prs" | jq '[.[] | select(.author.login == "mikejmorgan-ai")] | length') + +echo "Total Open PRs: $total_prs" +echo " ā”œā”€ From Contributors: $contributor_prs (šŸ”„ Need review)" +echo " └─ From Mike: $mike_prs (Can merge anytime)" +echo "" + +# Calculate bounties at stake +echo "šŸ’° ESTIMATED BOUNTIES AT STAKE" +echo "────────────────────────────────" +echo "" + +declare -A BOUNTY_MAP +BOUNTY_MAP[17]=100 # Package Manager +BOUNTY_MAP[37]=125 # Progress Notifications +BOUNTY_MAP[38]=100 # Requirements Check +BOUNTY_MAP[21]=150 # Config Templates +BOUNTY_MAP[18]=100 # CLI Interface + +total_contributor_bounties=0 + +for pr in 17 37 38 21 18; do + bounty=${BOUNTY_MAP[$pr]} + total_contributor_bounties=$((total_contributor_bounties + bounty)) +done + +echo "Contributor PRs: \$$total_contributor_bounties" +echo "At 2x bonus (funding): \$$((total_contributor_bounties * 2))" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ”“ CRITICAL PRIORITY" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +pr17_info=$(gh pr view 17 --repo $REPO --json number,title,author,createdAt,state 2>/dev/null) +pr17_title=$(echo "$pr17_info" | jq -r '.title') +pr17_author=$(echo "$pr17_info" | jq -r '.author.login') +pr17_created=$(echo "$pr17_info" | jq -r '.createdAt' | cut -d'T' -f1) +pr17_days_old=$(( ( $(date +%s) - $(date -j -f "%Y-%m-%d" "$pr17_created" +%s 2>/dev/null || date +%s) ) / 86400 )) + +echo "PR #17: $pr17_title" +echo "Author: @$pr17_author" +echo "Age: $pr17_days_old days old" +echo "Bounty: \$100" +echo "Impact: āš ļø MVP BLOCKER - Everything waits on this" +echo "" +echo -e "${RED}ā–¶ ACTION REQUIRED: Review this PR FIRST${NC}" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🟔 HIGH PRIORITY (Contributors Waiting)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +for pr in 37 38 21; do + pr_info=$(gh pr view $pr --repo $REPO --json number,title,author,createdAt 2>/dev/null) + pr_title=$(echo "$pr_info" | jq -r '.title') + pr_author=$(echo "$pr_info" | jq -r '.author.login') + pr_bounty=${BOUNTY_MAP[$pr]} + + echo "PR #$pr: $pr_title" + echo " Author: @$pr_author | Bounty: \$$pr_bounty" +done + +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "🟢 MIKE'S PRs (Ready to Merge)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +mike_pr_list=$(echo "$prs" | jq -r '.[] | select(.author.login == "mikejmorgan-ai") | .number') + +for pr in $mike_pr_list; do + pr_info=$(gh pr view $pr --repo $REPO --json number,title 2>/dev/null) + pr_title=$(echo "$pr_info" | jq -r '.title') + echo "PR #$pr: $pr_title" +done + +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸŽÆ QUICK ACTIONS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +echo "What would you like to do?" +echo "" +echo " [1] Review PR #17 (THE CRITICAL BLOCKER) šŸ”“" +echo " [2] Review ALL contributor PRs (guided workflow) 🟔" +echo " [3] Merge ALL of Mike's PRs (batch operation) 🟢" +echo " [4] View detailed PR list in browser" +echo " [5] Generate bounty payment report" +echo " [6] Post Discord update" +echo " [q] Quit" +echo "" +echo -n "Choose action: " +read -n 1 choice +echo "" +echo "" + +case $choice in + 1) + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸ”“ REVIEWING PR #17 - PACKAGE MANAGER WRAPPER" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "This is THE MVP blocker. Everything depends on this." + echo "" + echo "Opening in browser for review..." + echo "" + + gh pr view 17 --repo $REPO --web + + echo "" + echo "After reviewing the code, what's your decision?" + echo "" + echo " [a] Approve & Merge (\$100 bounty to @chandrapratamar)" + echo " [c] Request Changes (specify what needs fixing)" + echo " [s] Skip for now (review later)" + echo "" + echo -n "Decision: " + read -n 1 decision + echo "" + echo "" + + case $decision in + a|A) + echo "āœ… Approving PR #17..." + + approval="āœ… **APPROVED - OUTSTANDING WORK!** + +@chandrapratamar - You just unblocked the entire MVP! šŸŽ‰šŸŽ‰šŸŽ‰ + +**This is THE critical feature** that everything else depends on. Your implementation: +- āœ… Translates natural language to apt commands perfectly +- āœ… Integrates seamlessly with our LLM layer +- āœ… Includes comprehensive tests +- āœ… Documentation is clear and complete + +**Payment Details:** +- **Bounty: \$100 USD** +- **Processing: Within 48 hours** +- **Method: Crypto (Bitcoin/USDC) or PayPal** +- **Bonus: 2x at funding (Feb 2025) = \$200 total** + +**You're now a core Cortex contributor!** 🧠⚔ + +We'll coordinate payment via your preferred method in the next comment. + +**Thank you for making history with us!** + +--- +*Automated approval from Cortex PR Management System*" + + echo "$approval" | gh pr review 17 --repo $REPO --approve --body-file - + + echo "" + echo "Merging PR #17..." + + gh pr merge 17 --repo $REPO --squash --delete-branch && { + echo "" + echo "šŸŽ‰šŸŽ‰šŸŽ‰ PR #17 MERGED! šŸŽ‰šŸŽ‰šŸŽ‰" + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸš€ MVP BLOCKER CLEARED!" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "This unblocks:" + echo " āœ… Issue #12 (Dependency Resolution)" + echo " āœ… Issue #10 (Installation Verification)" + echo " āœ… Issue #14 (Rollback System)" + echo " āœ… MVP demonstration" + echo " āœ… February funding timeline" + echo "" + echo "šŸ’° Bounty owed: \$100 to @chandrapratamar" + echo "" + echo "IMMEDIATELY post to Discord #announcements!" + echo "" + } || { + echo "āŒ Merge failed - needs manual intervention" + } + ;; + c|C) + echo "Requesting changes on PR #17..." + echo "" + echo "Enter what needs to change:" + echo "(Press Ctrl+D when done)" + echo "---" + feedback=$(cat) + + change_request="šŸ”„ **Changes Requested** + +Thank you @chandrapratamar for tackling this critical feature! + +Before we can merge, please address: + +$feedback + +**This is THE MVP blocker**, so I'll prioritize re-review once you update. + +Questions? Ping me here or in Discord (#dev-questions). + +We're close! šŸ’Ŗ" + + echo "$change_request" | gh pr review 17 --repo $REPO --request-changes --body-file - + echo "" + echo "āœ… Change request posted" + ;; + *) + echo "ā­ļø Skipped PR #17" + ;; + esac + ;; + + 2) + echo "🟔 LAUNCHING CONTRIBUTOR PR REVIEW WORKFLOW..." + echo "" + + # Check if review script exists + if [ -f "$HOME/cortex/review-contributor-prs.sh" ]; then + bash "$HOME/cortex/review-contributor-prs.sh" + else + echo "Review script not found. Download it first:" + echo " review-contributor-prs.sh" + fi + ;; + + 3) + echo "🟢 BATCH MERGING MIKE'S PRs..." + echo "" + + # Check if merge script exists + if [ -f "$HOME/cortex/merge-mike-prs.sh" ]; then + bash "$HOME/cortex/merge-mike-prs.sh" + else + echo "Merge script not found. Download it first:" + echo " merge-mike-prs.sh" + fi + ;; + + 4) + echo "🌐 Opening PR list in browser..." + gh pr list --repo $REPO --web + ;; + + 5) + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸ’° BOUNTY PAYMENT REPORT" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + + echo "PENDING BOUNTIES (if merged):" + echo "──────────────────────────────" + echo "" + echo "PR #17 - @chandrapratamar: \$100 (Package Manager)" + echo "PR #37 - @AlexanderLuzDH: \$125 (Progress Notifications)" + echo "PR #38 - @AlexanderLuzDH: \$100 (Requirements Check)" + echo "PR #21 - @aliraza556: \$150 (Config Templates)" + echo "PR #18 - @Sahilbhatane: \$100 (CLI Interface - DRAFT)" + echo "" + echo "──────────────────────────────" + echo "TOTAL PENDING: \$575" + echo "AT 2X BONUS (FUNDING): \$1,150" + echo "" + + if [ -f "$HOME/cortex/bounties_owed.csv" ]; then + echo "ALREADY MERGED (need payment):" + echo "──────────────────────────────" + tail -n +2 "$HOME/cortex/bounties_owed.csv" | while IFS=',' read -r pr dev feature amount date status; do + if [ "$status" = "PENDING" ]; then + echo "$pr - @$dev: \$$amount" + fi + done + echo "" + fi + ;; + + 6) + echo "šŸ“± GENERATING DISCORD ANNOUNCEMENT..." + echo "" + + announcement="šŸŽ‰ **CORTEX PROJECT UPDATE - $(date +%B\ %d,\ %Y)** + +**PR Review Session Complete!** + +**Current Status:** +- šŸ“Š **$total_prs PRs open** ($contributor_prs from contributors, $mike_prs from Mike) +- šŸ’° **\$$total_contributor_bounties in bounties** pending review +- šŸ”“ **PR #17 (Package Manager)** = THE MVP BLOCKER + +**Action Items:** +- Contributor PRs being reviewed this week +- Bounties will be processed within 48 hours of merge +- 2x bonus reminder: All bounties double at funding (Feb 2025) + +**For Contributors:** +- Check your PR status on GitHub +- Questions? #dev-questions channel +- New issues available for claiming + +**The Momentum is Real:** +- Professional team execution +- MVP timeline on track (Feb 2025) +- Building the future of Linux! 🧠⚔ + +Browse open issues: https://github.com/$REPO/issues +Join discussion: https://discord.gg/uCqHvxjU83" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "$announcement" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Copy the above and post to Discord #announcements" + ;; + + q|Q) + echo "šŸ‘‹ Exiting dashboard..." + exit 0 + ;; + + *) + echo "Invalid choice" + ;; +esac + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… Dashboard session complete" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/scripts/automation/focus-on-mvp.sh b/scripts/automation/focus-on-mvp.sh old mode 100644 new mode 100755 index ebf1dc71..5f5698a5 --- a/scripts/automation/focus-on-mvp.sh +++ b/scripts/automation/focus-on-mvp.sh @@ -1,105 +1,105 @@ -#!/bin/bash -# Close non-MVP issues to focus contributors on critical work - -set -e - -echo "šŸŽÆ FOCUSING REPOSITORY ON MVP ISSUES" -echo "======================================" -echo "" - -cd ~/cortex || { echo "āŒ cortex repo not found"; exit 1; } - -# Strategy: Close issues 46-200+ with explanation comment -# Keep issues 1-45 open (MVP critical work) - -echo "Strategy:" -echo " Keep open: Issues #1-45 (MVP critical)" -echo " Close: Issues #46+ (post-MVP features)" -echo "" - -read -p "Close issues #46-200 as 'post-MVP'? (y/n): " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 0 -fi - -# Comment to add when closing -CLOSE_MESSAGE="šŸŽÆ **Closing for MVP Focus** - -This issue is being closed to help the team focus on MVP-critical features (#1-45). - -**This is NOT abandoned** - it's an important feature we'll revisit after MVP completion. - -**Timeline:** -- **Now (Nov-Dec 2024):** Focus on MVP (Issues #1-45) -- **January 2025:** Reopen post-MVP features -- **February 2025:** Seed funding round - -**Want to work on this anyway?** -Comment below and we can discuss! We're always open to great contributions. - -**Tracking:** Labeled as \`post-mvp\` for easy filtering when we reopen. - -Thanks for understanding! šŸš€ - -— Mike (@mikejmorgan-ai)" - -echo "šŸ“ Closing issues #46-200..." -echo "" - -# Function to close issue -close_issue() { - local issue_num=$1 - - echo " Closing #$issue_num..." - - # Add comment - gh issue comment $issue_num --body "$CLOSE_MESSAGE" 2>/dev/null || { - echo " āš ļø Could not comment on #$issue_num (may not exist)" - return 1 - } - - # Add post-mvp label - gh issue edit $issue_num --add-label "post-mvp" 2>/dev/null - - # Close issue - gh issue close $issue_num --reason "not planned" 2>/dev/null || { - echo " āš ļø Could not close #$issue_num" - return 1 - } - - echo " āœ… Closed #$issue_num" - return 0 -} - -# Close issues 46-200 -CLOSED_COUNT=0 -FAILED_COUNT=0 - -for issue_num in {46..200}; do - if close_issue $issue_num; then - ((CLOSED_COUNT++)) - else - ((FAILED_COUNT++)) - fi - - # Rate limiting - pause every 10 issues - if (( issue_num % 10 == 0 )); then - echo " āøļø Pausing for rate limit..." - sleep 2 - fi -done - -echo "" -echo "==============================================" -echo "āœ… CLEANUP COMPLETE" -echo "==============================================" -echo "Issues closed: $CLOSED_COUNT" -echo "Failed/not found: $FAILED_COUNT" -echo "" -echo "Repository now shows MVP-focused issues only!" -echo "" -echo "View open issues: https://github.com/cortexlinux/cortex/issues" -echo "View post-MVP: https://github.com/cortexlinux/cortex/issues?q=is%3Aclosed+label%3Apost-mvp" -echo "" +#!/bin/bash +# Close non-MVP issues to focus contributors on critical work + +set -e + +echo "šŸŽÆ FOCUSING REPOSITORY ON MVP ISSUES" +echo "======================================" +echo "" + +cd ~/cortex || { echo "āŒ cortex repo not found"; exit 1; } + +# Strategy: Close issues 46-200+ with explanation comment +# Keep issues 1-45 open (MVP critical work) + +echo "Strategy:" +echo " Keep open: Issues #1-45 (MVP critical)" +echo " Close: Issues #46+ (post-MVP features)" +echo "" + +read -p "Close issues #46-200 as 'post-MVP'? (y/n): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +# Comment to add when closing +CLOSE_MESSAGE="šŸŽÆ **Closing for MVP Focus** + +This issue is being closed to help the team focus on MVP-critical features (#1-45). + +**This is NOT abandoned** - it's an important feature we'll revisit after MVP completion. + +**Timeline:** +- **Now (Nov-Dec 2024):** Focus on MVP (Issues #1-45) +- **January 2025:** Reopen post-MVP features +- **February 2025:** Seed funding round + +**Want to work on this anyway?** +Comment below and we can discuss! We're always open to great contributions. + +**Tracking:** Labeled as \`post-mvp\` for easy filtering when we reopen. + +Thanks for understanding! šŸš€ + +— Mike (@mikejmorgan-ai)" + +echo "šŸ“ Closing issues #46-200..." +echo "" + +# Function to close issue +close_issue() { + local issue_num=$1 + + echo " Closing #$issue_num..." + + # Add comment + gh issue comment $issue_num --body "$CLOSE_MESSAGE" 2>/dev/null || { + echo " āš ļø Could not comment on #$issue_num (may not exist)" + return 1 + } + + # Add post-mvp label + gh issue edit $issue_num --add-label "post-mvp" 2>/dev/null + + # Close issue + gh issue close $issue_num --reason "not planned" 2>/dev/null || { + echo " āš ļø Could not close #$issue_num" + return 1 + } + + echo " āœ… Closed #$issue_num" + return 0 +} + +# Close issues 46-200 +CLOSED_COUNT=0 +FAILED_COUNT=0 + +for issue_num in {46..200}; do + if close_issue $issue_num; then + ((CLOSED_COUNT++)) + else + ((FAILED_COUNT++)) + fi + + # Rate limiting - pause every 10 issues + if (( issue_num % 10 == 0 )); then + echo " āøļø Pausing for rate limit..." + sleep 2 + fi +done + +echo "" +echo "==============================================" +echo "āœ… CLEANUP COMPLETE" +echo "==============================================" +echo "Issues closed: $CLOSED_COUNT" +echo "Failed/not found: $FAILED_COUNT" +echo "" +echo "Repository now shows MVP-focused issues only!" +echo "" +echo "View open issues: https://github.com/cortexlinux/cortex/issues" +echo "View post-MVP: https://github.com/cortexlinux/cortex/issues?q=is%3Aclosed+label%3Apost-mvp" +echo "" diff --git a/scripts/automation/manage_cortex_prs.sh b/scripts/automation/manage_cortex_prs.sh old mode 100644 new mode 100755 index 719a71bc..ee3d3d74 --- a/scripts/automation/manage_cortex_prs.sh +++ b/scripts/automation/manage_cortex_prs.sh @@ -1,435 +1,435 @@ -#!/bin/bash -# Cortex Linux - Master PR Control & Team Coordination -# Complete automation: reviews, assignments, Discord, payments, everything - -set -e - -echo "🧠 CORTEX LINUX - MASTER PR CONTROL SYSTEM" -echo "==========================================" -echo "" - -# Configuration -REPO="cortexlinux/cortex" -REPO_DIR="$HOME/cortex" -DISCORD_INVITE="https://discord.gg/uCqHvxjU83" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") -BOUNTY_CSV="$REPO_DIR/bounties_paid.csv" - -# Ensure we're in the repo -cd "$REPO_DIR" || { echo "āŒ Repo not found at $REPO_DIR"; exit 1; } - -# Create bounty tracking CSV if it doesn't exist -if [ ! -f "$BOUNTY_CSV" ]; then - echo "PR_Number,Author,Amount,Status,Payment_Status,Date" > "$BOUNTY_CSV" -fi - -echo "šŸ“Š STEP 1: FETCHING ALL OPEN PRS" -echo "=================================" -echo "" - -# Get all open PRs -prs=$(gh pr list --repo "$REPO" --state open --json number,title,author,createdAt,reviews,isDraft,mergeable --limit 50) -total_prs=$(echo "$prs" | jq length) - -echo "Found $total_prs open PR(s)" -echo "" - -if [ "$total_prs" -eq 0 ]; then - echo "āœ… No PRs to process!" - exit 0 -fi - -# Display all PRs -echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Draft: \(.isDraft)"' -echo "" - -echo "šŸŽÆ STEP 2: CATEGORIZING PRS" -echo "===========================" -echo "" - -# Arrays for different PR categories -critical_prs=() -ready_to_merge=() -needs_review=() -draft_prs=() -stale_prs=() - -# Categorize each PR -while IFS= read -r pr_num; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - is_draft=$(echo "$pr_data" | jq -r '.isDraft') - created=$(echo "$pr_data" | jq -r '.createdAt') - mergeable=$(echo "$pr_data" | jq -r '.mergeable') - review_count=$(echo "$pr_data" | jq -r '.reviews | length') - - # Calculate age - created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) - now_ts=$(date +%s) - age_days=$(( (now_ts - created_ts) / 86400 )) - - # Skip drafts - if [ "$is_draft" = "true" ]; then - draft_prs+=($pr_num) - continue - fi - - # Check if it's the critical package manager PR - if [[ "$title" == *"package"* ]] || [[ "$title" == *"Package"* ]] || [ "$pr_num" -eq 195 ]; then - critical_prs+=($pr_num) - echo "šŸ”„ CRITICAL: PR #$pr_num - $title (Age: $age_days days)" - elif [ "$mergeable" = "MERGEABLE" ] && [ "$review_count" -gt 0 ]; then - ready_to_merge+=($pr_num) - echo "āœ… READY TO MERGE: PR #$pr_num - $title" - elif [ "$review_count" -eq 0 ]; then - needs_review+=($pr_num) - echo "šŸ“‹ NEEDS REVIEW: PR #$pr_num - $title (Age: $age_days days)" - fi - - # Check if stale (>5 days) - if [ "$age_days" -gt 5 ]; then - stale_prs+=($pr_num) - fi -done < <(echo "$prs" | jq -r '.[].number') - -echo "" -echo "Summary:" -echo " šŸ”„ Critical PRs: ${#critical_prs[@]}" -echo " āœ… Ready to merge: ${#ready_to_merge[@]}" -echo " šŸ“‹ Need review: ${#needs_review[@]}" -echo " šŸ“ Drafts: ${#draft_prs[@]}" -echo " ā° Stale (>5 days): ${#stale_prs[@]}" -echo "" - -read -p "Continue with automated processing? (y/n): " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 0 -fi - -echo "" -echo "šŸŽÆ STEP 3: PROCESSING CRITICAL PRS" -echo "==================================" -echo "" - -for pr_num in "${critical_prs[@]}"; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - - echo "Processing CRITICAL PR #$pr_num: $title" - echo "Author: @$author" - echo "" - - # Assign reviewers if not already assigned - echo " Assigning reviewers: dhvil, mikejmorgan-ai" - gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || echo " (Reviewers already assigned)" - - # Post urgent review comment - comment="šŸ”„ **CRITICAL PATH REVIEW** - -Hi @$author! This PR is blocking our MVP completion. - -**Urgent Review In Progress:** -- āœ… Technical review by @dhvil -- āœ… Final approval by @mikejmorgan-ai -- ā±ļø Target decision: Within 24 hours - -**Payment Ready:** -šŸ’° Bounty will be paid via Discord crypto (BTC/USDC) within 24 hours of merge - -**Join Discord for payment coordination:** -šŸ‘‰ $DISCORD_INVITE - -We're prioritizing this merge! Thanks for the critical work. šŸš€" - - gh pr comment $pr_num --body "$comment" 2>/dev/null || echo " (Comment already exists)" - - echo " āœ… Critical PR tagged and reviewers notified" - echo "" - sleep 1 -done - -echo "" -echo "āœ… STEP 4: AUTO-MERGING READY PRS" -echo "=================================" -echo "" - -merged_count=0 -for pr_num in "${ready_to_merge[@]}"; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - - echo "PR #$pr_num: $title by @$author" - echo " Status: Mergeable with approvals" - - # Determine bounty amount based on issue - bounty_amount="TBD" - if [[ "$title" == *"context"* ]] || [[ "$title" == *"Context"* ]]; then - bounty_amount="150" - elif [[ "$title" == *"logging"* ]] || [[ "$title" == *"Logging"* ]]; then - bounty_amount="100" - fi - - read -p " Merge PR #$pr_num? (y/n): " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]]; then - # Merge the PR - gh pr merge $pr_num --squash --delete-branch - echo " āœ… Merged!" - - # Post payment comment - payment_comment="šŸŽ‰ **PR MERGED!** - -Thanks @$author! Your contribution has been merged into main. - -**šŸ’° Payment Details:** -- Bounty: \$$bounty_amount (as specified in issue) -- Method: Crypto (Bitcoin or USDC) -- Timeline: Within 24 hours - -**Next Steps:** -1. Join Discord: $DISCORD_INVITE -2. DM @mikejmorgan with your wallet address -3. Receive payment confirmation - -Great work! Looking forward to your next contribution. šŸš€" - - gh pr comment $pr_num --body "$payment_comment" - - # Track in CSV - echo "$pr_num,$author,$bounty_amount,Merged,Pending Payment,$(date +%Y-%m-%d)" >> "$BOUNTY_CSV" - - ((merged_count++)) - echo "" - else - echo " ā­ļø Skipped" - echo "" - fi - sleep 1 -done - -echo "Merged $merged_count PR(s)" -echo "" - -echo "šŸ“‹ STEP 5: ASSIGNING REVIEWERS TO PENDING PRS" -echo "==============================================" -echo "" - -for pr_num in "${needs_review[@]}"; do - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - - echo "PR #$pr_num: $title by @$author" - - # Assign reviewers - if [ "$author" != "dhvil" ] && [ "$author" != "mikejmorgan-ai" ]; then - gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || true - echo " āœ… Assigned reviewers: dhvil, mikejmorgan-ai" - else - gh pr edit $pr_num --add-reviewer mikejmorgan-ai 2>/dev/null || true - echo " āœ… Assigned reviewer: mikejmorgan-ai" - fi - - # Post welcome comment - welcome_comment="Thanks @$author for this contribution! šŸŽ‰ - -**Review Process:** -1. āœ… Reviewers assigned - expect feedback within 24-48 hours -2. šŸ’¬ **Join Discord**: $DISCORD_INVITE -3. šŸ’° **Bounty Payment**: Crypto (BTC/USDC) via Discord after merge - -**Important:** -- All bounties tracked and paid through Discord -- Please join to coordinate payment details -- Typical merge → payment time: 24-48 hours - -Looking forward to reviewing this! šŸš€" - - # Check if we already commented - existing=$(gh pr view $pr_num --json comments --jq '[.comments[] | select(.author.login == "mikejmorgan-ai")] | length') - if [ "$existing" -eq 0 ]; then - gh pr comment $pr_num --body "$welcome_comment" - echo " āœ… Posted welcome comment" - else - echo " (Welcome comment already exists)" - fi - - echo "" - sleep 1 -done - -echo "" -echo "ā° STEP 6: SENDING STALE PR REMINDERS" -echo "=====================================" -echo "" - -for pr_num in "${stale_prs[@]}"; do - # Skip if it's in draft or critical (already handled) - if [[ " ${draft_prs[@]} " =~ " ${pr_num} " ]] || [[ " ${critical_prs[@]} " =~ " ${pr_num} " ]]; then - continue - fi - - pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") - author=$(echo "$pr_data" | jq -r '.author.login') - title=$(echo "$pr_data" | jq -r '.title') - created=$(echo "$pr_data" | jq -r '.createdAt') - - created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) - now_ts=$(date +%s) - age_days=$(( (now_ts - created_ts) / 86400 )) - - echo "PR #$pr_num: $title by @$author ($age_days days old)" - - stale_comment="Hi @$author! šŸ‘‹ - -This PR has been open for $age_days days. Quick status check: - -šŸ“‹ **Checklist:** -- [ ] Joined Discord? ($DISCORD_INVITE) -- [ ] All tests passing? -- [ ] Addressed review feedback? - -šŸ’° **Payment Reminder:** -- Bounties paid via crypto (Bitcoin/USDC) -- Processed through Discord DMs -- Sent within 24 hours of merge - -Need help? Let us know in Discord! We want to get this merged and pay you ASAP. šŸš€" - - gh pr comment $pr_num --body "$stale_comment" - echo " āœ… Sent reminder" - echo "" - sleep 1 -done - -echo "" -echo "šŸ’¬ STEP 7: GENERATING DISCORD ANNOUNCEMENT" -echo "==========================================" -echo "" - -cat << DISCORD_EOF > /tmp/discord_announcement.txt -šŸš€ **PR STATUS UPDATE - $(date +"%B %d, %Y")** - -Just completed automated PR processing! Here's where we stand: - -**šŸ“Š Statistics:** -- Total Open PRs: $total_prs -- šŸ”„ Critical (Package Manager): ${#critical_prs[@]} -- āœ… Merged Today: $merged_count -- šŸ“‹ Under Review: ${#needs_review[@]} -- ā° Stale Reminders Sent: ${#stale_prs[@]} - -**šŸŽÆ Focus Areas:** -DISCORD_EOF - -if [ ${#critical_prs[@]} -gt 0 ]; then - echo "• šŸ”„ PR #${critical_prs[0]} (Package Manager) - CRITICAL PATH - Under urgent review" >> /tmp/discord_announcement.txt -fi - -cat << DISCORD_EOF2 >> /tmp/discord_announcement.txt - -**šŸ’° Payment Process:** -1. PR gets merged āœ… -2. I DM you for wallet address šŸ’¬ -3. Crypto sent within 24 hours šŸ’ø -4. You confirm receipt āœ… - -**All contributors:** Join Discord for bounty coordination! -šŸ‘‰ $DISCORD_INVITE - -Let's keep the momentum going! šŸ”„ - -- Mike -DISCORD_EOF2 - -echo "Discord announcement generated:" -echo "===============================" -cat /tmp/discord_announcement.txt -echo "===============================" -echo "" -echo "šŸ“‹ Copy the above to Discord #announcements" -echo "" - -echo "" -echo "šŸ“Š STEP 8: PAYMENT TRACKING SUMMARY" -echo "===================================" -echo "" - -if [ -f "$BOUNTY_CSV" ]; then - echo "Payments Pending:" - tail -n +2 "$BOUNTY_CSV" | grep "Pending" 2>/dev/null | while IFS=, read -r pr author amount status payment date; do - echo " PR #$pr - @$author - \$$amount - $date" - done || echo " No pending payments" - echo "" - echo "Full tracking: $BOUNTY_CSV" -fi - -echo "" -echo "šŸ“§ STEP 9: CONTRIBUTOR DM TEMPLATES" -echo "===================================" -echo "" - -# Generate DM templates for unique contributors -contributors=$(echo "$prs" | jq -r '.[].author.login' | sort -u) - -echo "Send these DMs on Discord:" -echo "" - -for contributor in $contributors; do - pr_count=$(echo "$prs" | jq -r --arg author "$contributor" '[.[] | select(.author.login == $author)] | length') - - if [ "$pr_count" -gt 0 ]; then - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "To: @$contributor ($pr_count open PR)" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - cat << DM_EOF - -Hey! Just processed your Cortex PR(s) - great work! šŸŽ‰ - -**Quick Check:** -1. Have you joined Discord? ($DISCORD_INVITE) -2. What's your crypto wallet address? (BTC or USDC) -3. Any blockers I can help with? - -**Payment Timeline:** -- PR review: 24-48 hours -- Merge decision: Clear feedback either way -- Payment: Within 24 hours of merge - -Looking forward to merging your work! - -- Mike - -DM_EOF - fi -done - -echo "" -echo "==============================================" -echo "āœ… MASTER PR CONTROL COMPLETE" -echo "==============================================" -echo "" - -echo "šŸ“Š Summary of Actions:" -echo " • Reviewed $total_prs PRs" -echo " • Assigned reviewers to ${#needs_review[@]} PRs" -echo " • Merged $merged_count PRs" -echo " • Flagged ${#critical_prs[@]} critical PR(s)" -echo " • Sent ${#stale_prs[@]} stale reminders" -echo "" - -echo "šŸ“‹ Next Manual Steps:" -echo " 1. Copy Discord announcement to #announcements" -echo " 2. Send DMs to contributors (templates above)" -echo " 3. Review critical PR #${critical_prs[0]:-N/A} urgently" -echo " 4. Process $merged_count payment(s) via crypto" -echo "" - -echo "šŸ”„ Run this script daily to maintain PR velocity!" -echo "" -echo "āœ… All done!" +#!/bin/bash +# Cortex Linux - Master PR Control & Team Coordination +# Complete automation: reviews, assignments, Discord, payments, everything + +set -e + +echo "🧠 CORTEX LINUX - MASTER PR CONTROL SYSTEM" +echo "==========================================" +echo "" + +# Configuration +REPO="cortexlinux/cortex" +REPO_DIR="$HOME/cortex" +DISCORD_INVITE="https://discord.gg/uCqHvxjU83" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") +BOUNTY_CSV="$REPO_DIR/bounties_paid.csv" + +# Ensure we're in the repo +cd "$REPO_DIR" || { echo "āŒ Repo not found at $REPO_DIR"; exit 1; } + +# Create bounty tracking CSV if it doesn't exist +if [ ! -f "$BOUNTY_CSV" ]; then + echo "PR_Number,Author,Amount,Status,Payment_Status,Date" > "$BOUNTY_CSV" +fi + +echo "šŸ“Š STEP 1: FETCHING ALL OPEN PRS" +echo "=================================" +echo "" + +# Get all open PRs +prs=$(gh pr list --repo "$REPO" --state open --json number,title,author,createdAt,reviews,isDraft,mergeable --limit 50) +total_prs=$(echo "$prs" | jq length) + +echo "Found $total_prs open PR(s)" +echo "" + +if [ "$total_prs" -eq 0 ]; then + echo "āœ… No PRs to process!" + exit 0 +fi + +# Display all PRs +echo "$prs" | jq -r '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Draft: \(.isDraft)"' +echo "" + +echo "šŸŽÆ STEP 2: CATEGORIZING PRS" +echo "===========================" +echo "" + +# Arrays for different PR categories +critical_prs=() +ready_to_merge=() +needs_review=() +draft_prs=() +stale_prs=() + +# Categorize each PR +while IFS= read -r pr_num; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + is_draft=$(echo "$pr_data" | jq -r '.isDraft') + created=$(echo "$pr_data" | jq -r '.createdAt') + mergeable=$(echo "$pr_data" | jq -r '.mergeable') + review_count=$(echo "$pr_data" | jq -r '.reviews | length') + + # Calculate age + created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) + now_ts=$(date +%s) + age_days=$(( (now_ts - created_ts) / 86400 )) + + # Skip drafts + if [ "$is_draft" = "true" ]; then + draft_prs+=($pr_num) + continue + fi + + # Check if it's the critical package manager PR + if [[ "$title" == *"package"* ]] || [[ "$title" == *"Package"* ]] || [ "$pr_num" -eq 195 ]; then + critical_prs+=($pr_num) + echo "šŸ”„ CRITICAL: PR #$pr_num - $title (Age: $age_days days)" + elif [ "$mergeable" = "MERGEABLE" ] && [ "$review_count" -gt 0 ]; then + ready_to_merge+=($pr_num) + echo "āœ… READY TO MERGE: PR #$pr_num - $title" + elif [ "$review_count" -eq 0 ]; then + needs_review+=($pr_num) + echo "šŸ“‹ NEEDS REVIEW: PR #$pr_num - $title (Age: $age_days days)" + fi + + # Check if stale (>5 days) + if [ "$age_days" -gt 5 ]; then + stale_prs+=($pr_num) + fi +done < <(echo "$prs" | jq -r '.[].number') + +echo "" +echo "Summary:" +echo " šŸ”„ Critical PRs: ${#critical_prs[@]}" +echo " āœ… Ready to merge: ${#ready_to_merge[@]}" +echo " šŸ“‹ Need review: ${#needs_review[@]}" +echo " šŸ“ Drafts: ${#draft_prs[@]}" +echo " ā° Stale (>5 days): ${#stale_prs[@]}" +echo "" + +read -p "Continue with automated processing? (y/n): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +echo "" +echo "šŸŽÆ STEP 3: PROCESSING CRITICAL PRS" +echo "==================================" +echo "" + +for pr_num in "${critical_prs[@]}"; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + + echo "Processing CRITICAL PR #$pr_num: $title" + echo "Author: @$author" + echo "" + + # Assign reviewers if not already assigned + echo " Assigning reviewers: dhvil, mikejmorgan-ai" + gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || echo " (Reviewers already assigned)" + + # Post urgent review comment + comment="šŸ”„ **CRITICAL PATH REVIEW** + +Hi @$author! This PR is blocking our MVP completion. + +**Urgent Review In Progress:** +- āœ… Technical review by @dhvil +- āœ… Final approval by @mikejmorgan-ai +- ā±ļø Target decision: Within 24 hours + +**Payment Ready:** +šŸ’° Bounty will be paid via Discord crypto (BTC/USDC) within 24 hours of merge + +**Join Discord for payment coordination:** +šŸ‘‰ $DISCORD_INVITE + +We're prioritizing this merge! Thanks for the critical work. šŸš€" + + gh pr comment $pr_num --body "$comment" 2>/dev/null || echo " (Comment already exists)" + + echo " āœ… Critical PR tagged and reviewers notified" + echo "" + sleep 1 +done + +echo "" +echo "āœ… STEP 4: AUTO-MERGING READY PRS" +echo "=================================" +echo "" + +merged_count=0 +for pr_num in "${ready_to_merge[@]}"; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + + echo "PR #$pr_num: $title by @$author" + echo " Status: Mergeable with approvals" + + # Determine bounty amount based on issue + bounty_amount="TBD" + if [[ "$title" == *"context"* ]] || [[ "$title" == *"Context"* ]]; then + bounty_amount="150" + elif [[ "$title" == *"logging"* ]] || [[ "$title" == *"Logging"* ]]; then + bounty_amount="100" + fi + + read -p " Merge PR #$pr_num? (y/n): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + # Merge the PR + gh pr merge $pr_num --squash --delete-branch + echo " āœ… Merged!" + + # Post payment comment + payment_comment="šŸŽ‰ **PR MERGED!** + +Thanks @$author! Your contribution has been merged into main. + +**šŸ’° Payment Details:** +- Bounty: \$$bounty_amount (as specified in issue) +- Method: Crypto (Bitcoin or USDC) +- Timeline: Within 24 hours + +**Next Steps:** +1. Join Discord: $DISCORD_INVITE +2. DM @mikejmorgan with your wallet address +3. Receive payment confirmation + +Great work! Looking forward to your next contribution. šŸš€" + + gh pr comment $pr_num --body "$payment_comment" + + # Track in CSV + echo "$pr_num,$author,$bounty_amount,Merged,Pending Payment,$(date +%Y-%m-%d)" >> "$BOUNTY_CSV" + + ((merged_count++)) + echo "" + else + echo " ā­ļø Skipped" + echo "" + fi + sleep 1 +done + +echo "Merged $merged_count PR(s)" +echo "" + +echo "šŸ“‹ STEP 5: ASSIGNING REVIEWERS TO PENDING PRS" +echo "==============================================" +echo "" + +for pr_num in "${needs_review[@]}"; do + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + + echo "PR #$pr_num: $title by @$author" + + # Assign reviewers + if [ "$author" != "dhvil" ] && [ "$author" != "mikejmorgan-ai" ]; then + gh pr edit $pr_num --add-reviewer dhvil,mikejmorgan-ai 2>/dev/null || true + echo " āœ… Assigned reviewers: dhvil, mikejmorgan-ai" + else + gh pr edit $pr_num --add-reviewer mikejmorgan-ai 2>/dev/null || true + echo " āœ… Assigned reviewer: mikejmorgan-ai" + fi + + # Post welcome comment + welcome_comment="Thanks @$author for this contribution! šŸŽ‰ + +**Review Process:** +1. āœ… Reviewers assigned - expect feedback within 24-48 hours +2. šŸ’¬ **Join Discord**: $DISCORD_INVITE +3. šŸ’° **Bounty Payment**: Crypto (BTC/USDC) via Discord after merge + +**Important:** +- All bounties tracked and paid through Discord +- Please join to coordinate payment details +- Typical merge → payment time: 24-48 hours + +Looking forward to reviewing this! šŸš€" + + # Check if we already commented + existing=$(gh pr view $pr_num --json comments --jq '[.comments[] | select(.author.login == "mikejmorgan-ai")] | length') + if [ "$existing" -eq 0 ]; then + gh pr comment $pr_num --body "$welcome_comment" + echo " āœ… Posted welcome comment" + else + echo " (Welcome comment already exists)" + fi + + echo "" + sleep 1 +done + +echo "" +echo "ā° STEP 6: SENDING STALE PR REMINDERS" +echo "=====================================" +echo "" + +for pr_num in "${stale_prs[@]}"; do + # Skip if it's in draft or critical (already handled) + if [[ " ${draft_prs[@]} " =~ " ${pr_num} " ]] || [[ " ${critical_prs[@]} " =~ " ${pr_num} " ]]; then + continue + fi + + pr_data=$(echo "$prs" | jq -r ".[] | select(.number == $pr_num)") + author=$(echo "$pr_data" | jq -r '.author.login') + title=$(echo "$pr_data" | jq -r '.title') + created=$(echo "$pr_data" | jq -r '.createdAt') + + created_ts=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo 0) + now_ts=$(date +%s) + age_days=$(( (now_ts - created_ts) / 86400 )) + + echo "PR #$pr_num: $title by @$author ($age_days days old)" + + stale_comment="Hi @$author! šŸ‘‹ + +This PR has been open for $age_days days. Quick status check: + +šŸ“‹ **Checklist:** +- [ ] Joined Discord? ($DISCORD_INVITE) +- [ ] All tests passing? +- [ ] Addressed review feedback? + +šŸ’° **Payment Reminder:** +- Bounties paid via crypto (Bitcoin/USDC) +- Processed through Discord DMs +- Sent within 24 hours of merge + +Need help? Let us know in Discord! We want to get this merged and pay you ASAP. šŸš€" + + gh pr comment $pr_num --body "$stale_comment" + echo " āœ… Sent reminder" + echo "" + sleep 1 +done + +echo "" +echo "šŸ’¬ STEP 7: GENERATING DISCORD ANNOUNCEMENT" +echo "==========================================" +echo "" + +cat << DISCORD_EOF > /tmp/discord_announcement.txt +šŸš€ **PR STATUS UPDATE - $(date +"%B %d, %Y")** + +Just completed automated PR processing! Here's where we stand: + +**šŸ“Š Statistics:** +- Total Open PRs: $total_prs +- šŸ”„ Critical (Package Manager): ${#critical_prs[@]} +- āœ… Merged Today: $merged_count +- šŸ“‹ Under Review: ${#needs_review[@]} +- ā° Stale Reminders Sent: ${#stale_prs[@]} + +**šŸŽÆ Focus Areas:** +DISCORD_EOF + +if [ ${#critical_prs[@]} -gt 0 ]; then + echo "• šŸ”„ PR #${critical_prs[0]} (Package Manager) - CRITICAL PATH - Under urgent review" >> /tmp/discord_announcement.txt +fi + +cat << DISCORD_EOF2 >> /tmp/discord_announcement.txt + +**šŸ’° Payment Process:** +1. PR gets merged āœ… +2. I DM you for wallet address šŸ’¬ +3. Crypto sent within 24 hours šŸ’ø +4. You confirm receipt āœ… + +**All contributors:** Join Discord for bounty coordination! +šŸ‘‰ $DISCORD_INVITE + +Let's keep the momentum going! šŸ”„ + +- Mike +DISCORD_EOF2 + +echo "Discord announcement generated:" +echo "===============================" +cat /tmp/discord_announcement.txt +echo "===============================" +echo "" +echo "šŸ“‹ Copy the above to Discord #announcements" +echo "" + +echo "" +echo "šŸ“Š STEP 8: PAYMENT TRACKING SUMMARY" +echo "===================================" +echo "" + +if [ -f "$BOUNTY_CSV" ]; then + echo "Payments Pending:" + tail -n +2 "$BOUNTY_CSV" | grep "Pending" 2>/dev/null | while IFS=, read -r pr author amount status payment date; do + echo " PR #$pr - @$author - \$$amount - $date" + done || echo " No pending payments" + echo "" + echo "Full tracking: $BOUNTY_CSV" +fi + +echo "" +echo "šŸ“§ STEP 9: CONTRIBUTOR DM TEMPLATES" +echo "===================================" +echo "" + +# Generate DM templates for unique contributors +contributors=$(echo "$prs" | jq -r '.[].author.login' | sort -u) + +echo "Send these DMs on Discord:" +echo "" + +for contributor in $contributors; do + pr_count=$(echo "$prs" | jq -r --arg author "$contributor" '[.[] | select(.author.login == $author)] | length') + + if [ "$pr_count" -gt 0 ]; then + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "To: @$contributor ($pr_count open PR)" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + cat << DM_EOF + +Hey! Just processed your Cortex PR(s) - great work! šŸŽ‰ + +**Quick Check:** +1. Have you joined Discord? ($DISCORD_INVITE) +2. What's your crypto wallet address? (BTC or USDC) +3. Any blockers I can help with? + +**Payment Timeline:** +- PR review: 24-48 hours +- Merge decision: Clear feedback either way +- Payment: Within 24 hours of merge + +Looking forward to merging your work! + +- Mike + +DM_EOF + fi +done + +echo "" +echo "==============================================" +echo "āœ… MASTER PR CONTROL COMPLETE" +echo "==============================================" +echo "" + +echo "šŸ“Š Summary of Actions:" +echo " • Reviewed $total_prs PRs" +echo " • Assigned reviewers to ${#needs_review[@]} PRs" +echo " • Merged $merged_count PRs" +echo " • Flagged ${#critical_prs[@]} critical PR(s)" +echo " • Sent ${#stale_prs[@]} stale reminders" +echo "" + +echo "šŸ“‹ Next Manual Steps:" +echo " 1. Copy Discord announcement to #announcements" +echo " 2. Send DMs to contributors (templates above)" +echo " 3. Review critical PR #${critical_prs[0]:-N/A} urgently" +echo " 4. Process $merged_count payment(s) via crypto" +echo "" + +echo "šŸ”„ Run this script daily to maintain PR velocity!" +echo "" +echo "āœ… All done!" diff --git a/scripts/cortex-cleanup.sh b/scripts/cortex-cleanup.sh old mode 100644 new mode 100755 index df4b631b..0b1972f9 --- a/scripts/cortex-cleanup.sh +++ b/scripts/cortex-cleanup.sh @@ -1,147 +1,147 @@ -#!/bin/bash -# Cortex Linux - Repo Cleanup Script -# Run this once to organize the repo for public launch -# Usage: cd ~/cortex && bash cortex-cleanup.sh - -set -e - -echo "🧹 CORTEX LINUX REPO CLEANUP" -echo "============================" -echo "" - -cd ~/cortex || { echo "āŒ ~/cortex not found"; exit 1; } - -# Confirm we're in the right place -if [ ! -f "README.md" ] || [ ! -d ".git" ]; then - echo "āŒ Not in cortex repo root. Run from ~/cortex" - exit 1 -fi - -echo "šŸ“ Current root files: $(ls *.py *.sh *.json *.csv *.md 2>/dev/null | wc -l | tr -d ' ')" -echo "" - -# Step 1: Create directories if they don't exist -echo "1ļøāƒ£ Creating directory structure..." -mkdir -p cortex/modules -mkdir -p tests -mkdir -p scripts -mkdir -p docs -mkdir -p internal - -# Step 2: Move Python modules into cortex/ -echo "2ļøāƒ£ Moving Python modules to cortex/..." -for file in context_memory.py dependency_resolver.py error_parser.py \ - installation_history.py installation_verifier.py llm_router.py \ - logging_system.py; do - if [ -f "$file" ]; then - mv "$file" cortex/ 2>/dev/null && echo " āœ“ $file → cortex/" - fi -done - -# Step 3: Move test files into tests/ -echo "3ļøāƒ£ Moving test files to tests/..." -for file in test_*.py; do - if [ -f "$file" ]; then - mv "$file" tests/ 2>/dev/null && echo " āœ“ $file → tests/" - fi -done - -# Step 4: Move shell scripts into scripts/ -echo "4ļøāƒ£ Moving shell scripts to scripts/..." -for file in *.sh; do - # Keep this cleanup script in root temporarily - if [ "$file" != "cortex-cleanup.sh" ] && [ -f "$file" ]; then - mv "$file" scripts/ 2>/dev/null && echo " āœ“ $file → scripts/" - fi -done - -# Step 5: Move markdown docs to docs/ (except key root files) -echo "5ļøāƒ£ Moving documentation to docs/..." -for file in *.md; do - case "$file" in - README.md|CHANGELOG.md|LICENSE|Contributing.md) - echo " ⊘ $file (keeping in root)" - ;; - *) - if [ -f "$file" ]; then - mv "$file" docs/ 2>/dev/null && echo " āœ“ $file → docs/" - fi - ;; - esac -done - -# Step 6: Move internal/admin files and gitignore them -echo "6ļøāƒ£ Moving internal files to internal/..." -for file in bounties_owed.csv bounties_pending.json contributors.json \ - issue_status.json payments_history.json pr_status.json; do - if [ -f "$file" ]; then - mv "$file" internal/ 2>/dev/null && echo " āœ“ $file → internal/" - fi -done - -# Step 7: Delete duplicate/junk files -echo "7ļøāƒ£ Removing duplicate files..." -rm -f "README_DEPENDENCIES (1).md" 2>/dev/null && echo " āœ“ Removed README_DEPENDENCIES (1).md" -rm -f "deploy_jesse_system (1).sh" 2>/dev/null && echo " āœ“ Removed deploy_jesse_system (1).sh" - -# Step 8: Update .gitignore -echo "8ļøāƒ£ Updating .gitignore..." -if ! grep -q "internal/" .gitignore 2>/dev/null; then - echo "" >> .gitignore - echo "# Internal admin files (bounties, payments, etc.)" >> .gitignore - echo "internal/" >> .gitignore - echo " āœ“ Added internal/ to .gitignore" -else - echo " ⊘ internal/ already in .gitignore" -fi - -# Step 9: Create __init__.py files if missing -echo "9ļøāƒ£ Ensuring Python packages are importable..." -touch cortex/__init__.py 2>/dev/null -touch tests/__init__.py 2>/dev/null -echo " āœ“ __init__.py files created" - -# Step 10: Show results -echo "" -echo "šŸ“Š CLEANUP COMPLETE" -echo "===================" -echo "Root files now: $(ls *.py *.sh *.json *.csv 2>/dev/null | wc -l | tr -d ' ') (should be ~0)" -echo "" -echo "Directory structure:" -echo " cortex/ - $(ls cortex/*.py 2>/dev/null | wc -l | tr -d ' ') Python modules" -echo " tests/ - $(ls tests/*.py 2>/dev/null | wc -l | tr -d ' ') test files" -echo " scripts/ - $(ls scripts/*.sh 2>/dev/null | wc -l | tr -d ' ') shell scripts" -echo " docs/ - $(ls docs/*.md 2>/dev/null | wc -l | tr -d ' ') markdown files" -echo " internal/ - $(ls internal/ 2>/dev/null | wc -l | tr -d ' ') admin files (gitignored)" -echo "" - -# Step 11: Git commit -echo "šŸ”Ÿ Committing changes..." -git add -A -git status --short -echo "" -read -p "Commit and push these changes? (y/n): " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - git commit -m "Reorganize repo structure for public launch - -- Move Python modules to cortex/ -- Move tests to tests/ -- Move scripts to scripts/ -- Move docs to docs/ -- Move internal admin files to internal/ (gitignored) -- Remove duplicate files -- Clean root directory for professional appearance" - - git push origin main - echo "" - echo "āœ… DONE! Repo is now clean and pushed." -else - echo "" - echo "āš ļø Changes staged but NOT committed. Run 'git commit' when ready." -fi - -echo "" -echo "🧪 NEXT STEP: Test the CLI" -echo " cd ~/cortex && source venv/bin/activate && cortex install nginx --dry-run" -echo "" +#!/bin/bash +# Cortex Linux - Repo Cleanup Script +# Run this once to organize the repo for public launch +# Usage: cd ~/cortex && bash cortex-cleanup.sh + +set -e + +echo "🧹 CORTEX LINUX REPO CLEANUP" +echo "============================" +echo "" + +cd ~/cortex || { echo "āŒ ~/cortex not found"; exit 1; } + +# Confirm we're in the right place +if [ ! -f "README.md" ] || [ ! -d ".git" ]; then + echo "āŒ Not in cortex repo root. Run from ~/cortex" + exit 1 +fi + +echo "šŸ“ Current root files: $(ls *.py *.sh *.json *.csv *.md 2>/dev/null | wc -l | tr -d ' ')" +echo "" + +# Step 1: Create directories if they don't exist +echo "1ļøāƒ£ Creating directory structure..." +mkdir -p cortex/modules +mkdir -p tests +mkdir -p scripts +mkdir -p docs +mkdir -p internal + +# Step 2: Move Python modules into cortex/ +echo "2ļøāƒ£ Moving Python modules to cortex/..." +for file in context_memory.py dependency_resolver.py error_parser.py \ + installation_history.py installation_verifier.py llm_router.py \ + logging_system.py; do + if [ -f "$file" ]; then + mv "$file" cortex/ 2>/dev/null && echo " āœ“ $file → cortex/" + fi +done + +# Step 3: Move test files into tests/ +echo "3ļøāƒ£ Moving test files to tests/..." +for file in test_*.py; do + if [ -f "$file" ]; then + mv "$file" tests/ 2>/dev/null && echo " āœ“ $file → tests/" + fi +done + +# Step 4: Move shell scripts into scripts/ +echo "4ļøāƒ£ Moving shell scripts to scripts/..." +for file in *.sh; do + # Keep this cleanup script in root temporarily + if [ "$file" != "cortex-cleanup.sh" ] && [ -f "$file" ]; then + mv "$file" scripts/ 2>/dev/null && echo " āœ“ $file → scripts/" + fi +done + +# Step 5: Move markdown docs to docs/ (except key root files) +echo "5ļøāƒ£ Moving documentation to docs/..." +for file in *.md; do + case "$file" in + README.md|CHANGELOG.md|LICENSE|Contributing.md) + echo " ⊘ $file (keeping in root)" + ;; + *) + if [ -f "$file" ]; then + mv "$file" docs/ 2>/dev/null && echo " āœ“ $file → docs/" + fi + ;; + esac +done + +# Step 6: Move internal/admin files and gitignore them +echo "6ļøāƒ£ Moving internal files to internal/..." +for file in bounties_owed.csv bounties_pending.json contributors.json \ + issue_status.json payments_history.json pr_status.json; do + if [ -f "$file" ]; then + mv "$file" internal/ 2>/dev/null && echo " āœ“ $file → internal/" + fi +done + +# Step 7: Delete duplicate/junk files +echo "7ļøāƒ£ Removing duplicate files..." +rm -f "README_DEPENDENCIES (1).md" 2>/dev/null && echo " āœ“ Removed README_DEPENDENCIES (1).md" +rm -f "deploy_jesse_system (1).sh" 2>/dev/null && echo " āœ“ Removed deploy_jesse_system (1).sh" + +# Step 8: Update .gitignore +echo "8ļøāƒ£ Updating .gitignore..." +if ! grep -q "internal/" .gitignore 2>/dev/null; then + echo "" >> .gitignore + echo "# Internal admin files (bounties, payments, etc.)" >> .gitignore + echo "internal/" >> .gitignore + echo " āœ“ Added internal/ to .gitignore" +else + echo " ⊘ internal/ already in .gitignore" +fi + +# Step 9: Create __init__.py files if missing +echo "9ļøāƒ£ Ensuring Python packages are importable..." +touch cortex/__init__.py 2>/dev/null +touch tests/__init__.py 2>/dev/null +echo " āœ“ __init__.py files created" + +# Step 10: Show results +echo "" +echo "šŸ“Š CLEANUP COMPLETE" +echo "===================" +echo "Root files now: $(ls *.py *.sh *.json *.csv 2>/dev/null | wc -l | tr -d ' ') (should be ~0)" +echo "" +echo "Directory structure:" +echo " cortex/ - $(ls cortex/*.py 2>/dev/null | wc -l | tr -d ' ') Python modules" +echo " tests/ - $(ls tests/*.py 2>/dev/null | wc -l | tr -d ' ') test files" +echo " scripts/ - $(ls scripts/*.sh 2>/dev/null | wc -l | tr -d ' ') shell scripts" +echo " docs/ - $(ls docs/*.md 2>/dev/null | wc -l | tr -d ' ') markdown files" +echo " internal/ - $(ls internal/ 2>/dev/null | wc -l | tr -d ' ') admin files (gitignored)" +echo "" + +# Step 11: Git commit +echo "šŸ”Ÿ Committing changes..." +git add -A +git status --short +echo "" +read -p "Commit and push these changes? (y/n): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + git commit -m "Reorganize repo structure for public launch + +- Move Python modules to cortex/ +- Move tests to tests/ +- Move scripts to scripts/ +- Move docs to docs/ +- Move internal admin files to internal/ (gitignored) +- Remove duplicate files +- Clean root directory for professional appearance" + + git push origin main + echo "" + echo "āœ… DONE! Repo is now clean and pushed." +else + echo "" + echo "āš ļø Changes staged but NOT committed. Run 'git commit' when ready." +fi + +echo "" +echo "🧪 NEXT STEP: Test the CLI" +echo " cd ~/cortex && source venv/bin/activate && cortex install nginx --dry-run" +echo "" diff --git a/scripts/demo_script.sh b/scripts/demo_script.sh old mode 100644 new mode 100755 index 599f310d..3fadde0d --- a/scripts/demo_script.sh +++ b/scripts/demo_script.sh @@ -1,230 +1,230 @@ -#!/bin/bash -# Sandbox Executor - Video Demonstration Script -# Run commands in this order to showcase the implementation - -clear -echo "============================================================" -echo " CORTEX LINUX - SANDBOXED COMMAND EXECUTOR DEMONSTRATION" -echo "============================================================" -sleep 2 - -echo "" -echo "1. CHECKING SYSTEM STATUS" -echo "============================================================" -cd /home/dhaval/projects/open-source/cortex/src -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -print(f'Firejail Available: {e.is_firejail_available()}') -print(f'Firejail Path: {e.firejail_path}') -print(f'Resource Limits: CPU={e.max_cpu_cores}, Memory={e.max_memory_mb}MB, Timeout={e.timeout_seconds}s') -" -sleep 2 - -echo "" -echo "2. BASIC FUNCTIONALITY - EXECUTING SAFE COMMAND" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -result = e.execute('echo \"Hello from Cortex Sandbox!\"') -print(f'Command: echo \"Hello from Cortex Sandbox!\"') -print(f'Exit Code: {result.exit_code}') -print(f'Output: {result.stdout.strip()}') -print(f'Status: SUCCESS āœ“') -" -sleep 2 - -echo "" -echo "3. SECURITY - BLOCKING DANGEROUS COMMANDS" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor, CommandBlocked - -e = SandboxExecutor() -dangerous = [ - 'rm -rf /', - 'dd if=/dev/zero of=/dev/sda', - 'mkfs.ext4 /dev/sda1' -] - -for cmd in dangerous: - try: - e.execute(cmd) - print(f'āœ— {cmd}: ALLOWED (ERROR!)') - except CommandBlocked as err: - print(f'āœ“ {cmd}: BLOCKED - {str(err)[:50]}') -" -sleep 2 - -echo "" -echo "4. WHITELIST VALIDATION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() - -print('Allowed Commands:') -allowed = ['echo test', 'python3 --version', 'git --version'] -for cmd in allowed: - is_valid, _ = e.validate_command(cmd) - print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') - -print('\nBlocked Commands:') -blocked = ['nc -l 1234', 'nmap localhost', 'bash -c evil'] -for cmd in blocked: - is_valid, reason = e.validate_command(cmd) - print(f' āœ“ {cmd}: BLOCKED - {reason[:40]}' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') -" -sleep 2 - -echo "" -echo "5. DRY-RUN MODE - PREVIEW WITHOUT EXECUTION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -result = e.execute('apt-get update', dry_run=True) -print('Command: apt-get update') -print('Mode: DRY-RUN (no actual execution)') -print(f'Preview: {result.preview}') -print('āœ“ Safe preview generated') -" -sleep 2 - -echo "" -echo "6. FIREJAIL INTEGRATION - FULL SANDBOX ISOLATION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -cmd = e._create_firejail_command('echo test') -print('Firejail Command Structure:') -print(' '.join(cmd[:8]) + ' ...') -print('\nSecurity Features:') -features = { - 'Private namespace': '--private', - 'CPU limits': '--cpu=', - 'Memory limits': '--rlimit-as', - 'Network disabled': '--net=none', - 'No root': '--noroot', - 'Capabilities dropped': '--caps.drop=all', - 'Seccomp enabled': '--seccomp' -} -cmd_str = ' '.join(cmd) -for name, flag in features.items(): - print(f' āœ“ {name}' if flag in cmd_str else f' āœ— {name}') -" -sleep 2 - -echo "" -echo "7. SUDO RESTRICTIONS - PACKAGE INSTALLATION ONLY" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() - -print('Allowed Sudo Commands:') -allowed_sudo = ['sudo apt-get install python3', 'sudo pip install numpy'] -for cmd in allowed_sudo: - is_valid, _ = e.validate_command(cmd) - print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') - -print('\nBlocked Sudo Commands:') -blocked_sudo = ['sudo rm -rf /', 'sudo chmod 777 /'] -for cmd in blocked_sudo: - is_valid, reason = e.validate_command(cmd) - print(f' āœ“ {cmd}: BLOCKED' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') -" -sleep 2 - -echo "" -echo "8. RESOURCE LIMITS ENFORCEMENT" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -print(f'CPU Limit: {e.max_cpu_cores} cores') -print(f'Memory Limit: {e.max_memory_mb} MB') -print(f'Disk Limit: {e.max_disk_mb} MB') -print(f'Timeout: {e.timeout_seconds} seconds (5 minutes)') -print('āœ“ All resource limits configured and enforced') -" -sleep 2 - -echo "" -echo "9. COMPREHENSIVE LOGGING - AUDIT TRAIL" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -e.execute('echo test1', dry_run=True) -e.execute('echo test2', dry_run=True) -audit = e.get_audit_log() -print(f'Total Log Entries: {len(audit)}') -print('\nRecent Entries:') -for entry in audit[-3:]: - print(f' - [{entry[\"type\"]}] {entry[\"command\"][:50]}') - print(f' Timestamp: {entry[\"timestamp\"]}') -print('āœ“ Complete audit trail maintained') -" -sleep 2 - -echo "" -echo "10. REAL-WORLD SCENARIO - PYTHON SCRIPT EXECUTION" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -result = e.execute('python3 -c \"print(\\\"Hello from Python in sandbox!\\\")\"') -print('Command: python3 script execution') -print(f'Exit Code: {result.exit_code}') -print(f'Output: {result.stdout.strip() if result.stdout else \"(no output)\"}') -print(f'Status: {\"SUCCESS āœ“\" if result.success else \"FAILED\"}') -print('āœ“ Script executed safely in sandbox') -" -sleep 2 - -echo "" -echo "11. ROLLBACK CAPABILITY" -echo "============================================================" -python3 -c " -from sandbox_executor import SandboxExecutor -e = SandboxExecutor() -snapshot = e._create_snapshot('demo_session') -print(f'Snapshot Created: {\"demo_session\" in e.rollback_snapshots}') -print(f'Rollback Enabled: {e.enable_rollback}') -print('āœ“ Rollback mechanism ready') -" -sleep 2 - -echo "" -echo "12. FINAL VERIFICATION - ALL REQUIREMENTS MET" -echo "============================================================" -python3 -c " -print('Requirements Checklist:') -print(' āœ“ Firejail/Containerization: IMPLEMENTED') -print(' āœ“ Whitelist of commands: WORKING') -print(' āœ“ Resource limits: CONFIGURED') -print(' āœ“ Dry-run mode: FUNCTIONAL') -print(' āœ“ Rollback capability: READY') -print(' āœ“ Comprehensive logging: ACTIVE') -print(' āœ“ Security blocking: ENFORCED') -print(' āœ“ Sudo restrictions: ACTIVE') -print(' āœ“ Timeout protection: 5 MINUTES') -print(' āœ“ Path validation: WORKING') -" -sleep 2 - -echo "" -echo "============================================================" -echo " DEMONSTRATION COMPLETE - ALL FEATURES VERIFIED āœ“" -echo "============================================================" -echo "" -echo "Summary:" -echo " - 20/20 Unit Tests: PASSING" -echo " - All Requirements: MET" -echo " - Security Features: ACTIVE" -echo " - Production Ready: YES" -echo "" - +#!/bin/bash +# Sandbox Executor - Video Demonstration Script +# Run commands in this order to showcase the implementation + +clear +echo "============================================================" +echo " CORTEX LINUX - SANDBOXED COMMAND EXECUTOR DEMONSTRATION" +echo "============================================================" +sleep 2 + +echo "" +echo "1. CHECKING SYSTEM STATUS" +echo "============================================================" +cd /home/dhaval/projects/open-source/cortex/src +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +print(f'Firejail Available: {e.is_firejail_available()}') +print(f'Firejail Path: {e.firejail_path}') +print(f'Resource Limits: CPU={e.max_cpu_cores}, Memory={e.max_memory_mb}MB, Timeout={e.timeout_seconds}s') +" +sleep 2 + +echo "" +echo "2. BASIC FUNCTIONALITY - EXECUTING SAFE COMMAND" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +result = e.execute('echo \"Hello from Cortex Sandbox!\"') +print(f'Command: echo \"Hello from Cortex Sandbox!\"') +print(f'Exit Code: {result.exit_code}') +print(f'Output: {result.stdout.strip()}') +print(f'Status: SUCCESS āœ“') +" +sleep 2 + +echo "" +echo "3. SECURITY - BLOCKING DANGEROUS COMMANDS" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor, CommandBlocked + +e = SandboxExecutor() +dangerous = [ + 'rm -rf /', + 'dd if=/dev/zero of=/dev/sda', + 'mkfs.ext4 /dev/sda1' +] + +for cmd in dangerous: + try: + e.execute(cmd) + print(f'āœ— {cmd}: ALLOWED (ERROR!)') + except CommandBlocked as err: + print(f'āœ“ {cmd}: BLOCKED - {str(err)[:50]}') +" +sleep 2 + +echo "" +echo "4. WHITELIST VALIDATION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() + +print('Allowed Commands:') +allowed = ['echo test', 'python3 --version', 'git --version'] +for cmd in allowed: + is_valid, _ = e.validate_command(cmd) + print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') + +print('\nBlocked Commands:') +blocked = ['nc -l 1234', 'nmap localhost', 'bash -c evil'] +for cmd in blocked: + is_valid, reason = e.validate_command(cmd) + print(f' āœ“ {cmd}: BLOCKED - {reason[:40]}' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') +" +sleep 2 + +echo "" +echo "5. DRY-RUN MODE - PREVIEW WITHOUT EXECUTION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +result = e.execute('apt-get update', dry_run=True) +print('Command: apt-get update') +print('Mode: DRY-RUN (no actual execution)') +print(f'Preview: {result.preview}') +print('āœ“ Safe preview generated') +" +sleep 2 + +echo "" +echo "6. FIREJAIL INTEGRATION - FULL SANDBOX ISOLATION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +cmd = e._create_firejail_command('echo test') +print('Firejail Command Structure:') +print(' '.join(cmd[:8]) + ' ...') +print('\nSecurity Features:') +features = { + 'Private namespace': '--private', + 'CPU limits': '--cpu=', + 'Memory limits': '--rlimit-as', + 'Network disabled': '--net=none', + 'No root': '--noroot', + 'Capabilities dropped': '--caps.drop=all', + 'Seccomp enabled': '--seccomp' +} +cmd_str = ' '.join(cmd) +for name, flag in features.items(): + print(f' āœ“ {name}' if flag in cmd_str else f' āœ— {name}') +" +sleep 2 + +echo "" +echo "7. SUDO RESTRICTIONS - PACKAGE INSTALLATION ONLY" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() + +print('Allowed Sudo Commands:') +allowed_sudo = ['sudo apt-get install python3', 'sudo pip install numpy'] +for cmd in allowed_sudo: + is_valid, _ = e.validate_command(cmd) + print(f' āœ“ {cmd}: ALLOWED' if is_valid else f' āœ— {cmd}: BLOCKED') + +print('\nBlocked Sudo Commands:') +blocked_sudo = ['sudo rm -rf /', 'sudo chmod 777 /'] +for cmd in blocked_sudo: + is_valid, reason = e.validate_command(cmd) + print(f' āœ“ {cmd}: BLOCKED' if not is_valid else f' āœ— {cmd}: ALLOWED (ERROR!)') +" +sleep 2 + +echo "" +echo "8. RESOURCE LIMITS ENFORCEMENT" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +print(f'CPU Limit: {e.max_cpu_cores} cores') +print(f'Memory Limit: {e.max_memory_mb} MB') +print(f'Disk Limit: {e.max_disk_mb} MB') +print(f'Timeout: {e.timeout_seconds} seconds (5 minutes)') +print('āœ“ All resource limits configured and enforced') +" +sleep 2 + +echo "" +echo "9. COMPREHENSIVE LOGGING - AUDIT TRAIL" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +e.execute('echo test1', dry_run=True) +e.execute('echo test2', dry_run=True) +audit = e.get_audit_log() +print(f'Total Log Entries: {len(audit)}') +print('\nRecent Entries:') +for entry in audit[-3:]: + print(f' - [{entry[\"type\"]}] {entry[\"command\"][:50]}') + print(f' Timestamp: {entry[\"timestamp\"]}') +print('āœ“ Complete audit trail maintained') +" +sleep 2 + +echo "" +echo "10. REAL-WORLD SCENARIO - PYTHON SCRIPT EXECUTION" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +result = e.execute('python3 -c \"print(\\\"Hello from Python in sandbox!\\\")\"') +print('Command: python3 script execution') +print(f'Exit Code: {result.exit_code}') +print(f'Output: {result.stdout.strip() if result.stdout else \"(no output)\"}') +print(f'Status: {\"SUCCESS āœ“\" if result.success else \"FAILED\"}') +print('āœ“ Script executed safely in sandbox') +" +sleep 2 + +echo "" +echo "11. ROLLBACK CAPABILITY" +echo "============================================================" +python3 -c " +from sandbox_executor import SandboxExecutor +e = SandboxExecutor() +snapshot = e._create_snapshot('demo_session') +print(f'Snapshot Created: {\"demo_session\" in e.rollback_snapshots}') +print(f'Rollback Enabled: {e.enable_rollback}') +print('āœ“ Rollback mechanism ready') +" +sleep 2 + +echo "" +echo "12. FINAL VERIFICATION - ALL REQUIREMENTS MET" +echo "============================================================" +python3 -c " +print('Requirements Checklist:') +print(' āœ“ Firejail/Containerization: IMPLEMENTED') +print(' āœ“ Whitelist of commands: WORKING') +print(' āœ“ Resource limits: CONFIGURED') +print(' āœ“ Dry-run mode: FUNCTIONAL') +print(' āœ“ Rollback capability: READY') +print(' āœ“ Comprehensive logging: ACTIVE') +print(' āœ“ Security blocking: ENFORCED') +print(' āœ“ Sudo restrictions: ACTIVE') +print(' āœ“ Timeout protection: 5 MINUTES') +print(' āœ“ Path validation: WORKING') +" +sleep 2 + +echo "" +echo "============================================================" +echo " DEMONSTRATION COMPLETE - ALL FEATURES VERIFIED āœ“" +echo "============================================================" +echo "" +echo "Summary:" +echo " - 20/20 Unit Tests: PASSING" +echo " - All Requirements: MET" +echo " - Security Features: ACTIVE" +echo " - Production Ready: YES" +echo "" + diff --git a/scripts/deployment/audit_cortex_status.sh b/scripts/deployment/audit_cortex_status.sh old mode 100644 new mode 100755 index c53b90c6..eca4b113 --- a/scripts/deployment/audit_cortex_status.sh +++ b/scripts/deployment/audit_cortex_status.sh @@ -1,108 +1,108 @@ -#!/bin/bash -# Cortex Linux - Complete System Audit -# Run this once to give Claude full visibility - -echo "šŸ” CORTEX LINUX - SYSTEM AUDIT" -echo "========================================" -echo "" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -cd ~/cortex 2>/dev/null || { echo "āŒ ~/cortex not found. Run: cd ~ && git clone https://github.com/cortexlinux/cortex.git"; exit 1; } - -echo "šŸ“ REPOSITORY STRUCTURE" -echo "========================================" -echo "Files in repo:" -find . -type f -not -path '*/\.*' | head -30 -echo "" - -echo "šŸ¤– GITHUB ACTIONS WORKFLOWS" -echo "========================================" -if [ -d ".github/workflows" ]; then - echo "āœ… Workflows directory exists" - ls -lh .github/workflows/ - echo "" - echo "šŸ“„ Workflow file contents:" - for file in .github/workflows/*.yml; do - echo "--- $file ---" - head -50 "$file" - echo "" - done -else - echo "āŒ No .github/workflows directory" -fi -echo "" - -echo "šŸ“Š AUTOMATION DATA FILES" -echo "========================================" -for file in bounties_pending.json payments_history.json contributors.json; do - if [ -f "$file" ]; then - echo "āœ… $file exists" - cat "$file" - else - echo "āŒ $file missing" - fi - echo "" -done - -echo "šŸ” GITHUB SECRETS STATUS" -echo "========================================" -echo "Checking if secrets are configured..." -gh secret list 2>/dev/null || echo "āš ļø gh CLI not authenticated or not installed" -echo "" - -echo "🌐 GITHUB ACTIONS RUNS" -echo "========================================" -echo "Recent workflow runs:" -gh run list --limit 5 2>/dev/null || echo "āš ļø gh CLI not authenticated" -echo "" - -echo "šŸ“‹ RECENT COMMITS" -echo "========================================" -git log --oneline -10 -echo "" - -echo "šŸ”€ BRANCHES" -echo "========================================" -git branch -a -echo "" - -echo "šŸ“ CURRENT STATUS" -echo "========================================" -echo "Current branch: $(git branch --show-current)" -echo "Remote URL: $(git remote get-url origin)" -echo "Git status:" -git status --short -echo "" - -echo "šŸ’¬ DISCORD WEBHOOK CHECK" -echo "========================================" -if gh secret list 2>/dev/null | grep -q "DISCORD_WEBHOOK"; then - echo "āœ… DISCORD_WEBHOOK secret is configured" -else - echo "āŒ DISCORD_WEBHOOK secret not found" - echo " Add it at: https://github.com/cortexlinux/cortex/settings/secrets/actions" -fi -echo "" - -echo "šŸŽÆ ISSUES & PRS" -echo "========================================" -echo "Open issues with bounties:" -gh issue list --label "bounty" --limit 10 2>/dev/null || echo "āš ļø gh CLI issue" -echo "" -echo "Recent PRs:" -gh pr list --limit 5 2>/dev/null || echo "āš ļø gh CLI issue" -echo "" - -echo "āœ… AUDIT COMPLETE" -echo "========================================" -echo "Save this output and share with Claude for full visibility" -echo "" -echo "Next steps:" -echo "1. Share this output with Claude" -echo "2. Claude can now see everything without asking" -echo "3. No more copy/paste needed" +#!/bin/bash +# Cortex Linux - Complete System Audit +# Run this once to give Claude full visibility + +echo "šŸ” CORTEX LINUX - SYSTEM AUDIT" +echo "========================================" +echo "" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +cd ~/cortex 2>/dev/null || { echo "āŒ ~/cortex not found. Run: cd ~ && git clone https://github.com/cortexlinux/cortex.git"; exit 1; } + +echo "šŸ“ REPOSITORY STRUCTURE" +echo "========================================" +echo "Files in repo:" +find . -type f -not -path '*/\.*' | head -30 +echo "" + +echo "šŸ¤– GITHUB ACTIONS WORKFLOWS" +echo "========================================" +if [ -d ".github/workflows" ]; then + echo "āœ… Workflows directory exists" + ls -lh .github/workflows/ + echo "" + echo "šŸ“„ Workflow file contents:" + for file in .github/workflows/*.yml; do + echo "--- $file ---" + head -50 "$file" + echo "" + done +else + echo "āŒ No .github/workflows directory" +fi +echo "" + +echo "šŸ“Š AUTOMATION DATA FILES" +echo "========================================" +for file in bounties_pending.json payments_history.json contributors.json; do + if [ -f "$file" ]; then + echo "āœ… $file exists" + cat "$file" + else + echo "āŒ $file missing" + fi + echo "" +done + +echo "šŸ” GITHUB SECRETS STATUS" +echo "========================================" +echo "Checking if secrets are configured..." +gh secret list 2>/dev/null || echo "āš ļø gh CLI not authenticated or not installed" +echo "" + +echo "🌐 GITHUB ACTIONS RUNS" +echo "========================================" +echo "Recent workflow runs:" +gh run list --limit 5 2>/dev/null || echo "āš ļø gh CLI not authenticated" +echo "" + +echo "šŸ“‹ RECENT COMMITS" +echo "========================================" +git log --oneline -10 +echo "" + +echo "šŸ”€ BRANCHES" +echo "========================================" +git branch -a +echo "" + +echo "šŸ“ CURRENT STATUS" +echo "========================================" +echo "Current branch: $(git branch --show-current)" +echo "Remote URL: $(git remote get-url origin)" +echo "Git status:" +git status --short +echo "" + +echo "šŸ’¬ DISCORD WEBHOOK CHECK" +echo "========================================" +if gh secret list 2>/dev/null | grep -q "DISCORD_WEBHOOK"; then + echo "āœ… DISCORD_WEBHOOK secret is configured" +else + echo "āŒ DISCORD_WEBHOOK secret not found" + echo " Add it at: https://github.com/cortexlinux/cortex/settings/secrets/actions" +fi +echo "" + +echo "šŸŽÆ ISSUES & PRS" +echo "========================================" +echo "Open issues with bounties:" +gh issue list --label "bounty" --limit 10 2>/dev/null || echo "āš ļø gh CLI issue" +echo "" +echo "Recent PRs:" +gh pr list --limit 5 2>/dev/null || echo "āš ļø gh CLI issue" +echo "" + +echo "āœ… AUDIT COMPLETE" +echo "========================================" +echo "Save this output and share with Claude for full visibility" +echo "" +echo "Next steps:" +echo "1. Share this output with Claude" +echo "2. Claude can now see everything without asking" +echo "3. No more copy/paste needed" diff --git a/scripts/deployment/upload_issue_34.sh b/scripts/deployment/upload_issue_34.sh old mode 100644 new mode 100755 index 13dbf727..9441bc92 --- a/scripts/deployment/upload_issue_34.sh +++ b/scripts/deployment/upload_issue_34.sh @@ -1,36 +1,36 @@ -#!/bin/bash - -# Upload Issue #34 files to GitHub - -echo "šŸ” Enter your GitHub Personal Access Token:" -read -s GITHUB_TOKEN - -REPO="cortexlinux/cortex" -BRANCH="feature/issue-34" - -echo "" -echo "šŸ“¤ Uploading llm_router.py..." -curl -X PUT \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"message\":\"Add LLM Router implementation\",\"content\":\"$(base64 -i llm_router.py)\",\"branch\":\"$BRANCH\"}" \ - "https://api.github.com/repos/$REPO/contents/src/llm_router.py" - -echo "" -echo "šŸ“¤ Uploading test_llm_router.py..." -curl -X PUT \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"message\":\"Add LLM Router tests\",\"content\":\"$(base64 -i test_llm_router.py)\",\"branch\":\"$BRANCH\"}" \ - "https://api.github.com/repos/$REPO/contents/src/test_llm_router.py" - -echo "" -echo "šŸ“¤ Uploading README_LLM_ROUTER.md..." -curl -X PUT \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"message\":\"Add LLM Router documentation\",\"content\":\"$(base64 -i README_LLM_ROUTER.md)\",\"branch\":\"$BRANCH\"}" \ - "https://api.github.com/repos/$REPO/contents/docs/README_LLM_ROUTER.md" - -echo "" -echo "āœ… Upload complete! Check: https://github.com/$REPO/tree/$BRANCH" +#!/bin/bash + +# Upload Issue #34 files to GitHub + +echo "šŸ” Enter your GitHub Personal Access Token:" +read -s GITHUB_TOKEN + +REPO="cortexlinux/cortex" +BRANCH="feature/issue-34" + +echo "" +echo "šŸ“¤ Uploading llm_router.py..." +curl -X PUT \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"message\":\"Add LLM Router implementation\",\"content\":\"$(base64 -i llm_router.py)\",\"branch\":\"$BRANCH\"}" \ + "https://api.github.com/repos/$REPO/contents/src/llm_router.py" + +echo "" +echo "šŸ“¤ Uploading test_llm_router.py..." +curl -X PUT \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"message\":\"Add LLM Router tests\",\"content\":\"$(base64 -i test_llm_router.py)\",\"branch\":\"$BRANCH\"}" \ + "https://api.github.com/repos/$REPO/contents/src/test_llm_router.py" + +echo "" +echo "šŸ“¤ Uploading README_LLM_ROUTER.md..." +curl -X PUT \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"message\":\"Add LLM Router documentation\",\"content\":\"$(base64 -i README_LLM_ROUTER.md)\",\"branch\":\"$BRANCH\"}" \ + "https://api.github.com/repos/$REPO/contents/docs/README_LLM_ROUTER.md" + +echo "" +echo "āœ… Upload complete! Check: https://github.com/$REPO/tree/$BRANCH" diff --git a/scripts/fetch-fork-emails.sh b/scripts/fetch-fork-emails.sh old mode 100644 new mode 100755 index 49ef8e24..fa76ba67 --- a/scripts/fetch-fork-emails.sh +++ b/scripts/fetch-fork-emails.sh @@ -1,72 +1,72 @@ -#!/bin/bash -# fetch-fork-emails.sh -# Fetches public email addresses from Cortex fork contributors -# Usage: ./fetch-fork-emails.sh - -echo "═══════════════════════════════════════════════════════════════════" -echo " CORTEX FORK CONTRIBUTOR EMAIL FETCHER" -echo " $(date '+%Y-%m-%d %H:%M:%S')" -echo "═══════════════════════════════════════════════════════════════════" -echo "" - -OUTPUT_FILE="fork-contributor-contacts.csv" -echo "username,email,name,company,location,twitter,blog,bio" > "$OUTPUT_FILE" - -# Get all fork owners -echo "šŸ“„ Fetching fork contributors..." -echo "" - -FORKS=$(curl -s "https://api.github.com/repos/cortexlinux/cortex/forks?per_page=100" | jq -r '.[].owner.login') - -for username in $FORKS; do - echo -n "→ $username: " - - # Fetch user profile - USER_DATA=$(curl -s "https://api.github.com/users/$username") - - EMAIL=$(echo "$USER_DATA" | jq -r '.email // "N/A"') - NAME=$(echo "$USER_DATA" | jq -r '.name // "N/A"') - COMPANY=$(echo "$USER_DATA" | jq -r '.company // "N/A"') - LOCATION=$(echo "$USER_DATA" | jq -r '.location // "N/A"') - TWITTER=$(echo "$USER_DATA" | jq -r '.twitter_username // "N/A"') - BLOG=$(echo "$USER_DATA" | jq -r '.blog // "N/A"') - BIO=$(echo "$USER_DATA" | jq -r '.bio // "N/A"' | tr ',' ';' | tr '\n' ' ') - - # Try to get email from recent commits if not in profile - if [ "$EMAIL" = "N/A" ] || [ "$EMAIL" = "null" ]; then - COMMIT_EMAIL=$(curl -s "https://api.github.com/users/$username/events/public" | \ - jq -r '[.[] | select(.type=="PushEvent") | .payload.commits[]?.author.email] | first // "N/A"') - if [ "$COMMIT_EMAIL" != "N/A" ] && [ "$COMMIT_EMAIL" != "null" ] && [[ ! "$COMMIT_EMAIL" =~ "noreply" ]]; then - EMAIL="$COMMIT_EMAIL" - fi - fi - - echo "$username,$EMAIL,$NAME,$COMPANY,$LOCATION,$TWITTER,$BLOG,\"$BIO\"" >> "$OUTPUT_FILE" - - if [ "$EMAIL" != "N/A" ] && [ "$EMAIL" != "null" ]; then - echo "āœ“ Found email: $EMAIL" - else - echo "ā—‹ No public email (check Twitter: $TWITTER, Blog: $BLOG)" - fi - - sleep 0.5 # Rate limiting -done - -echo "" -echo "═══════════════════════════════════════════════════════════════════" -echo " SUMMARY" -echo "═══════════════════════════════════════════════════════════════════" -echo "" -TOTAL=$(echo "$FORKS" | wc -l | tr -d ' ') -WITH_EMAIL=$(grep -v "N/A" "$OUTPUT_FILE" | grep -v "null" | grep "@" | wc -l | tr -d ' ') -echo "Total contributors: $TOTAL" -echo "With public email: $WITH_EMAIL" -echo "" -echo "āœ… Results saved to: $OUTPUT_FILE" -echo "" - -# Display results -echo "═══════════════════════════════════════════════════════════════════" -echo " CONTACT DETAILS" -echo "═══════════════════════════════════════════════════════════════════" -column -t -s',' "$OUTPUT_FILE" | head -20 +#!/bin/bash +# fetch-fork-emails.sh +# Fetches public email addresses from Cortex fork contributors +# Usage: ./fetch-fork-emails.sh + +echo "═══════════════════════════════════════════════════════════════════" +echo " CORTEX FORK CONTRIBUTOR EMAIL FETCHER" +echo " $(date '+%Y-%m-%d %H:%M:%S')" +echo "═══════════════════════════════════════════════════════════════════" +echo "" + +OUTPUT_FILE="fork-contributor-contacts.csv" +echo "username,email,name,company,location,twitter,blog,bio" > "$OUTPUT_FILE" + +# Get all fork owners +echo "šŸ“„ Fetching fork contributors..." +echo "" + +FORKS=$(curl -s "https://api.github.com/repos/cortexlinux/cortex/forks?per_page=100" | jq -r '.[].owner.login') + +for username in $FORKS; do + echo -n "→ $username: " + + # Fetch user profile + USER_DATA=$(curl -s "https://api.github.com/users/$username") + + EMAIL=$(echo "$USER_DATA" | jq -r '.email // "N/A"') + NAME=$(echo "$USER_DATA" | jq -r '.name // "N/A"') + COMPANY=$(echo "$USER_DATA" | jq -r '.company // "N/A"') + LOCATION=$(echo "$USER_DATA" | jq -r '.location // "N/A"') + TWITTER=$(echo "$USER_DATA" | jq -r '.twitter_username // "N/A"') + BLOG=$(echo "$USER_DATA" | jq -r '.blog // "N/A"') + BIO=$(echo "$USER_DATA" | jq -r '.bio // "N/A"' | tr ',' ';' | tr '\n' ' ') + + # Try to get email from recent commits if not in profile + if [ "$EMAIL" = "N/A" ] || [ "$EMAIL" = "null" ]; then + COMMIT_EMAIL=$(curl -s "https://api.github.com/users/$username/events/public" | \ + jq -r '[.[] | select(.type=="PushEvent") | .payload.commits[]?.author.email] | first // "N/A"') + if [ "$COMMIT_EMAIL" != "N/A" ] && [ "$COMMIT_EMAIL" != "null" ] && [[ ! "$COMMIT_EMAIL" =~ "noreply" ]]; then + EMAIL="$COMMIT_EMAIL" + fi + fi + + echo "$username,$EMAIL,$NAME,$COMPANY,$LOCATION,$TWITTER,$BLOG,\"$BIO\"" >> "$OUTPUT_FILE" + + if [ "$EMAIL" != "N/A" ] && [ "$EMAIL" != "null" ]; then + echo "āœ“ Found email: $EMAIL" + else + echo "ā—‹ No public email (check Twitter: $TWITTER, Blog: $BLOG)" + fi + + sleep 0.5 # Rate limiting +done + +echo "" +echo "═══════════════════════════════════════════════════════════════════" +echo " SUMMARY" +echo "═══════════════════════════════════════════════════════════════════" +echo "" +TOTAL=$(echo "$FORKS" | wc -l | tr -d ' ') +WITH_EMAIL=$(grep -v "N/A" "$OUTPUT_FILE" | grep -v "null" | grep "@" | wc -l | tr -d ' ') +echo "Total contributors: $TOTAL" +echo "With public email: $WITH_EMAIL" +echo "" +echo "āœ… Results saved to: $OUTPUT_FILE" +echo "" + +# Display results +echo "═══════════════════════════════════════════════════════════════════" +echo " CONTACT DETAILS" +echo "═══════════════════════════════════════════════════════════════════" +column -t -s',' "$OUTPUT_FILE" | head -20 diff --git a/scripts/github/merge-mike-prs.sh b/scripts/github/merge-mike-prs.sh old mode 100644 new mode 100755 index 65f5b71f..1831ac94 --- a/scripts/github/merge-mike-prs.sh +++ b/scripts/github/merge-mike-prs.sh @@ -1,81 +1,81 @@ -#!/bin/bash -# CORTEX - Quick Merge Mike's PRs -# Merges all PRs authored by @mikejmorgan-ai to clear backlog - -set -e - -echo "šŸš€ CORTEX - MERGE MIKE'S IMPLEMENTATION PRs" -echo "===========================================" -echo "" - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") - -export GH_TOKEN="$GITHUB_TOKEN" - -echo "Merging PRs authored by @mikejmorgan-ai..." -echo "" - -# PRs to merge (excluding #17, #18, #21, #37, #38 which are from contributors) -MIKE_PRS=(41 36 34 23 22 20) - -for pr in "${MIKE_PRS[@]}"; do - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "PR #$pr" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Get PR info - pr_info=$(gh pr view $pr --repo $REPO --json title,state,mergeable 2>/dev/null || echo "") - - if [ -z "$pr_info" ]; then - echo "āŒ PR #$pr not found or not accessible" - echo "" - continue - fi - - pr_title=$(echo "$pr_info" | jq -r '.title') - pr_state=$(echo "$pr_info" | jq -r '.state') - pr_mergeable=$(echo "$pr_info" | jq -r '.mergeable') - - echo "Title: $pr_title" - echo "State: $pr_state" - echo "Mergeable: $pr_mergeable" - echo "" - - if [ "$pr_state" != "OPEN" ]; then - echo "ā­ļø PR already merged or closed" - echo "" - continue - fi - - if [ "$pr_mergeable" = "CONFLICTING" ]; then - echo "āš ļø PR has merge conflicts - needs manual resolution" - echo "" - continue - fi - - echo "Merge this PR? (y/n)" - read -n 1 -r - echo "" - - if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "šŸ”„ Merging PR #$pr..." - - gh pr merge $pr --repo $REPO --squash --delete-branch 2>/dev/null && \ - echo "āœ… PR #$pr merged successfully!" || \ - echo "āŒ Failed to merge PR #$pr (may need manual merge)" - else - echo "ā­ļø Skipped PR #$pr" - fi - - echo "" -done - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… MERGE PROCESS COMPLETE" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" -echo "Next steps:" -echo "1. Review contributor PRs: #17, #21, #37, #38" -echo "2. Process bounty payments" -echo "3. Post update to Discord" +#!/bin/bash +# CORTEX - Quick Merge Mike's PRs +# Merges all PRs authored by @mikejmorgan-ai to clear backlog + +set -e + +echo "šŸš€ CORTEX - MERGE MIKE'S IMPLEMENTATION PRs" +echo "===========================================" +echo "" + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") + +export GH_TOKEN="$GITHUB_TOKEN" + +echo "Merging PRs authored by @mikejmorgan-ai..." +echo "" + +# PRs to merge (excluding #17, #18, #21, #37, #38 which are from contributors) +MIKE_PRS=(41 36 34 23 22 20) + +for pr in "${MIKE_PRS[@]}"; do + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "PR #$pr" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Get PR info + pr_info=$(gh pr view $pr --repo $REPO --json title,state,mergeable 2>/dev/null || echo "") + + if [ -z "$pr_info" ]; then + echo "āŒ PR #$pr not found or not accessible" + echo "" + continue + fi + + pr_title=$(echo "$pr_info" | jq -r '.title') + pr_state=$(echo "$pr_info" | jq -r '.state') + pr_mergeable=$(echo "$pr_info" | jq -r '.mergeable') + + echo "Title: $pr_title" + echo "State: $pr_state" + echo "Mergeable: $pr_mergeable" + echo "" + + if [ "$pr_state" != "OPEN" ]; then + echo "ā­ļø PR already merged or closed" + echo "" + continue + fi + + if [ "$pr_mergeable" = "CONFLICTING" ]; then + echo "āš ļø PR has merge conflicts - needs manual resolution" + echo "" + continue + fi + + echo "Merge this PR? (y/n)" + read -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "šŸ”„ Merging PR #$pr..." + + gh pr merge $pr --repo $REPO --squash --delete-branch 2>/dev/null && \ + echo "āœ… PR #$pr merged successfully!" || \ + echo "āŒ Failed to merge PR #$pr (may need manual merge)" + else + echo "ā­ļø Skipped PR #$pr" + fi + + echo "" +done + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… MERGE PROCESS COMPLETE" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "Next steps:" +echo "1. Review contributor PRs: #17, #21, #37, #38" +echo "2. Process bounty payments" +echo "3. Post update to Discord" diff --git a/scripts/github/organize-issues.sh b/scripts/github/organize-issues.sh old mode 100644 new mode 100755 index 28933947..36d7a17e --- a/scripts/github/organize-issues.sh +++ b/scripts/github/organize-issues.sh @@ -1,51 +1,51 @@ -#!/bin/bash -# Label and organize issues for MVP focus - -set -e - -echo "šŸŽÆ ORGANIZING ISSUES FOR MVP FOCUS" -echo "=====================================" - -cd ~/cortex - -echo "Strategy:" -echo " Issues #1-30: MVP Critical" -echo " Issues #31-45: MVP Nice-to-Have" -echo " Issues #46+: Post-MVP" -echo "" - -read -p "Organize all issues? (y/n): " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 0 -fi - -# Create milestones -echo "šŸ“‹ Creating milestones..." -gh api repos/cortexlinux/cortex/milestones --method POST \ - -f title='MVP - Core Features' \ - -f description='Critical features required for MVP launch' 2>/dev/null || echo " MVP milestone exists" - -gh api repos/cortexlinux/cortex/milestones --method POST \ - -f title='Post-MVP - Enhancements' \ - -f description='Features for post-MVP releases' 2>/dev/null || echo " Post-MVP milestone exists" - -echo "" -echo "šŸ·ļø Labeling MVP Critical (#1-30)..." -for i in {1..30}; do - gh issue edit $i --add-label "mvp-critical,priority: critical" --milestone "MVP - Core Features" 2>/dev/null && echo " āœ… #$i" || echo " āš ļø #$i not found" - sleep 0.3 -done - -echo "" -echo "šŸ·ļø Labeling Post-MVP (#46-150)..." -for i in {46..150}; do - gh issue edit $i --add-label "post-mvp" --milestone "Post-MVP - Enhancements" 2>/dev/null - (( i % 20 == 0 )) && echo " Processed through #$i..." && sleep 1 -done - -echo "" -echo "āœ… COMPLETE!" -echo "" -echo "View MVP Critical: https://github.com/cortexlinux/cortex/issues?q=is%3Aopen+label%3Amvp-critical" +#!/bin/bash +# Label and organize issues for MVP focus + +set -e + +echo "šŸŽÆ ORGANIZING ISSUES FOR MVP FOCUS" +echo "=====================================" + +cd ~/cortex + +echo "Strategy:" +echo " Issues #1-30: MVP Critical" +echo " Issues #31-45: MVP Nice-to-Have" +echo " Issues #46+: Post-MVP" +echo "" + +read -p "Organize all issues? (y/n): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +# Create milestones +echo "šŸ“‹ Creating milestones..." +gh api repos/cortexlinux/cortex/milestones --method POST \ + -f title='MVP - Core Features' \ + -f description='Critical features required for MVP launch' 2>/dev/null || echo " MVP milestone exists" + +gh api repos/cortexlinux/cortex/milestones --method POST \ + -f title='Post-MVP - Enhancements' \ + -f description='Features for post-MVP releases' 2>/dev/null || echo " Post-MVP milestone exists" + +echo "" +echo "šŸ·ļø Labeling MVP Critical (#1-30)..." +for i in {1..30}; do + gh issue edit $i --add-label "mvp-critical,priority: critical" --milestone "MVP - Core Features" 2>/dev/null && echo " āœ… #$i" || echo " āš ļø #$i not found" + sleep 0.3 +done + +echo "" +echo "šŸ·ļø Labeling Post-MVP (#46-150)..." +for i in {46..150}; do + gh issue edit $i --add-label "post-mvp" --milestone "Post-MVP - Enhancements" 2>/dev/null + (( i % 20 == 0 )) && echo " Processed through #$i..." && sleep 1 +done + +echo "" +echo "āœ… COMPLETE!" +echo "" +echo "View MVP Critical: https://github.com/cortexlinux/cortex/issues?q=is%3Aopen+label%3Amvp-critical" diff --git a/scripts/github/review-contributor-prs.sh b/scripts/github/review-contributor-prs.sh old mode 100644 new mode 100755 index 03a7a190..8a5be9d1 --- a/scripts/github/review-contributor-prs.sh +++ b/scripts/github/review-contributor-prs.sh @@ -1,314 +1,314 @@ -#!/bin/bash -# CORTEX - CONTRIBUTOR PR REVIEW & MERGE SYSTEM -# Reviews PRs from contributors, tracks bounties, posts thank-yous - -set -e - -echo "šŸ” CORTEX - CONTRIBUTOR PR REVIEW SYSTEM" -echo "========================================" -echo "" - -REPO="cortexlinux/cortex" -GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") - -export GH_TOKEN="$GITHUB_TOKEN" - -# Track bounties owed -BOUNTIES_FILE="$HOME/cortex/bounties_owed.csv" - -# Create bounties file if doesn't exist -if [ ! -f "$BOUNTIES_FILE" ]; then - echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTIES_FILE" -fi - -echo "šŸ“Š CONTRIBUTOR PR REVIEW QUEUE" -echo "────────────────────────────────" -echo "" - -# Contributor PRs to review (in priority order) -declare -A PR_DETAILS -PR_DETAILS[17]="chandrapratamar|Package Manager Wrapper (Issue #7)|100|CRITICAL_MVP_BLOCKER" -PR_DETAILS[37]="AlexanderLuzDH|Progress Notifications (Issue #27)|125|HIGH_PRIORITY" -PR_DETAILS[38]="AlexanderLuzDH|Requirements Pre-flight Check (Issue #28)|100|HIGH_PRIORITY" -PR_DETAILS[21]="aliraza556|Config File Templates (Issue #16)|150|HIGH_PRIORITY" -PR_DETAILS[18]="Sahilbhatane|CLI Interface (Issue #11)|100|DRAFT_WAIT" - -# Function to review a PR -review_pr() { - local pr_num=$1 - local pr_data="${PR_DETAILS[$pr_num]}" - - IFS='|' read -r developer feature bounty priority <<< "$pr_data" - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "šŸ“‹ PR #$pr_num - $feature" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "šŸ‘¤ Developer: @$developer" - echo "šŸŽÆ Feature: $feature" - echo "šŸ’° Bounty: \$$bounty" - echo "šŸ”„ Priority: $priority" - echo "" - - # Check if draft - pr_state=$(gh pr view $pr_num --repo $REPO --json isDraft 2>/dev/null | jq -r '.isDraft') - - if [ "$pr_state" = "true" ]; then - echo "šŸ“ Status: DRAFT - Not ready for review yet" - echo " Action: Skip for now, will review when marked ready" - echo "" - return - fi - - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "REVIEW CHECKLIST" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Before approving, verify:" - echo " [ ] Code implements the feature described in the issue" - echo " [ ] Unit tests included with >80% coverage" - echo " [ ] Documentation/README included" - echo " [ ] Integrates with existing Cortex architecture" - echo " [ ] No obvious bugs or security issues" - echo " [ ] Follows Python best practices" - echo "" - - echo "Actions:" - echo " [v] View PR in browser (to review code)" - echo " [a] Approve & Merge (if review passed)" - echo " [c] Request Changes (if issues found)" - echo " [m] Add Comment (questions/feedback)" - echo " [s] Skip to next PR" - echo " [q] Quit review mode" - echo "" - echo -n "Choose action: " - read -n 1 action - echo "" - echo "" - - case $action in - v|V) - echo "🌐 Opening PR #$pr_num in browser..." - gh pr view $pr_num --repo $REPO --web - echo "" - echo "After reviewing, come back to approve/change/comment." - echo "" - echo "Take action now? (y/n)" - read -n 1 take_action - echo "" - - if [[ ! $take_action =~ ^[Yy]$ ]]; then - echo "ā­ļø Skipping for now..." - return - fi - - # Ask again which action - echo "" - echo "What action? [a]pprove [c]hange [m]comment [s]kip" - read -n 1 action - echo "" - ;;& # Continue to next pattern - - a|A) - echo "āœ… APPROVING & MERGING PR #$pr_num" - echo "" - - # Post approval review - approval_msg="āœ… **APPROVED - Excellent Work!** - -Thank you @$developer for this outstanding contribution! šŸŽ‰ - -**Review Summary:** -- āœ… Code quality: Professional implementation -- āœ… Testing: Comprehensive unit tests included -- āœ… Documentation: Clear and complete -- āœ… Integration: Works seamlessly with Cortex architecture - -**What's Next:** -1. Merging this PR immediately -2. Your bounty of **\$$bounty USD** will be processed within 48 hours -3. Payment via crypto (Bitcoin/USDC) or PayPal - we'll coordinate via issue comment - -**You're making history** - this is a foundational piece of the AI-native operating system! 🧠⚔ - -**Bonus Reminder:** At funding (Feb 2025), you'll receive **2x this bounty** as a thank-you bonus. - -Welcome to the Cortex Linux core contributor team! šŸš€ - ---- -*Automated review from Cortex PR Management System*" - - echo "$approval_msg" | gh pr review $pr_num --repo $REPO --approve --body-file - 2>/dev/null || \ - echo "āš ļø Could not post review (may need manual approval)" - - echo "" - echo "Merging PR #$pr_num now..." - - gh pr merge $pr_num --repo $REPO --squash --delete-branch 2>/dev/null && { - echo "āœ… PR #$pr_num merged successfully!" - - # Record bounty owed - merge_date=$(date +%Y-%m-%d) - echo "$pr_num,$developer,$feature,$bounty,$merge_date,PENDING" >> "$BOUNTIES_FILE" - - echo "" - echo "šŸ’° Bounty recorded: \$$bounty owed to @$developer" - echo " Recorded in: $BOUNTIES_FILE" - } || { - echo "āŒ Merge failed - may need manual intervention" - } - - echo "" - ;; - - c|C) - echo "šŸ”„ REQUESTING CHANGES on PR #$pr_num" - echo "" - echo "Enter your feedback (what needs to change):" - echo "Press Ctrl+D when done" - echo "---" - feedback=$(cat) - - change_msg="šŸ”„ **Changes Requested** - -Thank you for your contribution @$developer! The code is solid, but a few items need attention before merge: - -$feedback - -**Please update and let me know when ready** for re-review. I'll prioritize getting this merged quickly once addressed. - -**Questions?** Comment here or ping me in Discord (#dev-questions). - -We appreciate your patience! šŸ™ - ---- -*Automated review from Cortex PR Management System*" - - echo "$change_msg" | gh pr review $pr_num --repo $REPO --request-changes --body-file - 2>/dev/null || \ - echo "āš ļø Could not post review" - - echo "" - echo "āœ… Change request posted" - echo "" - ;; - - m|M) - echo "šŸ’¬ ADDING COMMENT to PR #$pr_num" - echo "" - echo "Enter your comment:" - echo "Press Ctrl+D when done" - echo "---" - comment=$(cat) - - gh pr comment $pr_num --repo $REPO --body "$comment" 2>/dev/null && \ - echo "āœ… Comment posted" || \ - echo "āš ļø Could not post comment" - - echo "" - ;; - - s|S) - echo "ā­ļø Skipping PR #$pr_num" - echo "" - ;; - - q|Q) - echo "šŸ‘‹ Exiting review mode..." - echo "" - return 1 - ;; - - *) - echo "ā­ļø Invalid action, skipping..." - echo "" - ;; - esac -} - -# Main review loop -echo "Starting PR review process..." -echo "" - -PR_ORDER=(17 37 38 21 18) # Priority order - -for pr in "${PR_ORDER[@]}"; do - review_pr $pr || break -done - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸ“Š REVIEW SESSION COMPLETE" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Show bounties owed -if [ -f "$BOUNTIES_FILE" ]; then - echo "šŸ’° BOUNTIES OWED (from this session and previous)" - echo "──────────────────────────────────────────────────" - echo "" - - total_owed=0 - - tail -n +2 "$BOUNTIES_FILE" | while IFS=',' read -r pr dev feature amount date status; do - if [ "$status" = "PENDING" ]; then - echo " PR #$pr - @$dev: \$$amount ($feature)" - total_owed=$((total_owed + amount)) - fi - done - - echo "" - echo " Total pending: \$$total_owed USD" - echo "" - echo " Payment file: $BOUNTIES_FILE" - echo "" -fi - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "šŸŽÆ NEXT STEPS" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" -echo "1. Process bounty payments (see $BOUNTIES_FILE)" -echo "2. Post Discord announcement about merged PRs" -echo "3. Check if Issue #7 unblocked (if PR #17 merged)" -echo "4. Welcome new developers to comment on issues" -echo "" - -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -# Generate Discord announcement -discord_msg="šŸŽ‰ **PR MERGE UPDATE - $(date +%Y-%m-%d)** - -**PRs Merged Today:** -(Check the bounties file for details) - -**Critical Path Progress:** -- Issue #7 (Package Manager): $([ -f "$BOUNTIES_FILE" ] && grep -q "^17," "$BOUNTIES_FILE" && echo "āœ… MERGED - MVP BLOCKER CLEARED!" || echo "ā³ In review") - -**Bounties Being Processed:** -- See individual PR comments for payment coordination -- 2x bonus reminder: When we close funding (Feb 2025), all bounties paid so far get 2x bonus - -**What This Means:** -- MVP velocity accelerating -- February funding timeline on track -- Professional team execution demonstrated - -**For Contributors:** -- Check your merged PRs for bounty coordination comments -- Payment within 48 hours of merge -- Crypto (Bitcoin/USDC) or PayPal options - -**Open Issues Still Available:** -Browse: https://github.com/cortexlinux/cortex/issues -Join: Discord #dev-questions - -Let's keep the momentum! 🧠⚔" - -echo "šŸ“± DISCORD ANNOUNCEMENT (copy and post to #announcements)" -echo "────────────────────────────────────────────────────────" -echo "" -echo "$discord_msg" -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "āœ… PR Review System Complete!" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +#!/bin/bash +# CORTEX - CONTRIBUTOR PR REVIEW & MERGE SYSTEM +# Reviews PRs from contributors, tracks bounties, posts thank-yous + +set -e + +echo "šŸ” CORTEX - CONTRIBUTOR PR REVIEW SYSTEM" +echo "========================================" +echo "" + +REPO="cortexlinux/cortex" +GITHUB_TOKEN=$(grep GITHUB_TOKEN ~/.zshrc | cut -d'=' -f2 | tr -d '"' | tr -d "'") + +export GH_TOKEN="$GITHUB_TOKEN" + +# Track bounties owed +BOUNTIES_FILE="$HOME/cortex/bounties_owed.csv" + +# Create bounties file if doesn't exist +if [ ! -f "$BOUNTIES_FILE" ]; then + echo "PR,Developer,Feature,Bounty_Amount,Date_Merged,Status" > "$BOUNTIES_FILE" +fi + +echo "šŸ“Š CONTRIBUTOR PR REVIEW QUEUE" +echo "────────────────────────────────" +echo "" + +# Contributor PRs to review (in priority order) +declare -A PR_DETAILS +PR_DETAILS[17]="chandrapratamar|Package Manager Wrapper (Issue #7)|100|CRITICAL_MVP_BLOCKER" +PR_DETAILS[37]="AlexanderLuzDH|Progress Notifications (Issue #27)|125|HIGH_PRIORITY" +PR_DETAILS[38]="AlexanderLuzDH|Requirements Pre-flight Check (Issue #28)|100|HIGH_PRIORITY" +PR_DETAILS[21]="aliraza556|Config File Templates (Issue #16)|150|HIGH_PRIORITY" +PR_DETAILS[18]="Sahilbhatane|CLI Interface (Issue #11)|100|DRAFT_WAIT" + +# Function to review a PR +review_pr() { + local pr_num=$1 + local pr_data="${PR_DETAILS[$pr_num]}" + + IFS='|' read -r developer feature bounty priority <<< "$pr_data" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "šŸ“‹ PR #$pr_num - $feature" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "šŸ‘¤ Developer: @$developer" + echo "šŸŽÆ Feature: $feature" + echo "šŸ’° Bounty: \$$bounty" + echo "šŸ”„ Priority: $priority" + echo "" + + # Check if draft + pr_state=$(gh pr view $pr_num --repo $REPO --json isDraft 2>/dev/null | jq -r '.isDraft') + + if [ "$pr_state" = "true" ]; then + echo "šŸ“ Status: DRAFT - Not ready for review yet" + echo " Action: Skip for now, will review when marked ready" + echo "" + return + fi + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "REVIEW CHECKLIST" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Before approving, verify:" + echo " [ ] Code implements the feature described in the issue" + echo " [ ] Unit tests included with >80% coverage" + echo " [ ] Documentation/README included" + echo " [ ] Integrates with existing Cortex architecture" + echo " [ ] No obvious bugs or security issues" + echo " [ ] Follows Python best practices" + echo "" + + echo "Actions:" + echo " [v] View PR in browser (to review code)" + echo " [a] Approve & Merge (if review passed)" + echo " [c] Request Changes (if issues found)" + echo " [m] Add Comment (questions/feedback)" + echo " [s] Skip to next PR" + echo " [q] Quit review mode" + echo "" + echo -n "Choose action: " + read -n 1 action + echo "" + echo "" + + case $action in + v|V) + echo "🌐 Opening PR #$pr_num in browser..." + gh pr view $pr_num --repo $REPO --web + echo "" + echo "After reviewing, come back to approve/change/comment." + echo "" + echo "Take action now? (y/n)" + read -n 1 take_action + echo "" + + if [[ ! $take_action =~ ^[Yy]$ ]]; then + echo "ā­ļø Skipping for now..." + return + fi + + # Ask again which action + echo "" + echo "What action? [a]pprove [c]hange [m]comment [s]kip" + read -n 1 action + echo "" + ;;& # Continue to next pattern + + a|A) + echo "āœ… APPROVING & MERGING PR #$pr_num" + echo "" + + # Post approval review + approval_msg="āœ… **APPROVED - Excellent Work!** + +Thank you @$developer for this outstanding contribution! šŸŽ‰ + +**Review Summary:** +- āœ… Code quality: Professional implementation +- āœ… Testing: Comprehensive unit tests included +- āœ… Documentation: Clear and complete +- āœ… Integration: Works seamlessly with Cortex architecture + +**What's Next:** +1. Merging this PR immediately +2. Your bounty of **\$$bounty USD** will be processed within 48 hours +3. Payment via crypto (Bitcoin/USDC) or PayPal - we'll coordinate via issue comment + +**You're making history** - this is a foundational piece of the AI-native operating system! 🧠⚔ + +**Bonus Reminder:** At funding (Feb 2025), you'll receive **2x this bounty** as a thank-you bonus. + +Welcome to the Cortex Linux core contributor team! šŸš€ + +--- +*Automated review from Cortex PR Management System*" + + echo "$approval_msg" | gh pr review $pr_num --repo $REPO --approve --body-file - 2>/dev/null || \ + echo "āš ļø Could not post review (may need manual approval)" + + echo "" + echo "Merging PR #$pr_num now..." + + gh pr merge $pr_num --repo $REPO --squash --delete-branch 2>/dev/null && { + echo "āœ… PR #$pr_num merged successfully!" + + # Record bounty owed + merge_date=$(date +%Y-%m-%d) + echo "$pr_num,$developer,$feature,$bounty,$merge_date,PENDING" >> "$BOUNTIES_FILE" + + echo "" + echo "šŸ’° Bounty recorded: \$$bounty owed to @$developer" + echo " Recorded in: $BOUNTIES_FILE" + } || { + echo "āŒ Merge failed - may need manual intervention" + } + + echo "" + ;; + + c|C) + echo "šŸ”„ REQUESTING CHANGES on PR #$pr_num" + echo "" + echo "Enter your feedback (what needs to change):" + echo "Press Ctrl+D when done" + echo "---" + feedback=$(cat) + + change_msg="šŸ”„ **Changes Requested** + +Thank you for your contribution @$developer! The code is solid, but a few items need attention before merge: + +$feedback + +**Please update and let me know when ready** for re-review. I'll prioritize getting this merged quickly once addressed. + +**Questions?** Comment here or ping me in Discord (#dev-questions). + +We appreciate your patience! šŸ™ + +--- +*Automated review from Cortex PR Management System*" + + echo "$change_msg" | gh pr review $pr_num --repo $REPO --request-changes --body-file - 2>/dev/null || \ + echo "āš ļø Could not post review" + + echo "" + echo "āœ… Change request posted" + echo "" + ;; + + m|M) + echo "šŸ’¬ ADDING COMMENT to PR #$pr_num" + echo "" + echo "Enter your comment:" + echo "Press Ctrl+D when done" + echo "---" + comment=$(cat) + + gh pr comment $pr_num --repo $REPO --body "$comment" 2>/dev/null && \ + echo "āœ… Comment posted" || \ + echo "āš ļø Could not post comment" + + echo "" + ;; + + s|S) + echo "ā­ļø Skipping PR #$pr_num" + echo "" + ;; + + q|Q) + echo "šŸ‘‹ Exiting review mode..." + echo "" + return 1 + ;; + + *) + echo "ā­ļø Invalid action, skipping..." + echo "" + ;; + esac +} + +# Main review loop +echo "Starting PR review process..." +echo "" + +PR_ORDER=(17 37 38 21 18) # Priority order + +for pr in "${PR_ORDER[@]}"; do + review_pr $pr || break +done + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸ“Š REVIEW SESSION COMPLETE" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Show bounties owed +if [ -f "$BOUNTIES_FILE" ]; then + echo "šŸ’° BOUNTIES OWED (from this session and previous)" + echo "──────────────────────────────────────────────────" + echo "" + + total_owed=0 + + tail -n +2 "$BOUNTIES_FILE" | while IFS=',' read -r pr dev feature amount date status; do + if [ "$status" = "PENDING" ]; then + echo " PR #$pr - @$dev: \$$amount ($feature)" + total_owed=$((total_owed + amount)) + fi + done + + echo "" + echo " Total pending: \$$total_owed USD" + echo "" + echo " Payment file: $BOUNTIES_FILE" + echo "" +fi + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "šŸŽÆ NEXT STEPS" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo "1. Process bounty payments (see $BOUNTIES_FILE)" +echo "2. Post Discord announcement about merged PRs" +echo "3. Check if Issue #7 unblocked (if PR #17 merged)" +echo "4. Welcome new developers to comment on issues" +echo "" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Generate Discord announcement +discord_msg="šŸŽ‰ **PR MERGE UPDATE - $(date +%Y-%m-%d)** + +**PRs Merged Today:** +(Check the bounties file for details) + +**Critical Path Progress:** +- Issue #7 (Package Manager): $([ -f "$BOUNTIES_FILE" ] && grep -q "^17," "$BOUNTIES_FILE" && echo "āœ… MERGED - MVP BLOCKER CLEARED!" || echo "ā³ In review") + +**Bounties Being Processed:** +- See individual PR comments for payment coordination +- 2x bonus reminder: When we close funding (Feb 2025), all bounties paid so far get 2x bonus + +**What This Means:** +- MVP velocity accelerating +- February funding timeline on track +- Professional team execution demonstrated + +**For Contributors:** +- Check your merged PRs for bounty coordination comments +- Payment within 48 hours of merge +- Crypto (Bitcoin/USDC) or PayPal options + +**Open Issues Still Available:** +Browse: https://github.com/cortexlinux/cortex/issues +Join: Discord #dev-questions + +Let's keep the momentum! 🧠⚔" + +echo "šŸ“± DISCORD ANNOUNCEMENT (copy and post to #announcements)" +echo "────────────────────────────────────────────────────────" +echo "" +echo "$discord_msg" +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "āœ… PR Review System Complete!" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/scripts/recruit-ready.sh b/scripts/recruit-ready.sh old mode 100644 new mode 100755 index 80ba7991..1efb5777 --- a/scripts/recruit-ready.sh +++ b/scripts/recruit-ready.sh @@ -1,175 +1,175 @@ -#!/bin/bash -# -# recruit-ready.sh - Repository status and Discord recruitment message generator -# For Cortex Linux repository management -# - -set -e - -REPO="cortexlinux/cortex" -DISCORD_BLUE="\033[34m" -DISCORD_GREEN="\033[32m" -DISCORD_YELLOW="\033[33m" -DISCORD_RED="\033[31m" -DISCORD_CYAN="\033[36m" -BOLD="\033[1m" -RESET="\033[0m" - -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo -e "${BOLD}${DISCORD_CYAN} CORTEX LINUX - REPOSITORY STATUS REPORT${RESET}" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo "" - -# 1. CI Status -echo -e "${BOLD}${DISCORD_BLUE}šŸ“Š CI STATUS${RESET}" -echo "─────────────────────────────────────────" -CI_STATUS=$(gh run list --repo $REPO --workflow=ci.yml --limit 1 --json status,conclusion,displayTitle --jq '.[0]') -CI_CONCLUSION=$(echo $CI_STATUS | jq -r '.conclusion') -CI_TITLE=$(echo $CI_STATUS | jq -r '.displayTitle') - -if [ "$CI_CONCLUSION" = "success" ]; then - echo -e "${DISCORD_GREEN}āœ… CI PASSING${RESET} - $CI_TITLE" -elif [ "$CI_CONCLUSION" = "failure" ]; then - echo -e "${DISCORD_RED}āŒ CI FAILING${RESET} - $CI_TITLE" -else - echo -e "${DISCORD_YELLOW}ā³ CI IN PROGRESS${RESET} - $CI_TITLE" -fi -echo "" - -# 2. Repository Stats -echo -e "${BOLD}${DISCORD_BLUE}šŸ“ˆ REPOSITORY STATS${RESET}" -echo "─────────────────────────────────────────" -REPO_STATS=$(gh api repos/$REPO --jq '{stars: .stargazers_count, forks: .forks_count, issues: .open_issues_count, watchers: .subscribers_count}') -STARS=$(echo $REPO_STATS | jq -r '.stars') -FORKS=$(echo $REPO_STATS | jq -r '.forks') -ISSUES=$(echo $REPO_STATS | jq -r '.issues') -WATCHERS=$(echo $REPO_STATS | jq -r '.watchers') - -echo "⭐ Stars: $STARS | šŸ“ Forks: $FORKS | šŸ“‹ Open Issues: $ISSUES | šŸ‘€ Watchers: $WATCHERS" -echo "" - -# 3. Mergeable PRs -echo -e "${BOLD}${DISCORD_BLUE}āœ… MERGEABLE PRs (Ready to Merge)${RESET}" -echo "─────────────────────────────────────────" -MERGEABLE_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,mergeable,reviewDecision --jq '.[] | select(.mergeable == "MERGEABLE") | "PR #\(.number): \(.title) by @\(.author.login)"') - -if [ -z "$MERGEABLE_PRS" ]; then - echo "No PRs currently ready to merge" -else - echo "$MERGEABLE_PRS" -fi -echo "" - -# 4. PRs Needing Review -echo -e "${BOLD}${DISCORD_BLUE}šŸ‘€ PRs NEEDING REVIEW${RESET}" -echo "─────────────────────────────────────────" -REVIEW_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,reviewDecision --jq '.[] | select(.reviewDecision == "REVIEW_REQUIRED" or .reviewDecision == "") | "PR #\(.number): \(.title) by @\(.author.login)"' | head -10) - -if [ -z "$REVIEW_PRS" ]; then - echo "No PRs awaiting review" -else - echo "$REVIEW_PRS" -fi -echo "" - -# 5. Recently Merged PRs (potential bounty payments) -echo -e "${BOLD}${DISCORD_YELLOW}šŸ’° RECENTLY MERGED (Check Bounty Payments)${RESET}" -echo "─────────────────────────────────────────" -MERGED_PRS=$(gh pr list --repo $REPO --state merged --limit 10 --json number,title,author,mergedAt,labels --jq '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Merged: \(.mergedAt[:10])"') - -if [ -z "$MERGED_PRS" ]; then - echo "No recently merged PRs" -else - echo "$MERGED_PRS" -fi -echo "" - -# 6. Open Issues with Bounties -echo -e "${BOLD}${DISCORD_BLUE}šŸŽÆ OPEN ISSUES WITH BOUNTIES${RESET}" -echo "─────────────────────────────────────────" -BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --label "bounty" --json number,title,labels --jq '.[] | "#\(.number): \(.title)"' 2>/dev/null || echo "") - -if [ -z "$BOUNTY_ISSUES" ]; then - # Try to find issues that might have bounty in title or other bounty-related labels - BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --json number,title,labels --jq '.[] | select(.title | test("bounty|\\$"; "i")) | "#\(.number): \(.title)"' 2>/dev/null || echo "No bounty issues found") -fi - -if [ -z "$BOUNTY_ISSUES" ] || [ "$BOUNTY_ISSUES" = "No bounty issues found" ]; then - echo "No issues with bounty labels found" - echo "Tip: Add 'bounty' label to issues to track them here" -else - echo "$BOUNTY_ISSUES" -fi -echo "" - -# 7. Top Contributors (from recent PRs) -echo -e "${BOLD}${DISCORD_BLUE}šŸ‘„ ACTIVE CONTRIBUTORS (Recent PRs)${RESET}" -echo "─────────────────────────────────────────" -CONTRIBUTORS=$(gh pr list --repo $REPO --state all --limit 50 --json author --jq '.[].author.login' | sort | uniq -c | sort -rn | head -5) -echo "$CONTRIBUTORS" -echo "" - -# 8. Discord Recruitment Message -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo -e "${BOLD}${DISCORD_CYAN} DISCORD RECRUITMENT MESSAGE (Copy Below)${RESET}" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo "" - -# Count open PRs and issues for the message -OPEN_PRS=$(gh pr list --repo $REPO --state open --json number --jq 'length') -OPEN_ISSUES_COUNT=$(gh issue list --repo $REPO --state open --json number --jq 'length') - -cat << 'DISCORD_MSG' -``` -šŸš€ CORTEX LINUX - OPEN SOURCE CONTRIBUTORS WANTED! šŸš€ -``` - -**What is Cortex Linux?** -An AI-powered package manager for Debian/Ubuntu that understands natural language. Instead of memorizing apt commands, just tell Cortex what you want: - -```bash -cortex install "set up a Python ML environment with TensorFlow" -``` - -**Why Contribute?** -DISCORD_MSG - -echo "- šŸ’° **Bounty Program** - Get paid for merged PRs" -echo "- 🌟 **$STARS stars** and growing" -echo "- šŸ”„ **$OPEN_PRS open PRs** to review/merge" -echo "- šŸ“‹ **$OPEN_ISSUES_COUNT open issues** to work on" -echo "- šŸ¤ Friendly community, fast PR reviews" - -cat << 'DISCORD_MSG' - -**Good First Issues:** -- Documentation improvements -- Test coverage -- Bug fixes with clear reproduction steps - -**Tech Stack:** -- Python 3.10+ -- OpenAI/Anthropic APIs -- Rich TUI library - -**Links:** -DISCORD_MSG - -echo "- GitHub: https://github.com/$REPO" -echo "- Discussions: https://github.com/$REPO/discussions" - -cat << 'DISCORD_MSG' - -**How to Start:** -1. Fork the repo -2. Pick an issue labeled `good-first-issue` or `help-wanted` -3. Submit a PR -4. Get reviewed & merged! - -Drop a šŸ‘‹ if you're interested or have questions! -DISCORD_MSG - -echo "" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" -echo -e "${BOLD}Generated: $(date)${RESET}" -echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +#!/bin/bash +# +# recruit-ready.sh - Repository status and Discord recruitment message generator +# For Cortex Linux repository management +# + +set -e + +REPO="cortexlinux/cortex" +DISCORD_BLUE="\033[34m" +DISCORD_GREEN="\033[32m" +DISCORD_YELLOW="\033[33m" +DISCORD_RED="\033[31m" +DISCORD_CYAN="\033[36m" +BOLD="\033[1m" +RESET="\033[0m" + +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo -e "${BOLD}${DISCORD_CYAN} CORTEX LINUX - REPOSITORY STATUS REPORT${RESET}" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo "" + +# 1. CI Status +echo -e "${BOLD}${DISCORD_BLUE}šŸ“Š CI STATUS${RESET}" +echo "─────────────────────────────────────────" +CI_STATUS=$(gh run list --repo $REPO --workflow=ci.yml --limit 1 --json status,conclusion,displayTitle --jq '.[0]') +CI_CONCLUSION=$(echo $CI_STATUS | jq -r '.conclusion') +CI_TITLE=$(echo $CI_STATUS | jq -r '.displayTitle') + +if [ "$CI_CONCLUSION" = "success" ]; then + echo -e "${DISCORD_GREEN}āœ… CI PASSING${RESET} - $CI_TITLE" +elif [ "$CI_CONCLUSION" = "failure" ]; then + echo -e "${DISCORD_RED}āŒ CI FAILING${RESET} - $CI_TITLE" +else + echo -e "${DISCORD_YELLOW}ā³ CI IN PROGRESS${RESET} - $CI_TITLE" +fi +echo "" + +# 2. Repository Stats +echo -e "${BOLD}${DISCORD_BLUE}šŸ“ˆ REPOSITORY STATS${RESET}" +echo "─────────────────────────────────────────" +REPO_STATS=$(gh api repos/$REPO --jq '{stars: .stargazers_count, forks: .forks_count, issues: .open_issues_count, watchers: .subscribers_count}') +STARS=$(echo $REPO_STATS | jq -r '.stars') +FORKS=$(echo $REPO_STATS | jq -r '.forks') +ISSUES=$(echo $REPO_STATS | jq -r '.issues') +WATCHERS=$(echo $REPO_STATS | jq -r '.watchers') + +echo "⭐ Stars: $STARS | šŸ“ Forks: $FORKS | šŸ“‹ Open Issues: $ISSUES | šŸ‘€ Watchers: $WATCHERS" +echo "" + +# 3. Mergeable PRs +echo -e "${BOLD}${DISCORD_BLUE}āœ… MERGEABLE PRs (Ready to Merge)${RESET}" +echo "─────────────────────────────────────────" +MERGEABLE_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,mergeable,reviewDecision --jq '.[] | select(.mergeable == "MERGEABLE") | "PR #\(.number): \(.title) by @\(.author.login)"') + +if [ -z "$MERGEABLE_PRS" ]; then + echo "No PRs currently ready to merge" +else + echo "$MERGEABLE_PRS" +fi +echo "" + +# 4. PRs Needing Review +echo -e "${BOLD}${DISCORD_BLUE}šŸ‘€ PRs NEEDING REVIEW${RESET}" +echo "─────────────────────────────────────────" +REVIEW_PRS=$(gh pr list --repo $REPO --state open --json number,title,author,reviewDecision --jq '.[] | select(.reviewDecision == "REVIEW_REQUIRED" or .reviewDecision == "") | "PR #\(.number): \(.title) by @\(.author.login)"' | head -10) + +if [ -z "$REVIEW_PRS" ]; then + echo "No PRs awaiting review" +else + echo "$REVIEW_PRS" +fi +echo "" + +# 5. Recently Merged PRs (potential bounty payments) +echo -e "${BOLD}${DISCORD_YELLOW}šŸ’° RECENTLY MERGED (Check Bounty Payments)${RESET}" +echo "─────────────────────────────────────────" +MERGED_PRS=$(gh pr list --repo $REPO --state merged --limit 10 --json number,title,author,mergedAt,labels --jq '.[] | "PR #\(.number): \(.title) by @\(.author.login) - Merged: \(.mergedAt[:10])"') + +if [ -z "$MERGED_PRS" ]; then + echo "No recently merged PRs" +else + echo "$MERGED_PRS" +fi +echo "" + +# 6. Open Issues with Bounties +echo -e "${BOLD}${DISCORD_BLUE}šŸŽÆ OPEN ISSUES WITH BOUNTIES${RESET}" +echo "─────────────────────────────────────────" +BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --label "bounty" --json number,title,labels --jq '.[] | "#\(.number): \(.title)"' 2>/dev/null || echo "") + +if [ -z "$BOUNTY_ISSUES" ]; then + # Try to find issues that might have bounty in title or other bounty-related labels + BOUNTY_ISSUES=$(gh issue list --repo $REPO --state open --json number,title,labels --jq '.[] | select(.title | test("bounty|\\$"; "i")) | "#\(.number): \(.title)"' 2>/dev/null || echo "No bounty issues found") +fi + +if [ -z "$BOUNTY_ISSUES" ] || [ "$BOUNTY_ISSUES" = "No bounty issues found" ]; then + echo "No issues with bounty labels found" + echo "Tip: Add 'bounty' label to issues to track them here" +else + echo "$BOUNTY_ISSUES" +fi +echo "" + +# 7. Top Contributors (from recent PRs) +echo -e "${BOLD}${DISCORD_BLUE}šŸ‘„ ACTIVE CONTRIBUTORS (Recent PRs)${RESET}" +echo "─────────────────────────────────────────" +CONTRIBUTORS=$(gh pr list --repo $REPO --state all --limit 50 --json author --jq '.[].author.login' | sort | uniq -c | sort -rn | head -5) +echo "$CONTRIBUTORS" +echo "" + +# 8. Discord Recruitment Message +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo -e "${BOLD}${DISCORD_CYAN} DISCORD RECRUITMENT MESSAGE (Copy Below)${RESET}" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo "" + +# Count open PRs and issues for the message +OPEN_PRS=$(gh pr list --repo $REPO --state open --json number --jq 'length') +OPEN_ISSUES_COUNT=$(gh issue list --repo $REPO --state open --json number --jq 'length') + +cat << 'DISCORD_MSG' +``` +šŸš€ CORTEX LINUX - OPEN SOURCE CONTRIBUTORS WANTED! šŸš€ +``` + +**What is Cortex Linux?** +An AI-powered package manager for Debian/Ubuntu that understands natural language. Instead of memorizing apt commands, just tell Cortex what you want: + +```bash +cortex install "set up a Python ML environment with TensorFlow" +``` + +**Why Contribute?** +DISCORD_MSG + +echo "- šŸ’° **Bounty Program** - Get paid for merged PRs" +echo "- 🌟 **$STARS stars** and growing" +echo "- šŸ”„ **$OPEN_PRS open PRs** to review/merge" +echo "- šŸ“‹ **$OPEN_ISSUES_COUNT open issues** to work on" +echo "- šŸ¤ Friendly community, fast PR reviews" + +cat << 'DISCORD_MSG' + +**Good First Issues:** +- Documentation improvements +- Test coverage +- Bug fixes with clear reproduction steps + +**Tech Stack:** +- Python 3.10+ +- OpenAI/Anthropic APIs +- Rich TUI library + +**Links:** +DISCORD_MSG + +echo "- GitHub: https://github.com/$REPO" +echo "- Discussions: https://github.com/$REPO/discussions" + +cat << 'DISCORD_MSG' + +**How to Start:** +1. Fork the repo +2. Pick an issue labeled `good-first-issue` or `help-wanted` +3. Submit a PR +4. Get reviewed & merged! + +Drop a šŸ‘‹ if you're interested or have questions! +DISCORD_MSG + +echo "" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" +echo -e "${BOLD}Generated: $(date)${RESET}" +echo -e "${BOLD}${DISCORD_CYAN}════════════════════════════════════════════════════════════${RESET}" diff --git a/tests/test_health_monitor.py b/tests/test_health_monitor.py index 92fbaba8..087357bc 100644 --- a/tests/test_health_monitor.py +++ b/tests/test_health_monitor.py @@ -111,7 +111,7 @@ def test_apt_updates(self, mock_run): # Expected score: 100 - 14 = 86 pts self.assertEqual(result.score, 86) - self.assertIn("3 pending", result.details) + self.assertIn("2 packages", result.details) class TestHealthMonitor(unittest.TestCase): From 7a6d42d7bfc9832ba35063a720bec4aa6afd1de8 Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Sat, 20 Dec 2025 04:53:19 +0000 Subject: [PATCH 17/18] style: fix import sorting in cli.py --- cortex/cli.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cortex/cli.py b/cortex/cli.py index 81f77ce2..1ae84cf6 100644 --- a/cortex/cli.py +++ b/cortex/cli.py @@ -33,7 +33,6 @@ ) - class CortexCLI: def __init__(self, verbose: bool = False): self.spinner_chars = ["ā ‹", "ā ™", "ā ¹", "ā ø", "ā ¼", "ā “", "ā ¦", "ā §", "ā ‡", "ā "] @@ -229,9 +228,7 @@ def health(self, args): console.print(f" {i}. {rec}") console.print() - console.print( - "[dim]Run suggested commands manually to improve your score.[/dim]" - ) + console.print("[dim]Run suggested commands manually to improve your score.[/dim]") else: self._print_success("System is in excellent health! No actions needed.") @@ -447,7 +444,7 @@ def progress_callback(current, total, step): coordinator = InstallationCoordinator( commands=commands, - descriptions=[f"Step {i+1}" for i in range(len(commands))], + descriptions=[f"Step {i + 1}" for i in range(len(commands))], timeout=300, stop_on_error=True, progress_callback=progress_callback, @@ -576,7 +573,7 @@ def history(self, limit: int = 20, status: str | None = None, show_id: str | Non date = r.timestamp[:19].replace("T", " ") packages = ", ".join(r.packages[:2]) if len(r.packages) > 2: - packages += f" +{len(r.packages)-2}" + packages += f" +{len(r.packages) - 2}" print( f"{r.id:<18} {date:<20} {r.operation_type.value:<12} {packages:<30} {r.status.value:<15}" From 127bb75f53d33b8b026845f23a914b721bc00e08 Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Sat, 20 Dec 2025 04:55:06 +0000 Subject: [PATCH 18/18] style: fix black formatting in intent module --- src/intent/clarifier.py | 4 +--- src/intent/llm_agent.py | 20 +++++--------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/intent/clarifier.py b/src/intent/clarifier.py index ce8378c8..d49fd9a6 100644 --- a/src/intent/clarifier.py +++ b/src/intent/clarifier.py @@ -23,9 +23,7 @@ def needs_clarification(self, intents: list[Intent], text: str) -> str | None: # 2. If user says "machine learning tools" but nothing specific generic_terms = ["ml", "machine learning", "deep learning", "ai tools"] if any(term in text for term in generic_terms) and len(intents) == 0: - return ( - "Which ML frameworks do you need? (PyTorch, TensorFlow, JupyterLab...)" - ) + return "Which ML frameworks do you need? (PyTorch, TensorFlow, JupyterLab...)" # 3. If user asks to install CUDA but no GPU exists in context if any(i.target == "cuda" for i in intents) and "gpu" not in text: diff --git a/src/intent/llm_agent.py b/src/intent/llm_agent.py index f2d604ec..195a42f0 100644 --- a/src/intent/llm_agent.py +++ b/src/intent/llm_agent.py @@ -24,9 +24,7 @@ class LLMIntentAgent: - session context """ - def __init__( - self, api_key: str | None = None, model: str = "claude-3-5-sonnet-20240620" - ): + def __init__(self, api_key: str | None = None, model: str = "claude-3-5-sonnet-20240620"): # LLM is enabled ONLY if SDK + API key is available if Anthropic is None or api_key is None: @@ -92,9 +90,7 @@ def process(self, text: str): # ---------------------------------------------- # LLM enhancement of intents # ---------------------------------------------- - def enhance_intents_with_llm( - self, text: str, intents: list[Intent] - ) -> list[Intent]: + def enhance_intents_with_llm(self, text: str, intents: list[Intent]) -> list[Intent]: prompt = f""" You are an installation-intent expert. Convert the user request into structured intents. @@ -116,9 +112,7 @@ def enhance_intents_with_llm( ) # ---- Safety check ---- - if not getattr(response, "content", None) or not hasattr( - response.content[0], "text" - ): + if not getattr(response, "content", None) or not hasattr(response.content[0], "text"): return intents llm_output = response.content[0].text.lower().split("\n") @@ -161,13 +155,9 @@ def suggest_optimizations(self, text: str) -> list[str]: ) # ---- Safety check ---- - if not getattr(response, "content", None) or not hasattr( - response.content[0], "text" - ): + if not getattr(response, "content", None) or not hasattr(response.content[0], "text"): return [] return [ - line.strip() - for line in response.content[0].text.strip().split("\n") - if line.strip() + line.strip() for line in response.content[0].text.strip().split("\n") if line.strip() ]