# How Symlink-Based Sync Enables Real-Time Configuration Updates in Compound Engineering

> Learn how symlink-based sync enables real-time configuration updates in compound engineering. Instantly reflect file changes across outputs without copies or rebuilds. Optimize your workflow today.

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

---

**Symlink-based sync creates symbolic links between canonical skill directories and target platform folders, ensuring that any modification to source files is instantly reflected across all synced outputs without requiring copy or rebuild operations.**

The **compound-engineering-plugin** leverages symlink-based sync to bridge the gap between centralized skill development and multi-platform deployment. By using symbolic links rather than file copies, the plugin ensures that engineers can iterate on skills in real-time while maintaining consistent configurations across Pi, Droid, OpenCode, Cursor, and Codex targets.

## How Symlink-Based Sync Works in the Compound Engineering Plugin

### The Core Mechanism: Symbolic Links as Live Pointers

At the heart of the plugin's real-time capability is the creation of **symbolic links** (symlinks) from the canonical skill directories to platform-specific output folders. In [`src/utils/symlink.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/utils/symlink.ts), the `forceSymlink` function establishes these pointers using the underlying filesystem's symlink capability.

When a developer modifies a skill's source files in the canonical location, those changes are immediately visible through every symlink that points to that directory. Unlike copy-based deployment strategies that require explicit rebuild or re-sync operations, symlink-based sync ensures the configuration that target platforms read is always current.

### Safety and Idempotence with forceSymlink

The `forceSymlink` utility in [`src/utils/symlink.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/utils/symlink.ts) implements robust safety checks to make repeated sync calls reliable and non-destructive:

- **Existing symlink removal**: If the destination already exists as a symlink, it is removed and recreated, ensuring the link points to the correct source.
- **Regular file cleanup**: If the destination is a regular file, it is removed before creating the symlink.
- **Directory collision protection**: If the destination is a real directory, the operation aborts with a clear error message, preventing accidental data loss.

This idempotent design allows developers to run the sync command as often as needed—such as after every edit—without corrupting the output tree or accumulating stale files.

### Security Validation via isValidSkillName

Before creating any symlink, the plugin validates skill identifiers using `isValidSkillName` in [`src/utils/symlink.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/utils/symlink.ts). This validation blocks path-traversal attacks by ensuring only safe directory names are linked, preventing malicious skill names from escaping the intended directory structure.

## Real-Time Configuration Updates Across Target Platforms

### Platform-Specific Sync Implementations

The plugin implements target-specific sync routines that leverage the core symlink utilities:

- **[`src/sync/pi.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/pi.ts)**: Implements `syncToPi`, creating symlinks for each skill and writing the [`mcporter.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/mcporter.json) configuration file.
- **[`src/sync/droid.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/droid.ts)**: Mirrors the Pi logic for Android-based targets.
- **[`src/sync/opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/opencode.ts)**: Handles `syncToOpenCode`, symlinking skills and merging MCP server definitions into [`opencode.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/opencode.json).
- **[`src/sync/cursor.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/cursor.ts)** and **[`src/sync/codex.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/codex.ts)**: Apply the same symlink-based pattern for Cursor and Codex platforms.

Each routine calls `forceSymlink` for every skill, ensuring the target output directory contains live pointers to the canonical skill sources.

### MCP Server Configuration Merging

For platforms that consume JSON configuration files (such as Pi and OpenCode), the sync routine performs atomic configuration updates. In [`src/sync/opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/opencode.ts), the process:

1. Reads any existing [`opencode.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/opencode.json) configuration file.
2. Merges user-defined MCP server definitions with the current configuration.
3. Writes the result back atomically.

Because the JSON file is written **after** the symlinks are established, the final output folder contains both live skill links and an up-to-date configuration file. This sequencing ensures that the platform never sees a configuration file referencing skills that haven't been linked yet.

## Practical Implementation Examples

To sync a Claude home configuration to the Pi target:

```typescript
import { syncToPi } from "./src/sync/pi";
import { readClaudeHome } from "./src/parsers/claude-home";

async function main() {
  const config = await readClaudeHome("/path/to/.claude-plugin");
  await syncToPi(config, "/tmp/pi-output");
}
main();

```

To sync to the OpenCode target, which includes [`opencode.json`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/opencode.json) updates:

```typescript
import { syncToOpenCode } from "./src/sync/opencode";

await syncToOpenCode(myConfig, "/tmp/opencode-output");

```

Both functions internally call `forceSymlink` for each skill, as seen in [`src/sync/pi.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/pi.ts) at line 19:

```typescript
// Inside syncToPi (see line 19)
await forceSymlink(skill.sourceDir, target);

```

## Summary

- **Symlink-based sync** creates live pointers from canonical skill directories to platform output folders, eliminating copy latency.
- The **`forceSymlink`** utility in [`src/utils/symlink.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/utils/symlink.ts) ensures safe, idempotent link creation with protection against directory collisions.
- **Security validation** via `isValidSkillName` prevents path-traversal attacks before symlink creation.
- Platform-specific routines in [`src/sync/pi.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/pi.ts), [`src/sync/opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/sync/opencode.ts), and sibling files combine symlinking with atomic JSON configuration merging.
- Changes to skill source files are immediately reflected across all synced targets without rebuild steps.

## Frequently Asked Questions

### How does symlink-based sync differ from copy-based deployment?

Symlink-based sync creates symbolic links that point to the original skill directories, whereas copy-based deployment duplicates files into the target folder. With symlinks, any modification to the source files is instantly visible to the target platform because the filesystem resolves the link to the current state of the original directory. Copy-based approaches require explicit re-sync or rebuild operations to propagate changes, introducing latency and the risk of stale configurations.

### What happens if I run the sync command multiple times?

The sync command is designed to be idempotent and safe for repeated execution. The `forceSymlink` utility in [`src/utils/symlink.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/utils/symlink.ts) checks the destination before creating each link: existing symlinks are removed and recreated, regular files are deleted, and the operation aborts with an error if it encounters a real directory. This ensures that running the sync command after every edit updates the links without accumulating stale files or corrupting the output tree.

### Does symlink-based sync work across different filesystems or network drives?

Symlink-based sync requires that the source skill directories and the target output directories reside on the same filesystem or a filesystem that supports symbolic links. If the target directory is on a different filesystem or a network drive that does not support symlinks (such as certain FAT32 or exFAT volumes, or some network-attached storage configurations), the `forceSymlink` operation will fail. For cross-filesystem deployment scenarios, the plugin would require a copy-based fallback strategy rather than the native symlink approach.