# How the Output Directory Structure Is Determined for Each Target Platform in Compound Engineering

> Learn how the compound engineering CLI determines output directory structure for each target platform. Discover file writing strategies and subdirectory creation methods.

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

---

**The compound-engineering CLI determines output directory structures by inspecting the basename of the `outputRoot` argument through target-specific resolver functions, writing files directly into native configuration directories when detected or creating hidden subdirectories otherwise.**

The EveryInc/compound-engineering-plugin repository provides a conversion CLI that transforms Claude plugins into platform-specific formats. When converting to targets like OpenCode, Codex, Droid, Cursor, or Pi, the tool must decide whether to write files directly into the specified output path or nest them within platform-specific hidden folders to maintain clean project structures.

## Path Resolution Architecture

Each target platform implements a dedicated resolver function that analyzes `path.basename(outputRoot)` to determine the final layout. These helpers—`resolveOpenCodePaths`, `resolveCodexRoot`, `resolveDroidPaths`, `resolveCursorPaths`, and `resolvePiPaths`—separate the concern of *where* to write from *what* to write.

The decision logic follows a consistent pattern across all targets:

1. Extract the base name of the provided `outputRoot` path
2. Compare against the target's native configuration directory name (e.g., `.opencode`, `.codex`, `.factory`)
3. Write directly if matched; otherwise create a nested hidden directory

## Target-Specific Directory Structures

### OpenCode

In [`src/targets/opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/opencode.ts), the `resolveOpenCodePaths` function checks if the basename equals `"opencode"` (global install) or `".opencode"` (project install). If matched, files write directly; otherwise they nest under `.opencode/`.

- [`opencode.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/opencode.json) or [`.opencode/opencode.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/.opencode/opencode.json)
- `agents/` or `.opencode/agents/`
- `plugins/` or `.opencode/plugins/`
- `skills/` or `.opencode/skills/`

### Codex

The `resolveCodexRoot` function in [`src/targets/codex.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/codex.ts) returns `outputRoot` unchanged when the basename is `".codex"`. Any other base name triggers nesting under `outputRoot/.codex`.

- [`config.toml`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/config.toml) (or [`.codex/config.toml`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/.codex/config.toml))
- `prompts/`
- `skills/`

### Droid

Implemented in [`src/targets/droid.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/droid.ts), `resolveDroidPaths` recognizes `.factory` as the native Droid configuration directory. Other outputs receive a `.factory/` subdirectory.

- `commands/` or `.factory/commands/`
- `droids/` or `.factory/droids/`
- `skills/` or `.factory/skills/`

### Cursor

In [`src/targets/cursor.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/cursor.ts), the `resolveCursorPaths` function treats `.cursor` as the leaf directory. Non-matching bases result in `outputRoot/.cursor/`.

- `rules/`
- `commands/`
- `skills/`
- [`mcp.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/mcp.json)

### Pi

The `resolvePiPaths` function in [`src/targets/pi.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/pi.ts) handles three distinct cases:
- Global install: `~/.pi/agent` (basename `agent`)
- Project-local: `.pi` directory
- Default: Creates `.pi/` subdirectory

- `skills/`
- `prompts/`
- `extensions/`
- [`compound-engineering/mcporter.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/compound-engineering/mcporter.json)
- [`AGENTS.md`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/AGENTS.md) (agents block injected)

## Practical Examples

### Converting to OpenCode with Custom Output

When the output directory does not match the native `.opencode` name, the CLI creates a nested structure:

```bash
bun run src/commands/convert.ts --from ./my-plugin --to opencode --output ./my-plugin-output

```

Resulting structure:

```

my-plugin-output/
└── .opencode/
    ├── opencode.json
    ├── agents/
    ├── plugins/
    └── skills/

```

### Global Installation for Pi

Targeting the global Pi installation path bypasses subdirectory creation:

```bash
bun run src/commands/convert.ts --from ./my-plugin --to pi --output ~/.pi/agent

```

This writes directly to:

```

~/.pi/agent/
├── skills/
├── prompts/
├── extensions/
├── compound-engineering/
│   └── mcporter.json
└── AGENTS.md

```

### Codex with Non-Standard Output Path

```bash
bun run src/commands/convert.ts --from ./my-plugin --to codex --output ./codex-output

```

Because `codex-output` does not equal `.codex`, the tool nests files:

```

codex-output/
└── .codex/
    ├── config.toml
    ├── prompts/
    └── skills/

```

## Key Implementation Files

| File | Responsibility |
|------|---------------|
| [`src/targets/index.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/index.ts) | Registers targets and wires `convert` and `write` functions |
| [`src/targets/opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/opencode.ts) | OpenCode conversion logic and `resolveOpenCodePaths` |
| [`src/targets/codex.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/codex.ts) | Codex output and `resolveCodexRoot` |
| [`src/targets/droid.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/droid.ts) | Droid implementation and `resolveDroidPaths` |
| [`src/targets/cursor.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/cursor.ts) | Cursor handling and `resolveCursorPaths` |
| [`src/targets/pi.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/pi.ts) | Pi platform logic and `resolvePiPaths` |
| [`src/utils/files.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/utils/files.ts) | Shared filesystem utilities for all writers |

## Summary

- **Basename inspection drives layout**: Each target's resolver function examines `path.basename(outputRoot)` to determine whether to write files directly or create nested hidden directories.
- **Native directories write flat**: When `outputRoot` ends with the target's native config name (`.opencode`, `.codex`, `.factory`, `.cursor`, or `agent` for Pi), files write directly into that path.
- **Automatic nesting**: Non-matching output paths automatically receive hidden subdirectories (`.opencode/`, `.codex/`, etc.) to maintain clean project structures.
- **Separation of concerns**: Resolver functions handle path logic while writer functions focus on content generation, as implemented across `src/targets/*.ts`.

## Frequently Asked Questions

### How does the CLI know whether to create a subdirectory or write directly to the output path?

The CLI extracts the basename of the `outputRoot` argument and compares it against the target platform's native configuration directory name. If the basename matches (e.g., `.codex` for Codex targets or `agent` for global Pi installs), it writes directly; otherwise it creates a hidden subdirectory with the appropriate name.

### Can I force the CLI to write directly to a specific directory without automatic nesting?

Yes, by naming your output directory to match the target's native configuration folder. For example, use `--output ./.codex` for Codex targets or `--output ~/.pi/agent` for global Pi installations. The resolver functions in `src/targets/*.ts` explicitly check for these basenames to bypass subdirectory creation.

### What happens when converting to Droid if my output folder is not named `.factory`?

The `resolveDroidPaths` function in [`src/targets/droid.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/droid.ts) creates a `.factory/` subdirectory within your specified output path. Your generated files then populate `outputRoot/.factory/commands/`, `outputRoot/.factory/droids/`, and `outputRoot/.factory/skills/` rather than writing directly to the root.

### Where are the resolver functions defined for each target platform?

Each target platform defines its own resolver in its respective file under `src/targets/`: `resolveOpenCodePaths` in [`opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/opencode.ts), `resolveCodexRoot` in [`codex.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/codex.ts), `resolveDroidPaths` in [`droid.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/droid.ts), `resolveCursorPaths` in [`cursor.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/cursor.ts), and `resolvePiPaths` in [`pi.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/pi.ts). These functions are imported and used by the main conversion logic in [`src/targets/index.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/targets/index.ts).