# How Claude, Codex, and OpenCode Integrate with no-mistakes via the Native Command Protocol

> Discover how Claude Codex and OpenCode integrate seamlessly with no-mistakes. Learn about their native command protocol communication via line-delimited JSON over stdinstdout.

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

---

**Claude, Codex, and OpenCode integrate with no-mistakes as external command-line binaries that communicate via a line-delimited JSON protocol over stdin/stdout, enabling the daemon to request operations like `list-findings` through lightweight structured messages.**

The `no-mistakes` project (kunchenguid/no-mistakes) implements a **native command protocol** that treats LLM agents as standalone executables rather than linked libraries. By spawning Claude Code, Codex CLI, and OpenCode as subprocesses and exchanging JSON messages over their standard streams, the system achieves loose coupling while maintaining synchronous request-response semantics. This design allows new agents to be added without modifying the daemon's core logic, provided they implement the simple contract defined in [`internal/agent/native_command.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/native_command.go).


## Native Command Protocol Specification

The protocol centers on two structs defined in [`internal/agent/native_command.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/native_command.go): `CommandRequest` and `CommandResponse`. These types enforce a consistent envelope format across all agent integrations.

### Request and Response Types

```go
type CommandRequest struct {
    Command string   `json:"command"` // e.g. "list-findings"
    Args    []string `json:"args,omitempty"`
    Input   []byte   `json:"input,omitempty"` // optional JSON payload
}

type CommandResponse struct {
    Status  string `json:"status"` // "ok" or "error"
    Output  []byte `json:"output,omitempty"`
    Message string `json:"message,omitempty"` // error text if Status=="error"
}

```

Communication occurs through **line-delimited JSON**: the daemon writes a single JSON-encoded `CommandRequest` to the agent's stdin and expects a single JSON-encoded `CommandResponse` on stdout. This design eliminates the need for socket management or HTTP servers while remaining extensible.


## Agent Spawning and Lifecycle Management

When the daemon needs to invoke an agent, it uses [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go) to orchestrate the process through `agent.RunOnce`. The implementation creates a process group to ensure proper cleanup of child processes and uses context cancellation for timeout handling.

### Process Group Setup

The spawning logic in [`internal/agent/server.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/server.go) configures the command to run in a new process group. This allows `no-mistakes` to reap grandchildren processes reliably, preventing zombie processes when agents spawn their own subprocesses.

```go
// internal/daemon/manager.go – start an agent run
ctx, cancel := context.WithCancel(ctx)
defer cancel()

agentPath := cfg.AgentPathOverride[types.AgentClaude] // from config.yaml
cmd := exec.CommandContext(ctx, agentPath, "serve")
shellenv.ConfigureShellCommand(cmd)                 // creates a process group
cmd.Env = append(os.Environ(), "NM_AGENT=claude")   // tell the binary which agent it is
out, err := cmd.CombinedOutput()
if err != nil {
    // handle failure to start the agent
}

```

### Binary Discovery and Configuration

Agents are located using the `agent_path_override` configuration key, which maps agent types to executable paths. If no override is specified, the system falls back to built-in "fake-agent" binaries for testing. The environment setup in [`internal/agent/env.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/env.go) injects identifiers like `NM_AGENT=claude` so the binary knows which role to assume.


## Claude, Codex, and OpenCode Integration Points

Each supported agent implements a `Run` method that satisfies the protocol contract. While the daemon sees a uniform interface, each agent file handles command-specific logic and caching.

### Claude Code Implementation

In [`internal/agent/claude.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/claude.go), the `ClaudeAgent` struct implements the handler:

```go
func (a *ClaudeAgent) Run(ctx context.Context, req *CommandRequest) (*CommandResponse, error) {
    switch req.Command {
    case "list-findings":
        return &CommandResponse{
            Status: "ok",
            Output: marshalFindings(claudeCacheFindings()),
        }, nil
    // other commands …
    }
    return &CommandResponse{Status: "error", Message: "unknown command"}, nil
}

```

