oneagent

package module
v0.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2026 License: MIT Imports: 16 Imported by: 1

README

oneagent

GitHub Stars Go Reference Go Report Card

Config-driven multi-agent CLI.

oa gives Claude, Codex, OpenCode, Pi, and any future agent CLI a single normalized interface. Every backend gets the same flags, the same JSON and streaming output, and portable conversation threads. Adding a new agent is a JSON config edit — no code required.

Prerequisites

  • At least one supported agent CLI installed and signed in (e.g., claude, codex, opencode, or pi)

Install

Homebrew:

brew install 1broseidon/tap/oa

Or with Go:

go install github.com/1broseidon/oneagent/cmd/oa@latest

Quick start

# Talk to Claude (the default backend)
oa "explain this codebase"

# Use a different backend
oa -b codex "fix the auth bug"

# Machine-readable JSON output
oa --json "explain this codebase"

# Live text stream
oa --stream "review the repo"

# Normalized JSONL stream (for piping to other tools)
oa --jsonl "review the repo"

# Portable threads — start on one backend, continue on another
oa -t auth-fix "investigate the failing auth tests"
oa -b codex -t auth-fix "patch the bug"
oa -b claude -t auth-fix "summarize what changed"

# Specify model and working directory
oa -b pi -m "google/gemini-2.5-pro" -C ~/project "add tests"

# Resume a native session
oa -b claude -s abc123 "now refactor it"

# Pipe content as context
git diff | oa -b claude "review these changes"
cat internal/auth/handler.go | oa -b codex "find bugs in this file"
go test ./... 2>&1 | oa -b claude "fix these test failures"

# Thread management
oa thread list
oa thread show auth-fix
oa thread compact auth-fix

Works out of the box if claude, codex, opencode, or pi is installed and signed in.

Agent-as-tool

oa works as a dispatch layer for agents that want to delegate work to other agents. An outer agent (e.g., Claude Code) can run oa as a background task to send a targeted edit to a different model, then inspect the diff when it's done:

# From inside an agent session — dispatch a file edit to gpt-5.4 via Pi
oa -b pi -m openai-codex/gpt-5.4 "Edit internal/auth/handler.go: add rate limiting to Login" --jsonl

# Verify the result
git diff internal/auth/handler.go

The normalized output means the outer agent can parse results from any backend without special handling. Portable threads let you chain follow-ups across models.

An agent skill is included for agents that support the Agent Skills format. Install it with:

npx skills add 1broseidon/oneagent --skill oa-dispatch

Pipelines

Chain agents sequentially with && and shared threads. Each step sees the file changes and conversation context from previous steps:

# Build → review → document, different agents, one thread
oa -b codex -t feat "add input validation to the signup handler" && \
oa -b codex -t feat "review the changes, run tests, report any issues" && \
git diff | oa -b claude "write a changelog entry for this change"

Pipe content into any step as context:

# Feed test output to an agent for diagnosis
go test ./... 2>&1 | oa -b claude -t fix "diagnose these failures and fix them"

# Code review a specific diff
git diff main..HEAD | oa -b codex "review this PR for security issues"

# Summarize a log file
cat /var/log/app/errors.log | oa -b claude "summarize the error patterns"

Piped content becomes context. Positional args become instructions. If both are provided, they're combined.

Output

By default, oa prints plain text:

Here's what I found...

With --json, every invocation returns a normalized response:

{
  "result": "Here's what I found...",
  "session": "abc123-def456",
  "thread_id": "auth-fix",
  "backend": "claude"
}

With --stream, you get live text output with activity indicators on stderr:

[activity] Read README.md
OK

With --jsonl (or --stream --json), output is normalized JSONL — one event per line:

{"type":"start","run_id":"run-...","ts":"2026-03-22T15:04:05Z","backend":"claude"}
{"type":"session","run_id":"run-...","ts":"2026-03-22T15:04:05Z","backend":"claude","session":"abc123-def456"}
{"type":"activity","run_id":"run-...","ts":"2026-03-22T15:04:06Z","backend":"claude","session":"abc123-def456","activity":"Read README.md"}
{"type":"heartbeat","run_id":"run-...","ts":"2026-03-22T15:04:15Z","backend":"claude"}
{"type":"delta","run_id":"run-...","ts":"2026-03-22T15:04:16Z","backend":"claude","session":"abc123-def456","delta":"OK"}
{"type":"done","run_id":"run-...","ts":"2026-03-22T15:04:16Z","backend":"claude","session":"abc123-def456","result":"OK"}

