otto-cli 0.1.0

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

Otto

check release crates.io license

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

% 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:

cargo install otto-cli --locked
otto version

60-second quick start

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

Then add your own task to otto.yml:

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:

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:

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

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