### Codex CLI and OpenCode

The pattern repeats in [`internal/agent/codex.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/codex.go) and [`internal/agent/opencode.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/opencode.go). Each maintains its own cache format—accessed via `internal/intent/reader_*` files—but exposes identical commands (`list-findings`, `apply-fix`, `summarize-intent`) to the daemon.


## Practical Communication Flow

When requesting findings, the daemon builds a `CommandRequest`, marshals it to JSON, and writes it to the agent's stdin. It then reads the response line and unmarshals the result.

### Sending Commands from the Daemon

```go
// Build a request to list findings
req := agent.CommandRequest{
    Command: "list-findings",
}
enc, _ := json.Marshal(req)

// Write request line
fmt.Fprintln(agentStdin, string(enc))

// Read response line
line, _ := bufio.NewReader(agentStdout).ReadString('\n')
var resp agent.CommandResponse
json.Unmarshal([]byte(line), &resp)

if resp.Status != "ok" {
    log.Fatalf("agent error: %s", resp.Message)
}
var findings []types.Finding
json.Unmarshal(resp.Output, &findings)

```

This synchronous exchange happens for every command, keeping the interaction model simple and stateless between requests.


## Summary

- **Claude, Codex, and OpenCode** integrate with `no-mistakes` as external binaries that communicate via line-delimited JSON over stdin/stdout.
- The protocol is defined in [`internal/agent/native_command.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/native_command.go) using `CommandRequest` and `CommandResponse` structs, requiring no custom RPC frameworks.
- Each agent implements the `Run(ctx context.Context, req *CommandRequest) (*CommandResponse, error)` signature in dedicated files: [`claude.go`](https://github.com/kunchenguid/no-mistakes/blob/main/claude.go), [`codex.go`](https://github.com/kunchenguid/no-mistakes/blob/main/codex.go), and [`opencode.go`](https://github.com/kunchenguid/no-mistakes/blob/main/opencode.go).
- Process lifecycle is managed through [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go) and [`internal/agent/server.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/server.go), creating process groups for clean shutdown and using `agent_path_override` for binary discovery.
- New commands can be added without recompiling the daemon—only the agent binary needs to understand new verbs like `summarize-intent` or `apply-fix`.


## Frequently Asked Questions

### What is the no-mistakes native command protocol?

The native command protocol is a lightweight communication standard used by `no-mistakes` to exchange structured messages with LLM agents. It uses line-delimited JSON over stdin/stdout, with `CommandRequest` and `CommandResponse` structs defined in [`internal/agent/native_command.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/native_command.go). This approach treats agents as standard command-line programs rather than linked libraries or network services.

### How does no-mistakes locate the Claude or Codex binary?

The system checks the `agent_path_override` configuration key in [`config.yaml`](https://github.com/kunchenguid/no-mistakes/blob/main/config.yaml) to find the absolute path to the agent executable. If no override exists for a specific agent type, it falls back to built-in "fake-agent" binaries. The [`internal/agent/env.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/env.go) file handles setting environment variables like `NM_AGENT` to identify which agent role the binary should assume.

### Can I add custom commands to the native protocol?

Yes. Because the protocol uses a generic `Command` string field, you can extend agents to recognize new verbs without modifying the daemon's source code. Simply update your agent implementation in [`internal/agent/claude.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/claude.go) (or the respective agent file) to handle the new command in the `Run` method's switch statement. The daemon will pass through any command string sent via `agent.RunOnce`.

### Why does no-mistakes use stdin/stdout instead of HTTP or gRPC?

The stdin/stdout approach eliminates network port management and firewall concerns while enforcing a clean process boundary. The [`internal/agent/server.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/agent/server.go) implementation creates process groups that ensure proper cleanup of child processes, making it easier to manage resource-intensive LLM agents that might spawn additional subprocesses. This design also allows agents to be written in any language capable of reading JSON from standard input.