harness

package module
v0.0.0-...-a7464a5 Latest Latest
Warning

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

Go to latest
Published: May 19, 2026 License: MIT Imports: 7 Imported by: 0

README

harness

A Go library for calling different AI coding agent CLIs (Docker Agent, Claude Code, Pi, Codex, opencode) through a unified interface. Switch between agents by changing a single line of code.

Install

go get github.com/rumpl/harness

Quick start

package main

import (
	"context"
	"fmt"

	"github.com/rumpl/harness"
	"github.com/rumpl/harness/dockeragent"
)

func main() {
	// Create a provider — swap this line to switch agents.
	p := dockeragent.New("coder")

	// Run the agent and handle streaming events.
	harness.Run(context.Background(), p, "Explain goroutines", func(ev harness.Event) {
		switch ev.Type {
		case harness.EventText:
			fmt.Print(ev.Text)
		case harness.EventToolCallStart:
			fmt.Printf("[tool: %s]\n", ev.ToolName)
		case harness.EventToolCallDelta:
			fmt.Print(ev.ToolArgs)
		case harness.EventToolCall:
			fmt.Printf("[tool: %s] %s\n", ev.ToolName, ev.ToolArgs)
		case harness.EventToolResult:
			fmt.Printf("[tool result: %s] %s\n", ev.ToolName, ev.ToolOutput)
		case harness.EventResult:
			fmt.Printf("\nResult: %s\n", ev.Result)
		}
	})
}
Switching providers
// Docker Agent
p := dockeragent.New("coder")

// Claude Code
p := claudecode.New("claude-sonnet-4-6", claudecode.WithEffort(claudecode.EffortHigh))

// Pi
p := pi.New("claude-sonnet-4-6")

// Codex
p := codex.New("gpt-5.4-mini")

// opencode
p := opencode.New("anthropic/claude-sonnet-4-6")

The rest of your code stays exactly the same — all providers implement harness.Provider.

For providers whose CLIs have their own default model, pass an empty model string to omit the model flag entirely (for example, codex.New("") emits codex exec ... without -m).

The Provider interface

type Provider interface {
	Name() string
	PrintCommand(prompt string) string
	InteractiveArgs(prompt string) []string
	ParseStreamLine(line string) []Event
}
Method Purpose
Name() Human-readable identifier ("docker-agent", "claude-code", etc.)
PrintCommand() Shell command for non-interactive mode (sh -c safe)
InteractiveArgs() Arg list for interactive mode (first element = binary)
ParseStreamLine() Parse one NDJSON line into []Event

Event types

Type Fields set
EventText Text
EventResult Result, Usage (opt)
EventToolCallStart ToolID (opt), ToolName
EventToolCallDelta ToolID (opt), ToolName (opt), ToolArgs raw delta
EventToolCall ToolID (opt), ToolName, ToolArgs
EventToolResult ToolID (opt), ToolName (opt), ToolOutput, ToolError

Example CLI

go run ./cmd/harness-example --provider docker-agent --model coder "Hello world"
go run ./cmd/harness-example --provider claude-code --model claude-sonnet-4-6 "Hello world"
go run ./cmd/harness-example --provider pi --model claude-sonnet-4-6 "Hello world"
go run ./cmd/harness-example --provider codex --model gpt-5.4-mini "Hello world"
go run ./cmd/harness-example --provider opencode --model anthropic/claude-sonnet-4-6 "Hello world"

# Just print the command without executing:
go run ./cmd/harness-example --print-cmd --provider docker-agent --model coder "test"

License

MIT

Documentation

Overview

Package harness provides a unified interface for interacting with different AI coding agent CLIs (Claude Code, Pi, Codex). It abstracts away the differences in command construction and stream output parsing so that switching between agents requires minimal code changes.

The central type is Provider. Create one with the constructor from a sub-package (e.g. [claudecode.New]), then call its methods to build commands or parse streaming output.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ParseJSON

func ParseJSON(line string) (map[string]any, bool)

ParseJSON is a convenience that unmarshals a line into a map. It returns nil, false if the line is not valid JSON or does not start with '{'.

func Run

func Run(ctx context.Context, p Provider, prompt string, fn func(Event)) error

Run executes the provider in print (non-interactive) mode and streams parsed events to the callback. It blocks until the command finishes or the context is cancelled. The callback is invoked synchronously for each event as it arrives.

func ShellEscape

func ShellEscape(s string) string

ShellEscape returns s wrapped in single quotes with any embedded single quotes escaped for POSIX shells: ' → '\”

Types

type Event

type Event struct {
	Type       EventType `json:"type"`
	Text       string    `json:"text,omitempty"`
	Result     string    `json:"result,omitempty"`
	Usage      *Usage    `json:"usage,omitempty"`
	ToolID     string    `json:"tool_id,omitempty"`
	ToolName   string    `json:"name,omitempty"`
	ToolArgs   string    `json:"args,omitempty"`
	ToolOutput string    `json:"output,omitempty"`
	ToolError  bool      `json:"tool_error,omitempty"`
	Reasoning  string    `json:"reasoning,omitempty"` // set when Type == EventReasoning
}

