178 lines
6.1 KiB
Ruby
178 lines
6.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# tools/adn/cli/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
|