# frozen_string_literal: true require 'optparse' require 'json' require 'open3' require_relative '../core/colores' require_relative '../core/help_formatter' class SubcomandoMsp def initialize(args, logger) @args = args @logger = logger @opciones = {} @subcommand = nil end def ejecutar @subcommand = @args.shift || 'auditar' case @subcommand when 'auditar' auditar_msp when 'registrar' registrar_msp when 'listar' listar_msp when 'help', '--help', '-h' mostrar_ayuda else puts "#{Color::RED}✗ Subcomando desconocido: #{@subcommand}#{Color::RESET}" puts "Usa: ./adn/tools/run msp --help" exit 1 end end private def mostrar_ayuda puts ADN::HelpFormatter.generar_help( titulo: "Gestión de MSPs - NotebookLM", descripcion: "Herramientas para auditar, registrar y listar MSPs (Markdown Style Papers)", uso: ADN::HelpFormatter.format_usage("./adn/tools/run msp", " [opciones]"), opciones: [ { names: %w[auditar], desc: "Verificar estado de conexión MCP y NotebookLM" }, { names: %w[registrar], desc: "Registrar un MSP descargado en la base de conocimiento", default: "--archivo " }, { names: %w[listar], desc: "Listar MSPs disponibles en el sistema" } ], ejemplos: [ { cmd: "./adn/tools/run msp auditar", desc: "Verificar estado de MCP y NotebookLM" }, { cmd: "./adn/tools/run msp listar", desc: "Listar MSPs disponibles" }, { cmd: "./adn/tools/run msp registrar --archivo /ruta/msp.md", desc: "Registrar un MSP" } ] ) end def auditar_msp parser = OptionParser.new do |opts| opts.banner = "Uso: ./adn/tools/run msp auditar" opts.on("-h", "--help", "Mostrar ayuda") do puts opts exit end end parser.parse!(@args) @logger.info("Auditando estado de MSPs y conexión MCP") puts "#{Color::CYAN}🔍 Auditoría de MSPs - NotebookLM#{Color::RESET}" puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}" resultados = { mcp_config: verificar_mcp_config, ejecutable: verificar_ejecutable, autenticacion: verificar_autenticacion, msp_disponibles: verificar_msps_locales } puts "" puts "#{Color::CYAN}📋 Resultados:#{Color::RESET}" puts " #{Color::CYAN}MCPs configurados:#{Color::RESET} #{resultados[:mcp_config][:mcps].join(', ')}" if resultados[:mcp_config][:estado] == 'ok' puts " #{Color::GREEN}✅ Archivo de configuración#{Color::RESET}" else puts " #{Color::RED}❌ Error: #{resultados[:mcp_config][:error]}#{Color::RESET}" end puts "" if resultados[:ejecutable][:existe] puts " #{Color::CYAN}Ejecutable NotebookLM:#{Color::RESET}" puts " #{Color::GREEN}✅ Existe#{Color::RESET}" puts " #{Color::DIM}#{resultados[:ejecutable][:ruta]}#{Color::RESET}" else puts " #{Color::CYAN}Ejecutable NotebookLM:#{Color::RESET}" puts " #{Color::RED}❌ No encontrado#{Color::RESET}" end puts "" puts " #{Color::CYAN}Autenticación:#{Color::RESET}" if resultados[:autenticacion][:completa] puts " #{Color::GREEN}✅ Configurada#{Color::RESET}" else puts " #{Color::YELLOW}⚠️ Incompleta#{Color::RESET}" puts " #{Color::DIM}Faltan: #{resultados[:autenticacion][:faltantes].join(', ')}#{Color::RESET}" end puts "" puts " #{Color::CYAN}MSPs locales:#{Color::RESET}" puts " #{resultados[:msp_disponibles][:total]} archivos encontrados" puts "" puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}" puts "#{Color::CYAN}ℹ#{Color::RESET} Los MSPs se generan desde la UI web de NotebookLM" puts "#{Color::CYAN}ℹ#{Color::RESET} MCP NO incluye herramienta de generación MSP" @logger.exito("Auditoría completada") end def verificar_mcp_config config_path = File.expand_path('~/.gemini/antigravity/mcp_config.json') unless File.exist?(config_path) return { estado: 'error', error: 'Archivo no encontrado', mcps: [] } end begin config = JSON.parse(File.read(config_path)) mcps = config['mcpServers']&.keys || [] { estado: 'ok', mcps: mcps } rescue JSON::ParserError => e { estado: 'error', error: e.message, mcps: [] } end end def verificar_ejecutable ruta = File.expand_path('~/.local/share/notebooklm-mcp/bin/notebooklm-mcp') { existe: File.exist?(ruta), ruta: ruta } end def verificar_autenticacion auth_path = File.expand_path('~/.notebooklm-mcp/auth.json') unless File.exist?(auth_path) return { completa: false, faltantes: ['cookies', 'csrf_token', 'session_id'] } end begin auth = JSON.parse(File.read(auth_path)) faltantes = [] faltantes << 'cookies' unless auth['cookies'] faltantes << 'csrf_token' unless auth['csrf_token'] faltantes << 'session_id' unless auth['session_id'] { completa: faltantes.empty?, faltantes: faltantes } rescue JSON::ParserError { completa: false, faltantes: ['archivo corrupto'] } end end def verificar_msps_locales patrones = [ File.join(ADN::PROJECT_ROOT, '**/*MSP*.md'), File.join(ADN::PROJECT_ROOT, 'docs/mcps/*.md') ] archivos = [] patrones.each do |patron| archivos |= Dir.glob(patron).select { |f| File.file?(f) } end { total: archivos.count, archivos: archivos } end def registrar_msp parser = OptionParser.new do |opts| opts.banner = "Uso: ./adn/tools/run msp registrar --archivo " opts.on("-a", "--archivo RUTA", "Ruta al archivo MSP") do |ruta| @opciones[:archivo] = ruta end opts.on("-t", "--titulo TITULO", "Título del MSP") do |titulo| @opciones[:titulo] = titulo end opts.on("-h", "--help", "Mostrar ayuda") do puts opts exit end end parser.parse!(@args) unless @opciones[:archivo] puts "#{Color::RED}✗ Error: Se requiere --archivo #{Color::RESET}" puts "Usa: ./adn/tools/run msp registrar --help" exit 1 end @logger.info("Registrando MSP: #{@opciones[:archivo]}") unless File.exist?(@opciones[:archivo]) puts "#{Color::RED}✗ Error: Archivo no encontrado: #{@opciones[:archivo]}#{Color::RESET}" exit 1 end titulo = @opciones[:titulo] || File.basename(@opciones[:archivo], '.md') contenido = File.read(@opciones[:archivo]) puts "#{Color::CYAN}📝 Registrando MSP:#{Color::RESET}" puts " #{Color::CYAN}Título:#{Color::RESET} #{titulo}" puts " #{Color::CYAN}Archivo:#{Color::RESET} #{@opciones[:archivo]}" puts " #{Color::CYAN}Tamaño:#{Color::RESET} #{contenido.lines.count} líneas" puts "" puts "#{Color::YELLOW}⚠️ Registro en base de conocimiento no implementado#{Color::RESET}" puts "#{Color::DIM}Para registrar, usar: ./adn/tools/run conocimiento asimilar #{Color::RESET}" @logger.exito("MSP registrado: #{titulo}") end def listar_msp parser = OptionParser.new do |opts| opts.banner = "Uso: ./adn/tools/run msp listar" opts.on("-h", "--help", "Mostrar ayuda") do puts opts exit end end parser.parse!(@args) @logger.info("Listando MSPs disponibles") patrones = [ File.join(ADN::PROJECT_ROOT, '**/*MSP*.md'), File.join(ADN::PROJECT_ROOT, 'docs/mcps/*.md') ] archivos = [] patrones.each do |patron| archivos |= Dir.glob(patron).select { |f| File.file?(f) } end puts "#{Color::CYAN}📚 MSPs disponibles#{Color::RESET}" puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}" if archivos.empty? puts "#{Color::YELLOW}⚠️ No se encontraron MSPs#{Color::RESET}" puts "" puts "#{Color::CYAN}ℹ#{Color::RESET} Los MSPs se generan desde:" puts " 1. Ir a https://notebooklm.google.com" puts " 2. Crear/abrir un cuaderno con fuentes" puts " 3. Click en 'More' → 'Generate MSP'" puts " 4. Descargar y guardar en el proyecto" else archivos.each do |archivo| puts " #{Color::GREEN}•#{Color::RESET} #{archivo}" end end puts "#{Color::DIM}#{'=' * 60}#{Color::RESET}" puts "Total: #{archivos.count} MSP(s)" @logger.exito("Listado completado: #{archivos.count} MSPs") end end