Event is a single parsed event from an agent's streaming output. Depending on the Type field, different fields are populated:

  • EventText: Text is set.
  • EventResult: Result and (optionally) Usage are set.
  • EventToolCallStart: ToolID (optional) and ToolName are set.
  • EventToolCallDelta: ToolID (optional), ToolName (optional), and ToolArgs are set to the raw argument delta.
  • EventToolCall: ToolID (optional), ToolName, and ToolArgs are set to the provider's JSON argument object when available.
  • EventToolResult: ToolID (optional), ToolName (optional), ToolOutput, and ToolError are set.
  • EventReasoning: Reasoning is set.

type EventType

type EventType string

EventType enumerates the kinds of events that a stream can produce.

const (
	// EventText is emitted when the agent produces a chunk of text output.
	EventText EventType = "text"
	// EventResult is emitted when the agent produces its final answer.
	EventResult EventType = "result"
	// EventToolCallStart is emitted when the agent starts building a tool call.
	EventToolCallStart EventType = "tool_call_start"
	// EventToolCallDelta is emitted when the agent streams tool call arguments.
	EventToolCallDelta EventType = "tool_call_delta"
	// EventToolCall is emitted when the agent invokes a tool.
	EventToolCall EventType = "tool_call"
	// EventToolResult is emitted when a tool invocation completes.
	EventToolResult EventType = "tool_result"
	// EventReasoning is emitted when the agent produces reasoning/thinking content.
	// Not all providers support this; check provider documentation.
	EventReasoning EventType = "reasoning"
)

type Provider

type Provider interface {
	// Name returns a human-readable identifier for this provider (e.g.
	// "claude-code", "pi", "codex").
	Name() string

	// PrintCommand returns a complete shell command string that runs the
	// agent in non-interactive (print/exec) mode with the given prompt.
	// The returned string is safe to pass to "sh -c".
	PrintCommand(prompt string) string

	// InteractiveArgs returns the argument list (including the binary name
	// as the first element) for launching the agent in interactive mode.
	InteractiveArgs(prompt string) []string

	// ParseStreamLine parses a single line of newline-delimited JSON output
	// from the agent's streaming mode into zero or more [Event] values. Lines
	// that are not valid JSON or not recognized are silently ignored (an empty
	// slice is returned).
	ParseStreamLine(line string) []Event
}

Provider is the interface that all agent harnesses implement. It lets you build shell commands for non-interactive (print) mode, build argument lists for interactive mode, and parse newline-delimited JSON streaming output into a common event representation.

type Usage

type Usage struct {
	InputTokens              int     `json:"input_tokens"`
	OutputTokens             int     `json:"output_tokens"`
	CacheReadInputTokens     int     `json:"cache_read_input_tokens"`
	CacheCreationInputTokens int     `json:"cache_creation_input_tokens"`
	TotalCostUSD             float64 `json:"total_cost_usd"`
	NumTurns                 int     `json:"num_turns"`
	DurationMS               int     `json:"duration_ms"`
}

Usage captures token and cost statistics reported by the agent at the end of a run.

func ExtractCodexUsage

func ExtractCodexUsage(obj map[string]any) *Usage

ExtractCodexUsage pulls token usage from a Codex turn.completed object. Codex uses: {"usage":{"input_tokens":N,"output_tokens":N,"cached_input_tokens":N}}

func ExtractPiUsage

func ExtractPiUsage(msg map[string]any) *Usage

ExtractPiUsage pulls token usage from a Pi assistant message object. Pi uses a different schema: {"usage":{"input":N,"output":N,"cacheRead":N,"cacheWrite":N,"totalTokens":N,"cost":{...}}}

func ExtractUsage

func ExtractUsage(obj map[string]any) *Usage

ExtractUsage pulls token usage information out of a generic JSON object. Returns nil if the required fields are missing.

Directories

Path Synopsis
Package claudecode provides a harness.Provider implementation for the Claude Code CLI agent.
Package claudecode provides a harness.Provider implementation for the Claude Code CLI agent.
cmd
harness-example command
Command harness-example demonstrates how to use the harness library to run an AI coding agent and process its streaming output.
Command harness-example demonstrates how to use the harness library to run an AI coding agent and process its streaming output.
Package codex provides a harness.Provider implementation for the OpenAI Codex CLI agent.
Package codex provides a harness.Provider implementation for the OpenAI Codex CLI agent.
Package dockeragent provides a harness.Provider implementation for the Docker Agent CLI.
Package dockeragent provides a harness.Provider implementation for the Docker Agent CLI.
Package opencode provides a harness.Provider implementation for the opencode CLI agent (https://opencode.ai, https://github.com/sst/opencode).
Package opencode provides a harness.Provider implementation for the opencode CLI agent (https://opencode.ai, https://github.com/sst/opencode).
Package pi provides a harness.Provider implementation for the Pi CLI agent.
Package pi provides a harness.Provider implementation for the Pi CLI agent.

Jump to

Keyboard shortcuts

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