# How to Template Agent Runners for Headless CI Execution Across Claude Code, Codex, OpenCode, and CodeLayer

> Template agent runners for headless CI execution across Claude Code, Codex, OpenCode, and CodeLayer. Standardize runtime setup, API keys, and output extraction with a single YAML file in humanlayer/skills.

- Repository: [HumanLayer/skills](https://github.com/humanlayer/skills)
- Tags: how-to-guide
- Published: 2026-09-13

---

**The `humanlayer/skills` repository standardizes headless CI execution through a single YAML template file that configures runtime setup, API key injection, headless CLI invocation, output capture, and model-specific response extraction for each supported AI coding agent.**

The `humanlayer/skills` repository provides a unified approach to running AI coding agents in headless CI environments. By defining model-specific templates in [`plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md), teams can drop ready-made configurations into GitHub Actions workflows. This eliminates environment drift and ensures consistent behavior across **Claude Code**, **Codex CLI**, **OpenCode**, and **CodeLayer** runners.

## The 5-Step Headless Execution Pattern

Every agent runner template in the repository follows an identical five-step choreography to ensure deterministic, debuggable CI runs:

1. **Set up the runtime** – Install Node.js (or Bun) and the global CLI package for the chosen agent.
2. **Export the secret** – Inject the required API key from repository secrets (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.) into the environment.
3. **Run the agent headlessly** – Read the prompt from [`/tmp/agent-prompt.md`](https://github.com/humanlayer/skills/blob/main//tmp/agent-prompt.md) and pass it to the CLI with flags that suppress interactive prompts and force deterministic output formats.
4. **Capture raw output** – Tee every command's full console output to [`/tmp/agent-output.txt`](https://github.com/humanlayer/skills/blob/main//tmp/agent-output.txt) for artifact upload and debugging.
5. **Extract the final response** – Use model-specific extraction logic (often `jq` or `sed`) to write the agent's final answer to [`/tmp/pr-body.md`](https://github.com/humanlayer/skills/blob/main//tmp/pr-body.md) for downstream PR creation.

## Model-Specific Runner Configurations

While the orchestration pattern remains constant, each AI model requires distinct CLI flags, output formats, and extraction logic as defined in the templates file.

### Claude Code

**Claude Code** requires the `ANTHROPIC_API_KEY` secret and produces **stream-JSON** output that must be parsed with `jq`.

The installation uses `npm install -g @anthropic-ai/claude-code`. The headless invocation (lines 41-48 in the templates file) uses:

```bash
claude -p "$PROMPT" \
  --permission-mode bypassPermissions \
  --output-format stream-json \
  --verbose \
  2>&1 | tee /tmp/agent-output.txt

```

Extraction requires filtering the final assistant message from newline-delimited JSON objects:

```bash
cat /tmp/agent-output.txt \
  | grep '^{' \
  | jq -s '[.[] | select(.type == "assistant" and .message.content)] | last | .message.content[] | select(.type == "text") | .text' -r \
  > /tmp/pr-body.md

```

### Codex CLI

**Codex CLI** consumes the `OPENAI_API_KEY` secret and offers built-in extraction support, eliminating the need for manual parsing.

Installation via `npm install -g @openai/codex`. The run command (lines 73-78) includes a direct output flag:

```bash
codex exec "$PROMPT" \
  --json \
  --output-last-message /tmp/pr-body.md

```

Because `--output-last-message` handles extraction internally, no secondary `jq` step is required.

### OpenCode

**OpenCode** supports multiple providers (accepting either `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`) and uses Bun as its runtime.

Installation: `bun install -g opencode-ai`. The invocation (lines 5-11) specifies JSON formatting:

```bash
opencode run "$PROMPT" --format json

```

Extraction traverses the messages array:

```bash
jq -r '.messages | ... | .content' /tmp/agent-output.txt > /tmp/pr-body.md

```

### CodeLayer

**CodeLayer** typically requires both `ANTHROPIC_API_KEY` and `GH_TOKEN`, utilizing Bun's executable runner.

Installation uses `bunx @humanlayer/cli@latest codelayer`. The command (lines 42-46) outputs plain text with ANSI color codes:

```bash
bunx @humanlayer/cli@latest codelayer \
  --provider anthropic \
  --model claude-opus-4-8 \
  --thinking high \
  --prompt "$PROMPT" \
  2>&1 | tee /tmp/agent-output.txt

```

Extraction strips ANSI escape sequences using `sed`:

```bash
cat /tmp/agent-output.txt | sed 's/\x1b\[[0-9;]*m//g' > /tmp/pr-body.md

```

## Complete CI Workflow Examples

### Claude Code in GitHub Actions

```yaml

# .github/workflows/ci.yml

steps:
  - uses: actions/setup-node@v4
    with:
      node-version: 24
  - run: npm install -g @anthropic-ai/claude-code
  - name: Run Claude Code
    env:
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    run: |
      claude -p "$PROMPT" \
        --permission-mode bypassPermissions \
        --output-format stream-json \
        --verbose \
        2>&1 | tee /tmp/agent-output.txt
  - name: Extract PR body
    run: |
      cat /tmp/agent-output.txt \
        | grep '^{' \
        | jq -s '[.[] | select(.type == "assistant" and .message.content)] | last | .message.content[] | select(.type == "text") | .text' -r \
        > /tmp/pr-body.md

```

### CodeLayer in GitHub Actions

```yaml
steps:
  - uses: oven-sh/setup-bun@v2
  - name: Run CodeLayer
    env:
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      FORCE_COLOR: "3"
    run: |
      bunx @humanlayer/cli@latest codelayer \
        --provider anthropic \
        --model claude-opus-4-8 \
        --thinking high \
        --prompt "$PROMPT" \
        2>&1 | tee /tmp/agent-output.txt
  - name: Extract PR body
    run: |
      cat /tmp/agent-output.txt | sed 's/\x1b\[[0-9;]*m//g' > /tmp/pr-body.md

```

## Critical Implementation Details

All templates in [`agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/agent-runner-templates.md) include a mandatory pre-flight check: **run the actuator locally first** (lines 7-13). This verification step ensures the command syntax and prompt formatting work correctly before committing to a long-running CI pipeline.

The output format differences drive the extraction complexity:

- **Stream-JSON** (Claude Code): Requires `jq` to filter the last assistant message from potentially hundreds of incremental updates.
- **Structured JSON** (Codex, OpenCode): Allows direct property access or built-in extraction flags.
- **ANSI Text** (CodeLayer): Requires regex-based stripping of color codes to produce clean markdown.

## Key Files and Architecture

| File | Role |
|------|------|
| [`plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md) | Central source of all headless CLI snippets and extraction steps. |
| [`plugins/design-control-loop/skills/design-control-loop/SKILL.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/SKILL.md) | Explains how the templates integrate within the design-control-loop workflow. |
| [`plugins/build-iterated-agentic-loop/skills/build-iterated-agentic-loop/SKILL.md`](https://github.com/humanlayer/skills/blob/main/plugins/build-iterated-agentic-loop/skills/build-iterated-agentic-loop/SKILL.md) | Demonstrates template application for iterative loop construction. |

## Summary

- The `humanlayer/skills` repository provides a **single source of truth** for agent runner templates located at [`plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md).
- Each supported model follows a **5-step pattern**: runtime setup, secret export, headless execution, output capture, and response extraction.
- **Claude Code** and **OpenCode** require `jq` parsing of JSON outputs, while **Codex CLI** offers built-in extraction and **CodeLayer** requires ANSI stripping.
- All templates mandate **local actuator verification** (lines 7-13) before CI deployment to prevent workflow failures.

## Frequently Asked Questions

### Where are the agent runner templates defined in the repository?

The canonical definitions reside in [`plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md). This file contains the exact CLI invocations, environment variable requirements, and extraction scripts for Claude Code, Codex CLI, OpenCode, and CodeLayer.

### How does output extraction differ between Claude Code and Codex CLI?

Claude Code outputs **stream-JSON** lines that require `jq` to filter the final assistant message from the incremental stream (lines 41-48). Codex CLI provides a `--output-last-message` flag that writes the final response directly to disk (lines 73-78), eliminating the need for post-processing.

### Why should I run the actuator locally before committing to CI?

Lines 7-13 of the templates file explicitly remind developers to verify commands locally first. This catches prompt formatting errors, permission issues, or CLI flag changes that would otherwise fail silently or consume unnecessary CI minutes during the debugging cycle.

### What runtime dependencies are required for different agents?

**Claude Code** and **Codex CLI** require Node.js and `npm` for global installation, while **OpenCode** and **CodeLayer** rely on **Bun** (`bun install` or `bunx`). The templates specify the exact setup action (`actions/setup-node@v4` vs `oven-sh/setup-bun@v2`) for each runtime environment.