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

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 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:

// 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:

// 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:

// 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 to include your provider in the targets map. Import your converter and writer, then add the registry entry:

// 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:

// 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>
}
// 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 }
}
// 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))
  }
}
// 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 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →