README.md

# Argos ⚡

**Command Execution & Parallel Task Orchestration Library for Elixir**

Argos is a powerful Elixir library for executing system commands with structured results, orchestrating parallel tasks, and providing enriched logging with formatted output. It's designed to be reliable, efficient, and easy to use.

## 🌟 Features

- **Command Execution**: Execute system commands with structured results and comprehensive error handling
- **Parallel Task Orchestration**: Run multiple workers concurrently with sequential tasks per worker
- **Circuit Breaker**: Built-in circuit breaker pattern to prevent cascading failures
- **ASDF Integration**: Automatic version management using `.tool-versions` files
- **Rich Logging**: Structured logging with Aurora integration for beautiful terminal output
- **Process Management**: Kill processes by name with validation and safety checks
- **CLI Tool**: Standalone executable for parallel command execution
- **Event Subscription**: Subscribe to individual worker events or aggregated monitor state

## 📦 Installation

Add Argos to your `mix.exs`:

```elixir
def deps do
  [
    {:argos, "~> 2.0.0"},
    {:aurora, "~> 2.0.0"}  # Required dependency
  ]
end
```

Then run:

```bash
mix deps.get
```

## 🚀 Quick Start

### Basic Command Execution

```elixir
# Execute a command and get structured result
result = Argos.Command.exec("ls -la")

if result.success? do
  IO.puts("Success: #{result.output}")
else
  IO.puts("Error: #{result.error}")
end

# Access result properties
IO.puts("Exit code: #{result.exit_code}")
IO.puts("Duration: #{result.duration}ms")
```

### Parallel Task Orchestration

```elixir
# Start the parallel system
Argos.Parallel.start_system()

# Create worker specifications
specs = [
  Argos.Parallel.create_worker_spec(:worker1, [
    fn ->
      Process.sleep(1000)
      {:ok, :task1_complete}
    end,
    fn ->
      Process.sleep(500)
      {:ok, :task2_complete}
    end
  ])
]

# Start workers
Argos.Parallel.start_workers(specs)

# Subscribe to events
Argos.Parallel.subscribe()

# Receive events
receive do
  {:parallel_event, _leader, event} ->
    IO.inspect(event, label: "Worker event")
after
  5000 -> :timeout
end
```

## 📚 API Reference

### Command Module

Execute system commands with various modes and options.

#### `exec/2`

Execute a command with structured result.

```elixir
result = Argos.Command.exec("ls -la")
result = Argos.Command.exec("risky_command", circuit_breaker: true)
```

**Options:**

- `circuit_breaker: true` - Enable circuit breaker for this command
- `halt: false` - Don't halt on error (default: true)
- `env: [{"KEY", "value"}]` - Environment variables

#### `exec_raw/2`

Execute command without additional logging.

```elixir
{output, exit_code} = Argos.Command.exec_raw("echo hello")
```

#### `exec_silent/2`

Execute silently, returning only exit code.

```elixir
exit_code = Argos.Command.exec_silent("ls -la")
```

#### `exec_interactive/2`

Execute command interactively (for commands requiring input).

```elixir
result = Argos.Command.exec_interactive("vim file.txt")
```

#### `exec_sudo/2`

Execute command with sudo.

```elixir
result = Argos.Command.exec_sudo("apt update")
```

#### `asdf_exec/3`

Execute command using ASDF with project-specific versions.

```elixir
# Execute using versions from .tool-versions in project directory
result = Argos.Command.asdf_exec(
  "mix test",
  "/path/to/project",
  env: [{"MIX_ENV", "test"}]
)
```

**Features:**

- Automatically reads `.tool-versions` from project directory
- Uses `asdf exec` to run command with correct versions
- Configures PATH to include ASDF shims
- Falls back to normal execution if `.tool-versions` doesn't exist

#### `kill_process/1`

Kill a process by name.

```elixir
result = Argos.Command.kill_process("process_name")
```

#### `kill_processes_by_name/1`

Kill multiple processes by name.

```elixir
results = Argos.Command.kill_processes_by_name(["proc1", "proc2"])
```

### Parallel Module

Orchestrate parallel task execution with workers.

#### `start_system/1`

Start the parallel execution system.

```elixir
{:ok, pid} = Argos.Parallel.start_system()
```

