#!/usr/bin/env ruby # frozen_string_literal: true # tools/adn.rb β€” Interfaz de lΓ­nea de comandos unificada para el sistema ADN # ============================================================================ # πŸ€– DIRECTIVA IA: Esta herramienta es el punto de entrada principal para la # automatizaciΓ³n del ADN. Su objetivo es reducir progresivamente la dependencia # de la IA mediante scripts Ruby autΓ³nomos. # ============================================================================ # Uso: ./tools/adn/run [opciones] [argumentos] # # Subcomandos disponibles: # validador [--watch] [--fix] Validar cumplimiento del ADN en bitΓ‘coras # contexto [--cmd] Obtener contexto topolΓ³gico de un nodo # salud [--dashboard] [--json] Ver salud general del sistema ADN # generar bitacora [fecha] Generar nueva bitΓ‘cora (fecha: YYYY-MM-DD) # generar nodo Generar ficha de nodo # generar proyecto Generar manifiesto de proyecto # conocimiento asimilar Asimilar documentaciΓ³n al ADN # evolucion Gestionar propuesta de evoluciΓ³n del ADN # ayuda [subcomando] Mostrar ayuda detallada # # DocumentaciΓ³n completa: docs/plan/260307-1400_Plan_Mejoras_ADN.md require 'optparse' require 'json' require 'fileutils' require 'date' require 'time' module ADN # El script ahora estΓ‘ en tools/adn/run, por lo que la raΓ­z es 2 niveles arriba PROJECT_ROOT = File.expand_path('../..', __dir__) NODOS_DIR = File.join(PROJECT_ROOT, 'nodos') PROYECTOS_DIR = File.join(PROJECT_ROOT, 'docs', 'proyectos') # CorrecciΓ³n a las rutas de bitΓ‘coras y logs BITACORAS_DIR = File.join(PROJECT_ROOT, 'docs', 'bitacoras') LOGS_DIR = File.join(PROJECT_ROOT, 'docs', 'bitacoras', '_hist') # Aseguramos que los directorios existan FileUtils.mkdir_p(LOGS_DIR) unless Dir.exist?(LOGS_DIR) FileUtils.mkdir_p(BITACORAS_DIR) unless Dir.exist?(BITACORAS_DIR) end require_relative 'core/logger' require_relative 'core/eventos' require_relative 'core/triggers' require_relative 'core/validador' require_relative 'comando/db' require_relative 'comando/plan' require_relative 'comando/backup' require_relative 'comando/triggers' require_relative 'comando/conocimiento' require_relative 'comando/cierre' require_relative 'comando/inicio' require_relative 'core/conciliador' require_relative 'core/colores' # ─── Logger estructurado ───────────────────────────────────────────────── # El logger estΓ‘ implementado en adn/logger.rb ADNLogger = ADN::Logger require_relative 'comando/validador' require_relative 'comando/contexto' require_relative 'comando/generar' require_relative 'comando/salud' require_relative 'comando/ayuda' # ─── CLI Principal ────────────────────────────────────────────────────── class ADNCLI def initialize @logger = ADNLogger.new end def ejecutar if ARGV.empty? SubcomandoAyuda.new([], @logger).ejecutar exit 0 end subcomando = ARGV[0] # Saneamiento preventivo de pendientes (con cooldown de 5 minutos para performance) ejecutar_saneamiento_preventivo(subcomando) args = ARGV[1..-1] || [] case subcomando when 'validador' SubcomandoValidador.new(args, @logger).ejecutar when 'contexto' SubcomandoContexto.new(args, @logger).ejecutar when 'salud' SubcomandoSalud.new(args, @logger).ejecutar when 'generar' SubcomandoGenerar.new(args, @logger).ejecutar when 'conocimiento' ADN::SubcomandoConocimiento.new(args, @logger).ejecutar when 'db' ADN::SubcomandoDB.new(args, @logger).ejecutar when 'backup', 'backups' ADN::SubcomandoBackup.new(args, @logger).ejecutar when 'triggers' ADN::SubcomandoTriggers.new(args, @logger).ejecutar when 'cierre' ADN::SubcomandoCierre.new(args, @logger).ejecutar when 'inicio' ADN::SubcomandoInicio.new(args, @logger).ejecutar when 'ayuda', 'help', '-h', '--help' SubcomandoAyuda.new(args, @logger).ejecutar when 'evolucion' @logger.info("Subcomando 'evolucion' serΓ‘ implementado en Fase 2") puts "#{Color::YELLOW}⚠ El subcomando 'evolucion' serΓ‘ implementado en Fase 2#{Color::RESET}" puts " Ver plan: docs/plan/260307-1400_Plan_Mejoras_ADN.md" when 'triggers' ADN::SubcomandoTriggers.new(args, @logger).ejecutar when 'plan' ADN::SubcomandoPlan.new(args, @logger).ejecutar else @logger.error("Subcomando desconocido: #{subcomando}") puts "#{Color::RED}βœ— Subcomando desconocido: '#{subcomando}'#{Color::RESET}" puts "" SubcomandoAyuda.new([], @logger).ejecutar exit 1 end rescue Interrupt puts "\n#{Color::YELLOW}⚠ Interrumpido por el usuario#{Color::RESET}" @logger.info("EjecuciΓ³n interrumpida por usuario") exit 130 rescue => e @logger.error("Error inesperado: #{e.message}", { backtrace: e.backtrace }) puts "#{Color::RED}βœ— Error inesperado: #{e.message}#{Color::RESET}" puts "#{Color::DIM}#{e.backtrace.first}#{Color::RESET}" if ENV['ADN_DEBUG'] exit 1 end private def ejecutar_saneamiento_preventivo(subcomando) # No sanear si es ayuda o db (para evitar bucles o lentitud en info rΓ‘pida) return if ['ayuda', 'help', 'db', 'salud', '-h', '--help'].include?(subcomando) archivo_cooldown = "/tmp/adn_sanity_cooldown" cooldown_segundo = 300 # 5 minutos if File.exist?(archivo_cooldown) last_run = File.mtime(archivo_cooldown) return if (Time.now - last_run) < cooldown_segundo end # Ejecutar conciliaciΓ³n begin # Tocamos el archivo primero para evitar que otras instancias concurrentes lo lancen FileUtils.touch(archivo_cooldown) ADN::Conciliador.new(@logger).ejecutar(silencioso: true) rescue => e @logger.advertencia("No se pudo completar el saneamiento preventivo: #{e.message}") end end end # ─── Punto de entrada ─────────────────────────────────────────────────── if __FILE__ == $0 ADNCLI.new.ejecutar end