otto-cli 0.1.0

Task runner with retries, timeouts, history, and notifications
Documentation
# Otto

[![check](https://github.com/mcmanussliam/otto/actions/workflows/check.yml/badge.svg)](https://github.com/mcmanussliam/otto/actions/workflows/check.yml)
[![release](https://img.shields.io/github/v/release/mcmanussliam/otto)](https://github.com/mcmanussliam/otto/releases)
[![crates.io](https://img.shields.io/crates/v/otto-cli.svg)](https://crates.io/crates/otto-cli)
[![license](https://img.shields.io/github/license/mcmanussliam/otto)](LICENSE)

Otto is a simple task runner. Think `make`, but with built-in retries, timeouts,
run history, and notifications.

```bash
% otto run -- echo "hello"

ok run "inline" finished in 3ms

% otto history

inline ok success
  source: inline
  exit: 0
  started (UTC): 2026-02-22 18:10:09
  duration: 3ms
```

## Install

Install from crates.io:

```bash
cargo install otto-cli --locked
otto version
```

## 60-second quick start

```bash
otto init
otto tasks
otto run -- echo "works"
```

Then add your own task to `otto.yml`:

```yaml
version: 1

defaults:
  timeout: "2m"
  retries: 0
  retry_backoff: "1s"
  notify_on: failure

notifications:
  desktop: true

tasks:
  test:
    description: run unit tests
    exec: ["cargo", "test"]
```

Run it:

```bash
otto run test
```

## How tasks work

Use exactly one command mode per task:

- `exec`: direct argv execution (no shell parsing)
- `run`: shell command (`/bin/sh -c` on macOS/Linux, `cmd /C` on Windows)
- `tasks`: compose other tasks by name

Task composition example:

```yaml
tasks:
  lint:
    exec: ["cargo", "fmt", "--all", "--check"]

  clippy:
    exec: ["cargo", "clippy", "--all-targets", "--all-features", "--", "-D", "warnings"]

  build:
    exec: ["cargo", "build", "--release"]

  ci:
    tasks: ["lint", "build", "clippy"]
    parallel: false # set true to run child tasks in parallel
```

Shared defaults live in `defaults`, and each task can override:

- `timeout`
- `retries` (0..10)
- `retry_backoff` (uses exponential backoff between attempts)
- `notify_on` (`never`, `failure`, `always`)

## Dotenv and env expansion

`otto run` auto-loads `.env` if present.

- Disable it: `--no-dotenv`
- Use a different file: `--env-file .env.staging`

Variables from process env + dotenv + task `env` are expanded in:

- `run`
- `exec` args
- `dir`
- `env` values

Unknown variables are preserved as `${NAME}`.

## Notifications

Supported channels:

- desktop (`osascript` on macOS, `notify-send` on Linux)
- webhook (`POST` JSON to `notifications.webhook_url`)

`notify_on` controls when notifications fire: `never`, `failure`, `always`.

## History and automation

Every run gets recorded in `.otto/history.jsonl`.

For scripts, use:

- `otto run --json`
- `otto tasks --json`
- `otto history --json`

In JSON mode, command output is suppressed so stdout is valid JSON only.

## Shell completion

```bash
otto completion bash
otto completion zsh
otto completion fish
otto completion powershell
```