166 lines
6.3 KiB
Ruby
Executable File
166 lines
6.3 KiB
Ruby
Executable File
#!/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 <subcomando> [opciones] [argumentos]
|
|
#
|
|
# Subcomandos disponibles:
|
|
# validador [--watch] [--fix] Validar cumplimiento del ADN en bitácoras
|
|
# contexto <nodo> [--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 <nombre> Generar ficha de nodo
|
|
# generar proyecto <código> Generar manifiesto de proyecto
|
|
# conocimiento asimilar <archivo> Asimilar documentación al ADN
|
|
# evolucion <propuesta.md> 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
|