Skip to content

v0.2.0

Choose a tag to compare

@joshrotenberg joshrotenberg released this 06 Jun 01:18
· 169 commits to main since this release
45c85f4

Initial public release. The CLI surface, exit codes, config schema,
and --json envelope are intended to be stable across 0.2.x;
breaking changes that landed before this release (e.g. flag
renames, schema renames) are listed under Removed.

Added

Surface

  • Prompt sources: positional argument, -p, --prompt TEXT
    (explicit prompt string; mutually exclusive with the positional
    argument -- the escape hatch when the positional form would be
    ambiguous against an optional-value flag like -c or -w, e.g.
    roba -c -p "follow up"), stdin (- or piped), -f FILE,
    -e (compose in $EDITOR / $VISUAL), --editor-history N
    (last N responses included as reference block in the editor).
  • Composition: --prepend FILE and --append FILE
    (repeatable), --attach GLOB (repeatable, embeds files with
    File: PATH framing), --git-diff, --git-log [N],
    --git-status, --var K=V template substitution.
  • Output: --json (versioned envelope on stdout), --quiet
    (answer only, no metadata), --code [LANG] (extract fenced
    blocks), -o, --out PATH (write to file AND stdout, extension
    drives format), --stream (TTY-only progress indicator),
    --show-thinking (extended-thinking blocks live on stderr,
    requires --stream), --trace PATH (writes spawned claude
    streaming events to PATH as JSONL; observability handle for
    in-flight runs), --echo (print resolved prompt), --plain
    (master kill-switch: no markdown render, no color, no spinner).
  • Dispatch session id on stderr: when the streaming pipeline
    is active (--stream or --trace), roba prints [roba] session: <id> to stderr as soon as the spawned session id
    becomes known (first event). Gives orchestrators a stable handle
    to the session JSONL without requiring --trace to be set
    upfront. Suppressed by --quiet.
  • Sessions: -c [ID] (bare = continue most recent in cwd;
    -c ID or -c=ID = resume specific session by id), --fork
    (branch a resumed session; requires an explicit id, -c ID --fork), --pick (interactive fuzzy chooser), --fresh (force
    new session, cancels env/profile continue), --agent NAME (pin a
    specific Claude Code subagent for the run), -w, --worktree [NAME] (run in a fresh git worktree; named or auto-generated).
  • Permissions: --readonly (Read/Glob/Grep only -- the
    default), --writable (adds Edit/Write), --allow-tool TOOL
    (repeatable), --deny-tool TOOL (repeatable), --full-auto
    (bypass everything), --permission-mode MODE (pass a specific
    claude permission mode -- plan, dontAsk, auto,
    acceptEdits, default, bypassPermissions -- orthogonal to
    the allowlist shortcuts; the shortcuts set the allowlist, this
    sets the mode), --show-permissions (preview the
    effective allow/deny set with per-entry provenance and exit 0
    without calling claude), --no-agent-check (suppress the
    agent frontmatter permission check).
  • Agent frontmatter permission check: when --agent NAME is
    set, roba parses the agent's tools: field from its YAML
    frontmatter and warns on stderr if any declared tools are not
    covered by the resolved allowlist. Granular Bash(git:*) allows
    count as covering bare Bash. The check is best-effort and
    non-blocking -- dispatch proceeds regardless. Suppressed by
    --full-auto, --quiet, and --no-agent-check; also
    honoured via ROBA_NO_AGENT_CHECK env var and no_agent_check = true in a profile. Closes the silent partial-capability trap
    when a lower-layer profile doesn't grant what the agent needs.
  • Other dispatch flags: -C, --cwd PATH (run as if invoked
    from a different directory), --model NAME (override the model
    per call), --no-retry (disable wrapper-level auto-retry on
    transient failures; deterministic for orchestrator scripts),
    --effort LEVEL (cost/quality tradeoff -- low, medium,
    high, xhigh, max), --bare (minimal-overhead mode: skip
    hooks, LSP, plugin sync, CLAUDE.md auto-discovery, auto-memory,
    and keychain reads -- for non-interactive dispatches),
    --system-prompt TEXT (replace the default system prompt) and
    --append-system-prompt TEXT (append to it; when both are set,
    replace runs first), --dispatch (preset for unattended
    file-mutating workers -- implies --full-auto, --worktree,
    and --fresh, each individually overridable; warns when
    --agent is unset).
  • Cost in dollars: roba cost now reports a dollar figure
    alongside tokens, computed from a bundled per-model rate table
    (src/rates.toml, baked in via include_str!). Per-project
    breakdown (--by-project) gains a COST column and --json
    gains a cost_usd field (rollup + per project) plus an
    input/output/cache usage breakdown. Dollars also appear in
    the per-call footer. Override the table with --rates-file PATH
    (or ROBA_RATES_FILE), or suppress dollars with --no-dollars
    when the bundled rates are stale; the table carries an as_of
    date surfaced on the report. Models the table doesn't cover are
    listed as "rates unknown" rather than costed at a misleading $0.
  • Subcommands: roba history [--paths [N]] (list recent
    sessions; --paths emits the JSONL session file paths,
    most-recent first, for shell composition and corpus mining),
    roba last (reprint last run), roba cost [--by-project] [--project SLUG] [--json] [--rates-file PATH] [--no-dollars]
    (token + dollar usage rollup),
    roba profile {list,show,init,path,active},
    roba alias {list,show,path} (user-defined aliases from
    roba.toml).
  • see_also error-envelope field: additive v1 field on the
    --json error envelope (error.see_also, a list of doc URLs).
    Omitted from the JSON when empty, so the v1 shape is unchanged for
    errors with no doc pointer.

