# How no-mistakes Integrates with Claude, Codex, and OpenCode Agents

> Discover how no-mistakes seamlessly integrates with Claude, Codex, and OpenCode agents. Treat AI drivers as first-class plugins for effortless swapping without pipeline logic changes.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: how-to-guide
- Published: 2026-07-02

---

**no-mistakes treats every AI driver as a first-class plugin that satisfies a single Go interface, allowing seamless swapping between Claude, Codex, and OpenCode without changing pipeline logic.**

The `no-mistakes` repository provides a unified abstraction layer for AI coding agents. At its core, the project defines a common `Agent` interface in [`internal/agent/agent.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/agent.go) that standardizes how the application communicates with Claude, Codex, and OpenCode. This design enables the CLI and pipeline steps to remain agnostic to the underlying model implementation.

## The Core Agent Interface Contract

The heart of the integration lives in **[`internal/agent/agent.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/agent.go)**, which declares the `Agent` interface that every AI driver must implement:

```go
type Agent interface {
    Name() string
    Run(context.Context, RunOpts) (*Result, error)
    Close() error
}

```

The **`RunOpts`** struct carries the execution context, including the prompt string, working directory, an optional JSON schema for structured output, and a streaming callback (`OnChunk`). The **`Result`** struct returns the raw text output, an optional structured JSON blob, and token usage accounting. This contract ensures that any agent—whether Claude, Codex, or OpenCode—can be invoked identically from the pipeline.

## Claude Integration via CLI Wrapping

The Claude implementation in **[`internal/agent/claude.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/claude.go)** acts as a wrapper around the Claude CLI binary. When `Run` is invoked, the agent spawns the Claude executable (`a.bin`) with arguments constructed by `buildArgs`. It uses `shellenv.ConfigureShellCommand` to ensure the subprocess is properly reaped on exit.

The implementation reads a **stream-JSON** event feed via `parseClaudeEvents`, which provides real-time token usage metrics and a final `claudeResult`. When a JSON schema is supplied, the agent post-processes the output through `finalizeClaudeResult` to extract structured data from the model's response.

## Codex Integration with Schema Files

The Codex driver in **[`internal/agent/codex.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/codex.go)** follows a similar CLI-wrapping pattern but adapts to Codex's specific output format. The agent calls the Codex CLI (`a.bin exec …`) and writes a temporary **output-schema** file when structured output is requested.

It parses Codex's JSON-L event stream using `parseCodexEvents` to capture the final `agent_message` and token usage. Unlike Claude, Codex only returns raw text, so the driver uses `finalizeTextResult` to package the response. The agent handles the temporary file cleanup automatically after the execution completes.

## OpenCode Integration via HTTP Server

The OpenCode implementation in **[`internal/agent/opencode.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/opencode.go)** takes a different approach by managing a persistent HTTP server. The agent starts `opencode serve` via `ensureServer` and maintains the process for the duration of the session.

It opens an SSE event stream through `connectEventStream` and posts prompts via `sendMessage`. The `parseOpencodeSSE` function handles incremental text chunks as they arrive. After the message finishes, the agent prefers the structured payload (`Info.Structured`) and falls back to JSON extraction from raw text using `finalizeTextResult` if needed.

## Factory Pattern and CLI Wiring

All three agents are wired into the system through a factory function in **[`internal/agent/agent.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/agent.go)**:

```go
func NewWithOptions(name types.AgentName, bin string, args []string,
    opts Options) (Agent, error) {
    switch name {
    case types.AgentClaude:
        return &claudeAgent{bin: bin, extraArgs: args}, nil
    case types.AgentCodex:
        return &codexAgent{bin: bin, extraArgs: args}, nil
    case types.AgentOpenCode:
        return &opencodeAgent{bin: bin, extraArgs: args}, nil
    }
    return nil, fmt.Errorf("unknown agent %q", name)
}

```

The CLI wizard in **[`internal/cli/wizard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/wizard.go)** instantiates the concrete agent by calling `agent.NewWithOptions` with configuration values from [`.no-mistakes.yaml`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) or command-line flags. Pipeline steps receive the `Agent` interface and invoke it uniformly. For example, in **[`internal/pipeline/steps/review.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/review.go)**:

