# How to Add a New Target Provider to the Converter Architecture in Compound Engineering

> Learn to add a new target provider to the converter architecture in Compound Engineering. Define bundle types implement converter functions create writer modules and register your provider.

- Repository: [Every/compound-engineering-plugin](https://github.com/everyinc/compound-engineering-plugin)
- Tags: how-to-guide
- Published: 2026-02-16

---

**To add a new target provider to the converter architecture, you must define a bundle type, implement a converter function, create a provider-specific writer module, and register the provider in the target registry.**

The EveryInc/compound-engineering-plugin CLI uses a modular conversion pipeline built around **target providers**. Each provider transforms Claude plugin data into a platform-specific bundle format. When you add a new target provider to the converter architecture, you extend the CLI to support additional output formats like Codex, OpenCode, or custom internal tools.

## Core Components of the Converter Architecture

Every target provider consists of three integrated components that work together to transform and persist plugin data.

### Target Registry

The **target registry** declares the provider’s metadata, implementation status, and handler functions. It lives in [`src/targets/index.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/index.ts) and maps provider names to their conversion and writing logic. The registry entry for Codex spans lines 35‑40【/tmp/instagit__i48kegw/src/targets/index.ts#L35-L40】.

### Writer Module

The **writer module** materializes the provider-specific bundle on disk. It handles directory creation, file writing, and configuration generation. Each provider has its own writer file at `src/targets/<provider>.ts`. The Codex writer’s core logic appears at lines 6‑8【/tmp/instagit__i48kegw/src/targets/codex.ts#L6-L8】.

### Bundle Type and Converter

The **bundle type** defines the data structure the writer expects, while the **converter** transforms Claude plugin data into that structure. Types reside in `src/types/<provider>.ts` and converters in `src/converters/claude-to-<provider>.ts`. The Codex bundle type is defined at lines 18‑23【/tmp/instagit__i48kegw/src/types/codex.ts#L18-L23】, and the converter function starts at line 10【/tmp/instagit__i48kegw/src/converters/claude-to-codex.ts#L10-L15】.

## Step-by-Step Implementation Guide

Follow these steps to integrate a new target provider into the converter architecture.

### Step 1: Define the Bundle Type

Create `src/types/<provider>.ts` to describe the data structure your writer will receive. Follow the pattern established by the Codex implementation:

```typescript
// src/types/<provider>.ts
export type <Provider>Prompt = { name: string; content: string }
export type <Provider>SkillDir = { name: string; sourceDir: string }
export type <Provider>GeneratedSkill = { name: string; content: string }

export type <Provider>Bundle = {
  prompts: <Provider>Prompt[]
  skillDirs: <Provider>SkillDir[]
  generatedSkills: <Provider>GeneratedSkill[]
  mcpServers?: Record<string, ClaudeMcpServer>
}

```

Replace `<Provider>` with your provider’s PascalCase name.

### Step 2: Implement the Converter

Create `src/converters/claude-to-<provider>.ts` to transform `ClaudePlugin` data into your bundle type. The function must accept a `ClaudePlugin` and return your `<Provider>Bundle`:

```typescript
// src/converters/claude-to-<provider>.ts
import type { ClaudePlugin } from "../types/claude"
import type { <Provider>Bundle } from "../types/<provider>"
import type { ClaudeToOpenCodeOptions } from "./claude-to-opencode"

export function convertClaudeTo<Provider>(
  plugin: ClaudePlugin,
  _opts: ClaudeToOpenCodeOptions,
): <Provider>Bundle {
  // Transform plugin data into provider-specific structures
  const skillDirs = plugin.skills.map(s => ({ name: s.name, sourceDir: s.sourceDir }))
  
  return { 
    prompts: [], 
    skillDirs, 
    generatedSkills: [], 
    mcpServers: plugin.mcpServers 
  }
}

```

### Step 3: Create the Writer Module

Add `src/targets/<provider>.ts` to handle disk operations. This module writes the bundle to the appropriate directory structure:

```typescript
// src/targets/<provider>.ts
import path from "path"
import { ensureDir, copyDir, writeText } from "../utils/files"
import type { <Provider>Bundle } from "../types/<provider>"

export async function write<Provider>Bundle(
  outputRoot: string, 
  bundle: <Provider>Bundle
): Promise<void> {
  const root = path.join(outputRoot, ".<provider>")
  await ensureDir(root)

  // Write prompts
  for (const prompt of bundle.prompts) {
    await writeText(
      path.join(root, "prompts", `${prompt.name}.md`), 
      prompt.content + "\n"
    )
  }

  // Copy skill directories
  for (const skill of bundle.skillDirs) {
    await copyDir(skill.sourceDir, path.join(root, "skills", skill.name))
  }

  // Write generated skills
  for (const gen of bundle.generatedSkills) {
    await writeText(
      path.join(root, "skills", gen.name, "SKILL.md"), 
      gen.content + "\n"
    )
  }
}

```

### Step 4: Register the Provider

Update [`src/targets/index.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/index.ts) to include your provider in the `targets` map. Import your converter and writer, then add the registry entry:

```typescript
// src/targets/index.ts
import { convertClaudeTo<Provider> } from "../converters/claude-to-<provider>"
import { write<Provider>Bundle } from "./<provider>"

// Inside the targets object:
<provider>: {
  name: "<provider>",
  implemented: true,
  convert: convertClaudeTo<Provider> as TargetHandler<<Provider>Bundle>["convert"],
  write: write<Provider>Bundle as TargetHandler<<Provider>Bundle>["write"],
},

```

### Step 5: Add Tests

Create test files following the existing patterns:

- `tests/<provider>-converter.test.ts` – Verify `convertClaudeTo<Provider>` produces correct bundle shapes using fixtures from `tests/fixtures/sample-plugin`.
- `tests/<provider>-writer.test.ts` – Verify `write<Provider>Bundle` creates expected directory structures and files.

### Step 6: Document the Provider

Add `docs/specs/<provider>.md` describing:
- File layout and directory structure
- Configuration format (if applicable)
- MCP server integration details
- Any provider-specific metadata

## Minimal Working Example

Here is a complete skeleton for a hypothetical `myprovider` target:

```typescript
// src/types/myprovider.ts
import type { ClaudeMcpServer } from "./claude"

export type MyProviderPrompt = { name: string; content: string }
export type MyProviderSkillDir = { name: string; sourceDir: string }
export type MyProviderGeneratedSkill = { name: string; content: string }

export type MyProviderBundle = {
  prompts: MyProviderPrompt[]
  skillDirs: MyProviderSkillDir[]
  generatedSkills: MyProviderGeneratedSkill[]
  mcpServers?: Record<string, ClaudeMcpServer>
}

```

```typescript
// src/converters/claude-to-myprovider.ts
import type { ClaudePlugin } from "../types/claude"
import type { MyProviderBundle } from "../types/myprovider"
import type { ClaudeToOpenCodeOptions } from "./claude-to-opencode"

export function convertClaudeToMyProvider(
  plugin: ClaudePlugin,
  _opts: ClaudeToOpenCodeOptions,
): MyProviderBundle {
  const skillDirs = plugin.skills.map(s => ({ name: s.name, sourceDir: s.sourceDir }))
  return { prompts: [], skillDirs, generatedSkills: [], mcpServers: plugin.mcpServers }
}

```

```typescript
// src/targets/myprovider.ts
import path from "path"
import { ensureDir, copyDir, writeText } from "../utils/files"
import type { MyProviderBundle } from "../types/myprovider"

export async function writeMyProviderBundle(outputRoot: string, bundle: MyProviderBundle): Promise<void> {
  const root = path.join(outputRoot, ".myprovider")
  await ensureDir(root)

  for (const skill of bundle.skillDirs) {
    await copyDir(skill.sourceDir, path.join(root, "skills", skill.name))
  }
}

```

```typescript
// src/targets/index.ts (add entry)
import { convertClaudeToMyProvider } from "../converters/claude-to-myprovider"
import { writeMyProviderBundle } from "./myprovider"

myprovider: {
  name: "myprovider",
  implemented: true,
  convert: convertClaudeToMyProvider as TargetHandler<MyProviderBundle>["convert"],
  write: writeMyProviderBundle as TargetHandler<MyProviderBundle>["write"],
},

```

## Summary

To add a new target provider to the converter architecture in the EveryInc/compound-engineering-plugin repository, you must complete four core tasks:

- **Define the bundle type** in `src/types/<provider>.ts` to establish the data contract between the converter and writer.
- **Implement the converter** in `src/converters/claude-to-<provider>.ts` to transform `ClaudePlugin` data into your bundle format.
- **Create the writer module** in `src/targets/<provider>.ts` to persist the bundle to disk using utilities like `ensureDir`, `copyDir`, and `writeText`.
- **Register the provider** in [`src/targets/index.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/index.ts) by adding an entry to the `targets` map with `implemented: true` and references to your convert and write functions.

## Frequently Asked Questions

### What is the target registry in compound-engineering-plugin?

The **target registry** is the central mapping in [`src/targets/index.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/index.ts) that declares all available conversion targets. Each entry specifies the provider name, whether it is implemented, and references to the converter and writer functions. The CLI reads this registry to determine which `--to` options are available.

### How do I test a new target provider before registering it?

Create unit tests in `tests/<provider>-converter.test.ts` and `tests/<provider>-writer.test.ts` using the sample plugin fixtures in `tests/fixtures/sample-plugin`. Verify that your converter produces the correct bundle structure and that your writer creates the expected directory tree and files on disk.

### Can I extend an existing provider instead of creating a new one?

While you can import and reuse utility functions from existing providers, the architecture expects each target to have its own distinct bundle type, converter, and writer. If you need to modify behavior for an existing target, you should update that target’s specific files rather than creating a parallel implementation.

### What file utilities are available for writing provider bundles?

The repository provides several utilities in [`src/utils/files.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/utils/files.ts) including `ensureDir` for creating directories, `copyDir` for copying skill directories, `writeText` for writing text files, and `backupFile` for creating backups before overwriting existing files. These utilities handle path resolution and ensure consistent file system operations across all target providers.