Refactor(ADN): Migración de tools/adn a adn/tools en busca de la Armonía Integral del DIIAA
This commit is contained in:
Executable
+165
@@ -0,0 +1,165 @@
|
||||
#!/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: ./adn/tools/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 adn/tools/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 'cli/db'
|
||||
require_relative 'cli/plan'
|
||||
require_relative 'cli/backup'
|
||||
require_relative 'cli/triggers'
|
||||
require_relative 'cli/conocimiento'
|
||||
require_relative 'cli/cierre'
|
||||
require_relative 'cli/inicio'
|
||||
require_relative 'core/conciliador'
|
||||
|
||||
require_relative 'core/colores'
|
||||
|
||||
# ─── Logger estructurado ─────────────────────────────────────────────────
|
||||
# El logger está implementado en adn/logger.rb
|
||||
ADNLogger = ADN::Logger
|
||||
|
||||
require_relative 'cli/validador'
|
||||
require_relative 'cli/contexto'
|
||||
require_relative 'cli/generar'
|
||||
require_relative 'cli/salud'
|
||||
require_relative 'cli/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
|
||||
Reference in New Issue
Block a user