Config

  • roba.toml config file with layered resolution:
    CLI > env (ROBA_<PARAM>) > active profile overlay >
    top-level keys > built-in default > claude default. Project
    files walk up to the git root; closer-to-cwd wins per key;
    lists concat across files; vars merge per key.
  • Profiles: [profile.NAME] overlays in any roba.toml;
    default profile auto-applies when none is named (suppressed
    via --no-default-profile or ROBA_PROFILE=).
  • User-defined aliases: [alias.NAME] shortcuts. Each alias
    is invoked as roba NAME [args] and expands to a prompt
    template (with positional ${1} / ${@}, named ${pr} via the
    args schema, $$ for literal $, and $(command) shell
    substitution) plus default flags. Aliases can pin a subagent
    (agent = "NAME") and override CLI flags. Lookup order:
    built-in subcommand first, then alias, then unknown-alias
    suggestions (Levenshtein top-3).
  • Env-var override layer: every CLI knob is settable via
    ROBA_<PARAM> (uppercased, - -> _, prefixed ROBA_).
    Lists comma-separated; vars per-key via ROBA_VAR_<KEY>.

Output discipline

  • stdout = the answer; stderr = metadata (cost footer, tool calls,
    refusal warnings, spinner, errors).
  • Auto-detect: rich on a TTY, plain on a pipe. NO_COLOR=1
    honored.
  • Versioned JSON envelope (--json):
    • Success: { "version": 1, "result": { QueryResult }, "refusal": bool }
    • Error: { "version": 1, "error": { kind, message, exit_code, chain } }
    • V1 contract: top-level version always present; result xor
      error; inner fields preserved across additive changes;
      breaking shape changes bump the version.
  • Refusal signal (refusal: bool in the success envelope) for
    non-TTY consumers; exit code stays 0 on refusal (the call
    succeeded, the heuristic labels the body).
  • Structured JSON error envelope: runtime failures emit a
    parseable { "error": {...} } object on stderr instead of plain
    anyhow text. kind mirrors the typed exit code.

Permissions precedence + cross-layer suppression

  • CLI --readonly actively suppresses lower-layer writable = true and full_auto = true. CLI --writable suppresses
    lower-layer full_auto = true. Mutual-exclusion holds across
    layers, not just within the CLI parse.
  • allow_tool / deny_tool lists accumulate across files;
    closer-to-cwd entries concat on top of farther-from-cwd.
    CLI / env replace the resolved list; profile concats.
  • Deny wins when the same tool appears in both lists.

Failure modes

  • Typed exit codes: 0 ok, 1 generic, 2 auth (re-login needed), 3
    budget exceeded, 4 timeout.
  • Fail-fast on interactive flags without a TTY: -e, --pick,
    and --editor-history N > 0 exit 1 with a clear message
    instead of hanging on input that can't arrive.
  • --no-retry flag and ROBA_NO_RETRY=1 env var disable
    wrapper-level auto-retry for the run.

Changed

  • Permissions precedence model documented in README.md
    (## Permissions -> ### Precedence) and docs/profiles.md.
    Spells out the CLI > env > profile overlay > top-level >
    built-in default layering, the writable / full_auto
    interaction, the concat-vs-replace behavior for tool lists, and
    the deny-wins rule.
  • --quiet vs --plain help text disambiguated. They're
    orthogonal: --quiet is the metadata kill-switch (suppress
    footer, spinner, tool markers); --plain is the decoration
    kill-switch (no markdown render, no color, no spinner).
  • --readonly is now an active suppressor rather than a
    no-op marker. Passing --readonly on the CLI cancels a
    writable = true or full_auto = true coming from a profile
    or env var.
  • --stream documented as a TTY-only nicety, never
    load-bearing on a pipe; the agent-ABI surface (--json, typed
    exit codes, error envelope) is the contract for non-TTY
    consumers.
  • Security audit moved off the PR critical path to a daily
    scheduled job (.github/workflows/security-audit.yml) + manual
    workflow_dispatch. PR CI feedback no longer waits on the
    audit's full dep-tree scan.
  • BREAKING (pre-publish): -c / --continue and -w /
    --worktree no longer require = for their values.
    The
    require_equals constraint was dropped, so the natural
    space-separated form now works (roba -c 7c3f9a21,
    roba -w mybranch). The trade-off: because the value is
    optional, a bare word right after the flag is consumed as that
    value, so roba -c "follow up" now treats follow up as the
    session id and roba -w "do it" treats do it as the worktree
    name (previously these were parsed as the prompt). The documented
    escape is the new -p / --prompt flag:
    roba -c -p "follow up", roba -w mybranch -p "do it". The
    = form (-c=ID, -w=NAME) still works unchanged.

Removed

These are pre-publish breaking surface changes; no published
users yet.

  • --head N and --tail N flags. Pipe mode already has
    | head/| tail; TTY scrollback covers the TTY case;
    source-vs-rendered semantics were never pinned.
  • --save PATH and --tee PATH flags. Collapsed into
    the new -o, --out PATH (writes to file AND stdout; extension
    drives format; --json forces JSON regardless). File-only is
    the Unix idiom -o foo.json > /dev/null.
  • --resume ID flag. Unified into -c [ID] (-c bare =
    continue most recent; -c ID or -c=ID = resume specific).
    --fork now requires an explicit id (-c ID --fork).
  • ROBA_PROFILES_FILE env var (point-at-an-extra-file).
    Subsumed by the general ROBA_<PARAM> override layer + file
    walk-up discovery.
  • BREAKING: bundled skill+agent library and the
    roba skill {install,list,show} and roba agent {install,list,show} subcommands. Bring your own skills
    and agents via ~/.claude/skills/ and
    ~/.claude/agents/. See
    joshrotenberg/agent-tools
    for one curated set.

Fixed

  • Fail fast when an interactive-only flag is set without a TTY
    on stdin. -e / --editor and --pick pre-check
    stdin.is_terminal() and exit 1 with a canonical message
    instead of hanging on input that can't arrive.

Docs

  • docs/vs-claude-p.md: side-by-side comparison between
    roba and claude -p, with worked examples for common cases.
  • docs/use-cases.md: cookbook-style use cases, seeded with
    the multi-repo orchestration pattern (orchestration bus =
    headless roba calls; cockpit = optional tmux for
    observation).
  • docs/examples/github-actions/pr-review.yml: example
    workflow YAML for running roba as a PR auto-reviewer.
  • docs/profiles.md: gh allow-list worked example under
    ## Worked examples.
  • Docs site: an mdbook
    aggregating the README and docs/ into a navigable site, deployed
    to GitHub Pages on push to main
    (joshrotenberg.github.io/roba).
    The book source is generated by book/build.sh; only book.toml
    and the script are committed.
  • README slimmed: the repo README.md is now a tight landing
    page serving both GitHub visitors and the book's Introduction. The
    heaviest sections moved into dedicated docs/ pages:
    docs/permissions.md, docs/scripting.md, docs/aliases.md.
  • Book structured into three audience-driven sections: "Getting
    started", "Using roba", "Reference". New docs pages:
    docs/quickstart.md (install-to-first-answer),
    docs/reference.md (flags, env vars, exit codes, JSON envelope,
    config schema). (refs #86)