Files
dtic-DIIAA/tools/adn/comando/conocimiento.rb
T

178 lines
6.1 KiB
Ruby

# frozen_string_literal: true
# tools/adn/comando/conocimiento.rb — Subcomando para gestionar la Base de Conocimiento del ADN
# ==============================================================================
# Inyecta nuevos documentos y los referencia automáticamente en el 00_indice.md
# cumpliendo con el principio de Armonía Integral.
require 'optparse'
require 'fileutils'
require 'date'
module ADN
class SubcomandoConocimiento
def initialize(args, logger)
@args = args
@logger = logger
@indice_path = File.join(PROJECT_ROOT, 'adn', '00_indice.md')
@conocimiento_dir = File.join(PROJECT_ROOT, 'adn', 'conocimiento')
end
def ejecutar
if @args.empty? || @args[0] == 'help'
mostrar_ayuda
return
end
subcomando = @args[0]
sub_args = @args[1..-1] || []
case subcomando
when 'asimilar'
comando_asimilar(sub_args)
else
@logger.error("Subcomando desconocido: conocimiento #{subcomando}")
puts "#{Color::RED}✗ Subcomando desconocido: '#{subcomando}'#{Color::RESET}"
mostrar_ayuda
exit 1
end
rescue Interrupt
puts "\n#{Color::YELLOW}⚠ Operación interrumpida#{Color::RESET}"
exit 130
rescue StandardError => e
@logger.error("Error asimilando conocimiento: #{e.message}")
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
puts "#{Color::DIM}#{e.backtrace.first}#{Color::RESET}" if ENV['ADN_DEBUG']
exit 1
end
private
def comando_asimilar(args)
opts = { forzar: false }
parser = OptionParser.new do |opt|
opt.banner = "Uso: adn conocimiento asimilar <archivo.md> [OPCIONES]"
opt.on("-f", "--force", "Forzar sobrescritura si el documento ya está asimilado") do
opts[:forzar] = true
end
opt.on("-h", "--help", "Mostrar ayuda") do
puts opt
exit
end
end
begin
parser.parse!(args)
rescue OptionParser::InvalidOption => e
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
puts parser
exit 1
end
archivo_origen = args[0]
if archivo_origen.nil? || archivo_origen.strip.empty?
puts "#{Color::RED}✗ Se requiere la ruta de un archivo Markdown.#{Color::RESET}"
puts parser
exit 1
end
unless File.exist?(archivo_origen)
puts "#{Color::RED}✗ El archivo origen no existe: #{archivo_origen}#{Color::RESET}"
exit 1
end
puts "#{Color::CYAN}🧠 Asimilando conocimiento desde #{archivo_origen}...#{Color::RESET}"
# Preparar el directorio de conocimiento
FileUtils.mkdir_p(@conocimiento_dir) unless Dir.exist?(@conocimiento_dir)
nombre_base = File.basename(archivo_origen)
archivo_destino = File.join(@conocimiento_dir, nombre_base)
if File.exist?(archivo_destino) && !opts[:forzar]
puts "#{Color::YELLOW}⚠ El documento ya existe en la base de ADN. Usa --force para sobrescribir.#{Color::RESET}"
exit 1
end
# Copiar el archivo
FileUtils.cp(archivo_origen, archivo_destino)
puts " #{Color::DIM}Documento integrado físicamente en: adn/conocimiento/#{nombre_base}#{Color::RESET}"
# Extraer metadatos
contenido = File.read(archivo_destino)
titulo = contenido.match(/^#\s+(.+)$/)&.captures&.first || nombre_base.sub('.md', '').capitalize
# Buscar el primer párrafo que no sea un título ni un bloque especial
resumen = "Asimilación automatizada de módulo."
parrafos = contenido.split("\n\n").reject { |p| p.start_with?('#', '>', '<', '---', '!') || p.strip.empty? }
if parrafos.any?
resumen_limpio = parrafos.first.gsub("\n", " ").strip
resumen = resumen_limpio[0..147] + "..." if resumen_limpio.length > 150
resumen = resumen_limpio if resumen_limpio.length <= 150
end
# Actualizar 00_indice.md
actualizar_indice(nombre_base, titulo, resumen)
puts "#{Color::GREEN}✅ Conocimiento asimilado correctamente y propagado a ADN/00_indice.md#{Color::RESET}"
@logger.info("Conocimiento asimilado: #{nombre_base}")
end
def actualizar_indice(nombre_archivo, titulo, resumen)
contenido_indice = File.read(@indice_path)
fecha_hoy = Date.today.strftime("%d/%m/%Y")
ruta_relativa = "conocimiento/#{nombre_archivo}"
fila_markdown = "| [**#{titulo}**](#{ruta_relativa}) | #{fecha_hoy} | #{resumen} |"
# Comprobar si ya existe la sección de conocimiento
seccion_conocimiento = "## 📚 Base de Conocimiento"
if contenido_indice.include?(seccion_conocimiento)
# Revisión de duplicados
return if contenido_indice.include?(ruta_relativa) && !contenido_indice.include?("Sobrescribir")
# Encontrar el final de la tabla e insertar la fila (reemplazando si hace falta actualizar)
lineas = contenido_indice.lines
idx_seccion = lineas.index { |l| l.include?(seccion_conocimiento) }
# Insertar después de las cabeceras de la tabla
idx_insercion = idx_seccion + 4
lineas.insert(idx_insercion, "#{fila_markdown}\n")
File.write(@indice_path, lineas.join)
else
# Inyectar toda la sección al final del archivo
bloque_nuevo = <<~MD
---
#{seccion_conocimiento}
| Documento | Fecha de Asimilación | Descripción |
| :--- | :--- | :--- |
#{fila_markdown}
MD
File.open(@indice_path, 'a') do |f|
f.puts bloque_nuevo
end
end
end
def mostrar_ayuda
puts "#{Color::CYAN}🧠 Herramienta ADN - Gestión de Base de Conocimiento#{Color::RESET}"
puts "Permite inyectar, organizar e indexar nueva documentación en el motor autónomo."
puts ""
puts "#{Color::BOLD}SUBCOMANDOS:#{Color::RESET}"
puts " #{Color::CYAN}asimilar <archivo.md>#{Color::RESET} Ingiere un documento Markdown a la base cognitiva del ADN."
puts ""
puts "#{Color::BOLD}EJEMPLOS:#{Color::RESET}"
puts " ./tools/adn/run conocimiento asimilar docs/configuracion_bd.md"
end
end
end