defmodule Argos.CLI do @moduledoc """ Command-line interface para Argos. Permite ejecutar múltiples comandos del sistema en paralelo desde la línea de comandos. Los comandos se ejecutan de forma concurrente y su salida se muestra preservando los códigos ANSI. ## Uso # Ejecutar un único comando argos --command="echo 'Hello World'" # Ejecutar múltiples comandos en paralelo argos --command="echo 'First'" --command="echo 'Second'" # Comandos con códigos ANSI (como aurora) argos --command="echo \"$(aurora --text='joroña' --hex='bada55')\"" ## Opciones - `--command="..."` o `-c "..."`: Comando a ejecutar (puede especificarse múltiples veces) - `--load-config`: Cargar explícitamente el archivo de configuración del shell - `--no-config`: No cargar el archivo de configuración del shell - `--no-login`: (Deprecated) Equivalente a `--no-config` - `--help` o `-h`: Muestra la ayuda - `--version` o `-v`: Muestra la versión ## Notas - Los comandos se ejecutan en paralelo usando Tasks - La salida de cada comando se muestra preservando los códigos ANSI - El código de salida será 0 si todos los comandos tienen éxito, o 1 si alguno falla - Por defecto se carga el archivo de configuración del shell (`.zshrc`, `.bashrc`, etc.) para permitir usar aliases y funciones personalizadas - El archivo de configuración se detecta automáticamente según el shell usado - Usa `--no-config` si no quieres cargar la configuración del shell ## Ejemplos con aliases # Usar un alias definido en .zshrc argos --command="mi_alias argumentos" # Ejecutar múltiples comandos con aliases argos --command="alias1" --command="alias2" # Si no quieres cargar .zshrc argos --no-login --command="comando_sin_config" """ alias Argos.CLI.{Executor, Formatter, Parser} @doc """ Parsea los argumentos de línea de comandos. """ @spec parse_args([String.t()]) :: {[String.t()], [String.t()], keyword()} defdelegate parse_args(argv), to: Parser @doc """ Recoge todos los comandos de las opciones (puede haber múltiples --command). """ @spec collect_commands(keyword()) :: [String.t()] defdelegate collect_commands(opts), to: Parser @doc """ Ejecuta un comando individual. """ @spec execute_command(String.t(), keyword()) :: Argos.CommandResult.t() defdelegate execute_command(command, opts \\ []), to: Executor @doc """ Punto de entrada principal del CLI. """ @spec main([String.t()]) :: no_return() def main(argv) do _ = Code.ensure_loaded(Aurora.Color) load_aurora_colors() argv |> Parser.parse_args() |> execute() end @dialyzer {:no_return, execute: 1} defp execute({commands, _remaining, opts, command_for_help}) do cond do Keyword.get(opts, :help, false) and not is_nil(command_for_help) -> Formatter.show_command_help(command_for_help) System.halt(0) Keyword.get(opts, :help, false) -> Formatter.show_help() System.halt(0) Keyword.get(opts, :examples, false) -> Formatter.show_examples() System.halt(0) Keyword.get(opts, :version, false) -> Formatter.show_version() System.halt(0) Enum.empty?(commands) -> Formatter.show_help() System.halt(0) true -> Executor.run_commands_parallel(commands, opts) end end defp load_aurora_colors do case Application.get_env(:aurora, :colors) do nil -> default_colors = get_default_aurora_colors() Application.put_env(:aurora, :colors, default_colors) :ok [] -> default_colors = get_default_aurora_colors() Application.put_env(:aurora, :colors, default_colors) :ok _ -> :ok end end defp get_default_aurora_colors do [ colors: %{ no_color: %{ name: :no_color, hex: "#F8F8F2", rgb: {248, 248, 242}, argb: {255, 248, 248, 242}, hsv: {60.0, 0.024, 0.972}, hsl: {60.0, 0.286, 0.961}, cmyk: {0.0, 0.0, 0.024, 0.028}, inverted: false }, primary: %{ name: :primary, hex: "#A1E7FA", rgb: {161, 231, 250}, argb: {255, 161, 231, 250}, hsv: {192.0, 0.356, 0.980}, hsl: {192.0, 0.892, 0.806}, cmyk: {0.356, 0.076, 0.0, 0.020}, inverted: false }, secondary: %{ name: :secondary, hex: "#3AABA3", rgb: {58, 171, 163}, argb: {255, 58, 171, 163}, hsv: {176.0, 0.661, 0.671}, hsl: {176.0, 0.493, 0.449}, cmyk: {0.661, 0.0, 0.047, 0.329}, inverted: false }, ternary: %{ name: :ternary, hex: "#FF8000", rgb: {255, 128, 0}, argb: {255, 255, 128, 0}, hsv: {30.0, 1.0, 1.0}, hsl: {30.0, 1.0, 0.5}, cmyk: {0.0, 0.498, 1.0, 0.0}, inverted: false }, success: %{ name: :success, hex: "#97C53C", rgb: {151, 197, 60}, argb: {255, 151, 197, 60}, hsv: {80.0, 0.695, 0.773}, hsl: {80.0, 0.534, 0.504}, cmyk: {0.234, 0.0, 0.695, 0.227}, inverted: false }, warning: %{ name: :warning, hex: "#FDD808", rgb: {253, 216, 8}, argb: {255, 253, 216, 8}, hsv: {51.0, 0.968, 0.992}, hsl: {51.0, 0.984, 0.509}, cmyk: {0.0, 0.146, 0.968, 0.008}, inverted: false }, error: %{ name: :error, hex: "#FF5B5B", rgb: {255, 91, 91}, argb: {255, 255, 91, 91}, hsv: {0.0, 0.643, 1.0}, hsl: {0.0, 1.0, 0.678}, cmyk: {0.0, 0.643, 0.643, 0.0}, inverted: false }, info: %{ name: :info, hex: "#00FFFF", rgb: {0, 255, 255}, argb: {255, 0, 255, 255}, hsv: {180.0, 1.0, 1.0}, hsl: {180.0, 1.0, 0.5}, cmyk: {1.0, 0.0, 0.0, 0.0}, inverted: false }, happy: %{ name: :happy, hex: "#EE80C3", rgb: {238, 128, 195}, argb: {255, 238, 128, 195}, hsv: {326.0, 0.462, 0.933}, hsl: {326.0, 0.767, 0.718}, cmyk: {0.0, 0.462, 0.181, 0.067}, inverted: false }, menu: %{ name: :menu, hex: "#ABCDF1", rgb: {171, 205, 241}, argb: {255, 171, 205, 241}, hsv: {210.0, 0.290, 0.945}, hsl: {210.0, 0.714, 0.808}, cmyk: {0.290, 0.149, 0.0, 0.055}, inverted: false } }, gradients: %{} ] end end