#### `stop_system/0`

Stop the parallel execution system and clean up resources.

```elixir
:ok = Argos.Parallel.stop_system()
```

#### `create_worker_spec/3`

Create a worker specification.

```elixir
spec = Argos.Parallel.create_worker_spec(
  :worker_id,
  [fn -> :task1 end, fn -> :task2 end],
  log: true  # Enable logging for this worker
)
```

**Options:**

- `log: true` - Enable individual log file for this worker
- `timeout: 30_000` - Worker timeout in milliseconds

#### `start_workers/2`

Start multiple workers with given specifications.

```elixir
specs = [spec1, spec2, spec3]
results = Argos.Parallel.start_workers(specs)
```

#### `stop_worker/2`

Stop a specific worker by ID.

```elixir
:ok = Argos.Parallel.stop_worker(:worker_id)
```

#### `subscribe/1`

Subscribe to individual worker events from the Leader.

```elixir
:ok = Argos.Parallel.subscribe()

# Receive events
receive do
  {:parallel_event, _leader, %{type: :result, worker_id: id, data: data}} ->
    IO.puts("Worker #{id} completed task: #{inspect(data)}")
  {:parallel_event, _leader, %{type: :progress, worker_id: id, data: data}} ->
    IO.puts("Worker #{id} progress: #{data.percent}%")
  {:parallel_event, _leader, %{type: :finished, worker_id: id}} ->
    IO.puts("Worker #{id} finished")
  {:parallel_event, _leader, %{type: :error, worker_id: id, data: data}} ->
    IO.puts("Worker #{id} error: #{data.reason}")
end
```

#### `subscribe_monitor/1`

Subscribe to aggregated monitor state updates.

```elixir
:ok = Argos.Parallel.subscribe_monitor()

# Receive aggregated state
receive do
  {:parallel_monitor_update, monitor_state} ->
    IO.inspect(monitor_state.workers)
    IO.inspect(monitor_state.stats)
end
```

#### `get_monitor_state/1`

Get current aggregated state from the monitor.

```elixir
state = Argos.Parallel.get_monitor_state()
IO.inspect(state.workers)
IO.inspect(state.stats)
```

#### `get_stats/1`

Get statistics about all workers.

```elixir
stats = Argos.Parallel.get_stats()
# %{
#   progress_percentage: 75.5,
#   total_workers: 4,
#   status_counts: %{running: 2, finished: 2},
#   completed_tasks: 10,
#   total_tasks: 14
# }
```

#### `get_worker_state/2`

Get state of a specific worker.

```elixir
worker_state = Argos.Parallel.get_worker_state(:worker_id)
```

### Validation Module

Comprehensive validation functions for security and error prevention.

#### `validate_command/1`

Validate a command before execution.

```elixir
:ok = Argos.Validation.validate_command("ls -la")
{:error, reason} = Argos.Validation.validate_command("")
```

#### `validate_process_name/1`

Validate a process name.

```elixir
:ok = Argos.Validation.validate_process_name("my_process")
{:error, reason} = Argos.Validation.validate_process_name("")
```

#### `validate_worker_spec/1`

Validate a worker specification.

```elixir
spec = %{id: :worker1, tasks: [fn -> :ok end]}
:ok = Argos.Validation.validate_worker_spec(spec)
```

#### `escape_process_name/1`

Escape process name for safe shell usage.

```elixir
safe_name = Argos.Validation.escape_process_name("process'name")
```

### Circuit Breaker

Prevent cascading failures with circuit breaker pattern.

```elixir
# Circuit breaker is automatically used when circuit_breaker: true option is passed
result = Argos.Command.exec("risky_command", circuit_breaker: true)

# Manual circuit breaker usage
{:ok, pid} = Argos.Command.CircuitBreaker.start_link([])
result = Argos.Command.CircuitBreaker.call(pid, fn -> risky_operation() end)
```

**States:**

- `:closed` - Normal operation, commands execute
- `:open` - Circuit is open, commands are rejected
- `:half_open` - Testing if service recovered

### Logger Module

Structured logging with Aurora integration.

```elixir
Argos.log(:info, "Informative message")
Argos.log(:error, "Operation failed", module: __MODULE__, line: 123)
Argos.log(:warning, "Important warning")
```