Events are intentionally simple: start, optional session / activity / delta, library-emitted heartbeat while the process is alive, then done or error. Each event also carries a run_id and timestamp so supervisors can track liveness per attempt.

Portable threads

Threads let oa own the conversation history instead of relying on a single backend's session.

  • Same backend, same thread: reuses that backend's native session when it was the last to contribute.
  • Different backend: rebuilds context from saved turns and continues on the new backend.
  • Concurrent-safe: thread files are locked during read/write, so a bot and a cron job can safely share a thread.
  • --thread and --session are mutually exclusive.
  • Threads are stored locally in ~/.local/state/oneagent/threads/<id>.json.

Use oa thread compact <id> to summarize older turns and keep long-running threads manageable.

Hooks

Run commands before or after agent execution with --pre-run and --post-run:

# Set up a worktree before running, notify after
oa -b codex -t feat \
  --pre-run 'git worktree add -b oa-$OA_THREAD_ID ../oa-$OA_THREAD_ID HEAD' \
  --post-run 'curl -s -X POST https://hooks.example.com/notify -d @-' \
  "add input validation"

# Post-run hook receives the result on stdin
oa -b claude --post-run 'cat > /tmp/last-result.txt' "explain this codebase"

Pre-run hooks abort the run on non-zero exit. Post-run hooks are best-effort. Both receive env vars: OA_BACKEND, OA_THREAD_ID, OA_SOURCE, OA_MODEL, OA_CWD. Post-run adds OA_SESSION, OA_ERROR, OA_EXIT.

Hooks can also be set per-backend in config (run on every invocation) and as Go callbacks in the library. See docs/library.md and docs/config.md.

Configuration

oa ships with built-in defaults for claude, codex, opencode, and pi — no config file needed.

To override a built-in backend or add a new one, create ~/.config/oneagent/backends.json:

{
  "my-agent": {
    "run": "my-agent --prompt {prompt} --model {model}",
    "format": "json",
    "result": "output.text",
    "session": "session_id"
  }
}

Same-named entries replace the built-in. New entries are added alongside defaults. Use -c /path/to/backends.json to load only a specific file.

For the full config schema, field reference, match conditions, and example backends, see docs/config.md.

Use as a library

go get github.com/1broseidon/oneagent@latest
import "github.com/1broseidon/oneagent"

backends, _ := oneagent.LoadBackends("")
client := oneagent.Client{Backends: backends}

// One-shot
resp := client.Run(oneagent.RunOpts{
    Backend: "claude",
    Prompt:  "explain this code",
    CWD:     "/path/to/project",
})
fmt.Println(resp.Result)

// Streaming
client.RunStream(oneagent.RunOpts{
    Backend: "claude",
    Prompt:  "review the repo",
}, func(ev oneagent.StreamEvent) {
    fmt.Print(ev.Delta)
})

// Portable threads
resp = client.RunWithThread(oneagent.RunOpts{
    Backend:  "claude",
    ThreadID: "auth-fix",
    Prompt:   "continue debugging",
})

For the full library API, streaming details, custom thread storage, and integration patterns, see docs/library.md.

Supported backends

Backend CLI Session resume
Claude claude --resume
Codex codex exec codex exec resume
OpenCode opencode run --session
Pi pi --session

Any CLI that outputs JSON or line-delimited JSON can be added via config.

Docs

License

MIT

Documentation

Overview

Package oneagent provides a config-driven interface for running any AI agent CLI.

Backends are defined in a compact JSON config with run/resume command strings, output format (json/jsonl), and field paths for extracting results, sessions, and errors. Template variables ({prompt}, {model}, {cwd}, {session}) are substituted at runtime. This lets you add new agent backends without writing any code.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompactThread

func CompactThread(backends map[string]Backend, threadID, backend string) error

CompactThread summarizes old turns using a backend, keeping the last keepTurns.

func ConfigDir

func ConfigDir() string

ConfigDir returns the default config directory (~/.config/oneagent).

func DefaultConfigPath

func DefaultConfigPath() string

DefaultConfigPath returns the optional user override config path.

func ListThreads

func ListThreads() ([]string, error)

ListThreads returns the IDs of all saved threads.

func LoadBackends

func LoadBackends(path string) (map[string]Backend, error)

LoadBackends loads embedded defaults when path is empty and merges the optional user override file (~/.config/oneagent/backends.json) on top. When path is non-empty, only that file is loaded.

func LoadBackendsWithOptions added in v0.11.5

func LoadBackendsWithOptions(opts LoadOptions) (map[string]Backend, error)

