francisco.trejo.dev
← Back to projects

ANIMYST

open-source

An agent framework built to plan, contextualize, and sequentially execute engineering tasks — terminal-native and provider-agnostic across Anthropic, OpenAI, and Google.

Overview

ANIMYST is an open-source terminal-native AI agent built on Textual. It runs a single foreground agent at a time — one "awakened" agent — with provider-agnostic streaming across Anthropic, OpenAI, and Google. A thin StreamEvent + dispatch-dict seam keeps the SDKs interchangeable without leaking through the rest of the codebase. Local-first by design: settings, conversations, and per-agent sessions live in ~/.animyst/ as inspectable JSON, with 0600 perms on the settings file. The TUI is a thin shell over domain/, storage/, services/, commands/, and ui/ modules, and ships with an embedded git panel for branch/diff/commit inline.

Problem

Most agent frameworks couple you to a single provider's SDK and a single agent shape. Switching providers — or running an agent next to your shell rather than inside a browser tab — still means rewriting the integration surface. Developer-native, provider-portable agent tooling is still mostly a custom build for every team.

Why it matters

The agent that lives in the terminal — next to your shell, your git tree, and your files — is going to be the daily-driver surface for engineers, not the one that lives in a browser tab. Provider-portability is the corollary: locking the daily-driver agent to one vendor's SDK isn't sustainable. Building both in the open is how that substrate gets stewarded.

Architecture

Runtime model. Single-process Textual TUI with one foreground agent "awakened" at a time. Blocking streaming runs on a worker thread via @work(thread=True); UI updates marshal back through call_from_thread(). No daemon, no background scheduler, no agent pool — interaction is the unit of work. Provider abstraction. animyst/llm.py defines a StreamEvent dataclass plus a PROVIDER_STREAMS dispatch dict mapping "anthropic" | "openai" | "google" to per-provider generator functions (stream_anthropic, stream_openai, stream_google). stream_chat() is the single entry point — picks a key from ~/.animyst/settings.json (falling back to env vars), routes to the provider stream, and normalizes deltas into StreamEvent(type="text" | "done" | "error") with a unified usage shape (input_tokens / output_tokens). Adding a provider is one function plus one dict entry. Parallel execution + scheduling. None today, by design. Agents run one at a time, interactively. A separate Ralph loop (ralph.sh) drives autonomous implementation phases for development work, but that's a dev-loop wrapper around the CLI, not a runtime agent scheduler. IDE integration surface. None — and that's the point. ANIMYST is terminal-native (Textual). Dev-workflow integration runs through the embedded git panel (branch/diff/commits inline in the TUI) and file-based config under ~/.animyst/ that's portable and version-controllable. ANIMYST_DIR points at a workspace-local config dir for sandboxed runs.

Stack

Python 3.10+Textual 1.0+Rich 13+GitPython 3.1+Anthropic SDK 0.39+OpenAI SDK 1.50+google-genai 1.0+Hatchling

What I learned

Building this in the open taught me that restraint is its own discipline. A thin provider seam beats a heavy framework, and shipping what's honestly working beats half-finished ambition. The harder lesson was respecting my own bandwidth — open source doesn't reward burning out, and the project gets better when I treat my time the same way I'd treat a teammate's: protected, finite, and worth spending well.

Links

Livebuild: 0c87a2e