## 🖥️ CLI Usage

### Building the Executable

```bash
mix escript.build
```

This generates the `./argos` executable.

### Basic Usage

```bash
# Execute a single command
./argos --command="echo 'Hello World'"

# Execute multiple commands in parallel
./argos --command="echo 'First'" --command="echo 'Second'"

# Short alias
./argos -c "ls -la"
```

### Advanced Usage

```bash
# Commands with ANSI codes (preserved)
./argos --command="echo \"$(aurora --text='Hello' --color=primary)\""

# Use shell aliases (loads .zshrc/.bashrc by default)
./argos --command="my_alias arguments"

# Don't load shell configuration
./argos --no-config --command="clean_command"

# Use ASDF versions from project's .tool-versions
./argos --asdf-local --command="cd /path/to/project && mix test"
```

### CLI Options

- `-c, --command="COMMAND"` - Command to execute (can be specified multiple times)
- `--load-config` - Explicitly load shell configuration file
- `--no-config` - Don't load shell configuration file
- `--asdf-local` - Use ASDF versions from project's `.tool-versions` file
- `-h, --help` - Show help
- `-e, --examples` - Show usage examples
- `-v, --version` - Show version

### CLI Features

- ✅ Commands execute in parallel using Elixir Tasks
- ✅ Output is shown in real-time preserving ANSI codes
- ✅ Shell configuration (`.zshrc`/`.bashrc`) is loaded by default for aliases
- ✅ Exit code is 0 if all commands succeed, 1 if any fails
- ✅ Help is shown automatically if no command is specified

## 📊 CommandResult Structure

```elixir
%Argos.CommandResult{
  command: "ls -la",           # Executed command
  args: [],                     # Arguments
  output: "total 48\n...",     # Command output
  exit_code: 0,                # Exit code
  success?: true,               # Execution success
  error: nil,                   # Error if occurred
  duration: 15                  # Duration in milliseconds
}
```

## ⚙️ Configuration

### Circuit Breaker

Configure circuit breaker in `config/config.exs`:

```elixir
config :argos, :circuit_breaker,
  threshold: 5,        # Number of failures before opening circuit
  timeout: 60_000,     # Time in ms before attempting half_open
  enabled: true        # Enable/disable circuit breaker
```

### Logger

Configure a custom logger:

```elixir
config :argos, :logger, MyCustomLogger

# Logger must implement Argos.Logger.Behaviour
defmodule MyCustomLogger do
  @behaviour Argos.Logger.Behaviour

  @impl Argos.Logger.Behaviour
  def log(level, message, metadata) do
    # Your implementation
  end
end
```

### Shell

Configure default shell:

```elixir
config :argos, :shell, "/bin/zsh"
config :argos, :shell_timeout, 30_000  # Timeout in ms
```

## 🔧 Development

### Running Tests

```bash
mix test
```

### Code Quality

```bash
mix quality  # Runs credo, dialyzer, and format check
```

### Building Documentation

```bash
mix docs
```

### Building Executable

```bash
mix escript.build
```

## 📐 Architecture

Argos follows a modular architecture:

- **Command Module**: System command execution with structured results
- **Parallel Module**: Worker orchestration with Leader, Monitor, and Supervisor
- **Validation Module**: Input validation and security checks
- **Circuit Breaker**: Failure prevention pattern
- **Logger Module**: Structured logging with Aurora integration
- **CLI Modules**: Parser, Executor, Formatter for command-line interface

### Parallel System Components

- **Leader**: Coordinates worker lifecycle and events
- **Monitor**: Aggregates state from all workers
- **Supervisor**: Supervises all parallel system processes
- **Worker**: Executes tasks sequentially within a worker
- **Logger**: Individual log files per worker (optional)

## 🤝 Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Ensure all tests pass (`mix test`)
5. Run code quality checks (`mix quality`)
6. Submit a pull request

## 📄 License

Apache 2.0 - See [LICENSE](LICENSE) for details.

## 🙏 Acknowledgments

Argos is part of the Ypsilon project ecosystem, designed to provide powerful, elegant tools for Elixir developers.

---

**Made with ⚡ for reliable command execution and parallel task orchestration**