LoadBackendsWithOptions loads backends using explicit options. This is useful for consumers that want the embedded defaults but need to own the override path instead of using ~/.config/oneagent/backends.json.

func PreflightCheckBackend added in v0.12.1

func PreflightCheckBackend(name string, b Backend) error

PreflightCheckBackend validates a single Backend without requiring a Client.

func ResolveBackendProgram added in v0.10.5

func ResolveBackendProgram(b Backend) (string, bool)

ResolveBackendProgram returns the resolved path for a backend's CLI binary, checking $PATH first, then the backend's configured paths. Returns the program name and whether it was found.

func ThreadDir

func ThreadDir() string

ThreadDir returns the directory for thread storage.

Types

type Backend

type Backend struct {
	Cmd          []string
	ResumeCmd    []string
	SystemPrompt string
	Format       string // "json" or "jsonl"
	Activity     string
	ActivityWhen string
	Delta        string
	DeltaWhen    string
	Result       string
	ResultWhen   string
	ResultAppend bool
	Session      string
	SessionWhen  string
	Error        string
	ErrorWhen    string
	DefaultModel string
	Paths        []string // additional directories to search for the CLI binary
	PromptStdin  bool     // pass prompt via stdin instead of argv
	PreRunCmd    string   // shell command to run before backend execution
	PostRunCmd   string   // shell command to run after backend execution
	Probe        string   // optional fast command to verify the backend is ready (e.g. "claude --version")
}

Backend defines how to invoke and parse output from an agent CLI. Populated by compiling a backendConfig from the JSON config file.

type Client

type Client struct {
	Backends map[string]Backend
	Store    Store
}

Client is an embeddable oneagent runtime with configurable backends and thread store.

func (Client) CompactThread

func (c Client) CompactThread(threadID, backend string) error

CompactThread summarizes old turns using a backend, keeping the last keepTurns.

func (Client) ListThreads

func (c Client) ListThreads() ([]string, error)

ListThreads returns the IDs of all saved threads from the configured store.

func (Client) LoadThread

func (c Client) LoadThread(id string) (*Thread, error)

LoadThread reads a thread from the configured store. A missing thread returns an empty thread.

func (Client) PreflightCheck added in v0.12.1

func (c Client) PreflightCheck(backend string) error

PreflightCheck validates that a backend is runnable before any job is queued. It verifies the CLI binary exists on disk and, when a Probe command is configured, executes it to catch missing API keys or auth issues early.

func (Client) Run

func (c Client) Run(opts RunOpts) Response

Run executes a prompt against the configured backends and returns a normalized response.

func (Client) RunContext added in v0.11.7

func (c Client) RunContext(ctx context.Context, opts RunOpts) Response

RunContext executes a prompt against the configured backends and returns a normalized response.

func (Client) RunStream

func (c Client) RunStream(opts RunOpts, emit func(StreamEvent)) Response

RunStream executes a prompt and emits normalized streaming events as they arrive.

func (Client) RunStreamContext added in v0.11.7

func (c Client) RunStreamContext(ctx context.Context, opts RunOpts, emit func(StreamEvent)) Response

RunStreamContext executes a prompt and emits normalized streaming events as they arrive.

func (Client) RunWithThread

func (c Client) RunWithThread(opts RunOpts) Response

RunWithThread wraps Run with thread load/save and context injection. Threading is handled by invoke() when ThreadID is set.

func (Client) RunWithThreadStream

func (c Client) RunWithThreadStream(opts RunOpts, emit func(StreamEvent)) Response

RunWithThreadStream wraps RunStream with thread load/save and context injection. Threading is handled by invoke() when ThreadID is set.

func (Client) SaveThread

func (c Client) SaveThread(t *Thread) error

SaveThread writes the thread to the configured store.

type FilesystemStore

type FilesystemStore struct {
	Dir string
}

FilesystemStore stores thread JSON files in a directory on disk.

func (FilesystemStore) ListThreads

func (s FilesystemStore) ListThreads() ([]string, error)

ListThreads returns the IDs of all saved threads from the filesystem store.

func (FilesystemStore) LoadThread

func (s FilesystemStore) LoadThread(id string) (*Thread, error)

LoadThread reads a thread from the filesystem store. A missing file returns an empty thread.

func (FilesystemStore) SaveThread

func (s FilesystemStore) SaveThread(t *Thread) error

SaveThread writes the thread to disk, creating the directory if needed.

type HookContext added in v0.11.0

type HookContext struct {
	Opts     RunOpts
	Response Response
}

HookContext is passed to the PostRun callback after a backend invocation completes.