```go
res, err := a.Agent.Run(ctx, RunOpts{
    Prompt:     prompt,
    CWD:        workDir,
    JSONSchema: schema,
    OnChunk:    onChunk,
})

```

## Retry Logic and Error Handling

The base agent implementation provides a generic **retry wrapper** called `runWithRetry` that all concrete agents invoke. This function classifies transient errors using `classifyTransient` and agent-specific logic like `claudeRetryClassifier`, capping attempts at `claudeMaxRetries`. This ensures resilience against temporary network failures or rate limits without duplicating logic across agent implementations.

## Practical Usage Examples

### Directly Invoking an Agent from Go

You can instantiate any agent programmatically using the factory:

```go
import (
    "context"
    "github.com/kunchenguid/no-mistakes/internal/agent"
    "github.com/kunchenguid/no-mistakes/internal/types"
)

func invokeClaude() (*agent.Result, error) {
    a, err := agent.NewWithOptions(types.AgentClaude, "claude", nil, agent.Options{})
    if err != nil {
        return nil, err
    }
    defer a.Close()

    return a.Run(context.Background(), agent.RunOpts{
        Prompt:     "Fix the bug in the supplied Go file.",
        CWD:        "/tmp/repo",
        JSONSchema: []byte(`{"type":"object","properties":{"fixed":{"type":"boolean"}}}`),
        OnChunk:    func(txt string) { fmt.Print(txt) },
    })
}

```

### Switching Agents via CLI

Change the active agent without modifying code:

```bash

# Use Claude (default)

no-mistakes run --prompt "Suggest a better test name."

# Switch to Codex

no-mistakes run --agent codex --prompt "Rewrite the function in Rust."

# Use OpenCode

no-mistakes run --agent opencode --prompt "Explain this code."

```

## Summary

- **Unified Interface**: The `Agent` interface in [`internal/agent/agent.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/agent.go) defines `Name()`, `Run()`, and `Close()` methods that standardize interaction across all AI drivers.
- **Claude**: Wraps the CLI binary with JSON event streaming and structured output support via `finalizeClaudeResult`.
- **Codex**: Wraps the CLI with temporary schema file handling but returns only raw text through `finalizeTextResult`.
- **OpenCode**: Manages a persistent HTTP server with SSE streaming and prefers structured payloads from `Info.Structured`.
- **Zero Pipeline Changes**: The factory pattern in `NewWithOptions` and the wizard in [`internal/cli/wizard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/wizard.go) enable runtime agent selection without touching pipeline step code.

## Frequently Asked Questions

### How do I switch between Claude and Codex in no-mistakes?

Pass the `--agent` flag with the driver name, or set the `agent` field in your [`.no-mistakes.yaml`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) configuration file. The CLI wizard in [`internal/cli/wizard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/wizard.go) parses this value and passes it to `agent.NewWithOptions`, which instantiates the correct concrete implementation without requiring changes to pipeline logic.

### Can I add a custom AI agent to no-mistakes?

Yes. Implement the `Agent` interface from [`internal/agent/agent.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/agent.go) in a new file (e.g., [`custom.go`](https://github.com/kunchenguid/no-mistakes/blob/main/custom.go)), then add a case in the `NewWithOptions` factory function to return your implementation when the appropriate `types.AgentName` is requested. The rest of the system will automatically recognize the new agent once referenced in configuration.

### Why does Codex not return structured output like Claude?

According to the source code in [`internal/agent/codex.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/codex.go), Codex only returns raw text responses. While the agent still accepts a JSON schema and writes a temporary schema file for the CLI to consume, the `finalizeTextResult` handler indicates that Codex does not populate the structured JSON field in the `Result` object, unlike Claude which uses `finalizeClaudeResult` for JSON extraction.

### How does no-mistakes handle transient failures with OpenCode?

All agents, including OpenCode, leverage the `runWithRetry` wrapper defined in [`internal/agent/agent.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/agent.go). This function implements exponential backoff with agent-specific classifiers like `claudeRetryClassifier` and caps retry attempts at `claudeMaxRetries`. For OpenCode specifically, this handles temporary HTTP connection drops or SSE stream interruptions automatically.