type LoadOptions added in v0.11.5

type LoadOptions struct {
	// IncludeEmbedded loads the embedded default backends first.
	IncludeEmbedded bool
	// OverridePath is merged on top when IncludeEmbedded is true,
	// or loaded directly when IncludeEmbedded is false.
	OverridePath string
}

LoadOptions controls how backend configs are loaded.

type Response

type Response struct {
	Result   string `json:"result"`
	Session  string `json:"session"`
	ThreadID string `json:"thread_id,omitempty"`
	Backend  string `json:"backend"`
	Error    string `json:"error,omitempty"`
	Warnings string `json:"warnings,omitempty"`
	ExitCode int    `json:"exit_code,omitempty"`
	Stderr   string `json:"stderr,omitempty"`
}

Response is the normalized output from any backend.

func Run

func Run(backends map[string]Backend, opts RunOpts) Response

Run executes a prompt against the specified backend and returns a normalized response.

func RunContext added in v0.11.7

func RunContext(ctx context.Context, backends map[string]Backend, opts RunOpts) Response

RunContext executes a prompt against the specified backend with cancellation support.

func RunStream

func RunStream(backends map[string]Backend, opts RunOpts, emit func(StreamEvent)) Response

RunStream executes a prompt and emits normalized streaming events as they arrive.

func RunStreamContext added in v0.11.7

func RunStreamContext(ctx context.Context, backends map[string]Backend, opts RunOpts, emit func(StreamEvent)) Response

RunStreamContext executes a prompt with cancellation support and emits normalized streaming events.

func RunWithThread

func RunWithThread(backends map[string]Backend, opts RunOpts) Response

RunWithThread wraps Run with thread load/save and context injection.

func RunWithThreadStream

func RunWithThreadStream(backends map[string]Backend, opts RunOpts, emit func(StreamEvent)) Response

RunWithThreadStream wraps RunStream with thread load/save and context injection.

type RunOpts

type RunOpts struct {
	Backend    string
	Prompt     string
	Model      string
	Thinking   string // thinking/reasoning effort level (e.g. "low", "medium", "high")
	CWD        string
	SessionID  string
	ThreadID   string
	Source     string
	PreRun     func(*RunOpts) error // library callback: called before backend executes, can modify opts, return error to abort
	PostRun    func(*HookContext)   // library callback: called after response, for side effects
	PreRunCmd  string               // CLI shell command to run before backend execution
	PostRunCmd string               // CLI shell command to run after backend execution
}

RunOpts configures a single agent invocation.

type Store

type Store interface {
	LoadThread(id string) (*Thread, error)
	SaveThread(thread *Thread) error
	ListThreads() ([]string, error)
}

Store persists portable thread state for a Client.

type StreamEvent

type StreamEvent struct {
	Type     string    `json:"type"`
	RunID    string    `json:"run_id,omitempty"`
	TS       time.Time `json:"ts,omitempty"`
	Backend  string    `json:"backend"`
	ThreadID string    `json:"thread_id,omitempty"`
	Session  string    `json:"session,omitempty"`
	Activity string    `json:"activity,omitempty"`
	Delta    string    `json:"delta,omitempty"`
	Result   string    `json:"result,omitempty"`
	Error    string    `json:"error,omitempty"`
}

StreamEvent is a normalized incremental event emitted during a streaming run.

type Thread

type Thread struct {
	ID             string            `json:"id"`
	Summary        string            `json:"summary,omitempty"`
	Turns          []Turn            `json:"turns"`
	NativeSessions map[string]string `json:"native_sessions,omitempty"`
}

Thread is a portable conversation that can span multiple backends.

func LoadThread

func LoadThread(id string) (*Thread, error)

LoadThread reads a thread from disk. A missing file returns an empty thread.

func (*Thread) CompileContext

func (t *Thread) CompileContext(budget int) (string, bool)

CompileContext builds a context string from the thread's history within a byte budget.

func (*Thread) CompileRecentTurns added in v0.13.0

func (t *Thread) CompileRecentTurns(maxTurns, budget int) (string, bool)

CompileRecentTurns builds a minimal context string from the most recent turns.

func (*Thread) Save

func (t *Thread) Save() error

Save writes the thread to disk, creating the directory if needed.

type Turn

type Turn struct {
	Role    string `json:"role"`
	Content string `json:"content"`
	Backend string `json:"backend"`
	Source  string `json:"source,omitempty"`
	TS      string `json:"ts"`
}

Turn is a single conversation turn stored in a thread.

Directories

Path Synopsis
cmd
oa command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL