# How the `--permissions` Flag Works in Compound-Plugin Conversion

> Understand the --permissions flag in compound-plugin conversion for generating tool access rights. Explore none, broad, and from-commands modes to control access effectively.

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

---

**The `--permissions` flag controls how tool access rights are generated during compound-plugin conversion, offering three modes: `none` (no permissions), `broad` (allow all tools), or `from-commands` (inherit from Claude command definitions).**

When converting Claude Desktop plugins to OpenCode and other target formats using the `EveryInc/compound-engineering-plugin` repository, the `--permissions` flag determines whether the generated plugin is permissive, restrictive, or inherits fine-grained tool allowances defined in the original Claude commands.

## Understanding the `--permissions` Flag in Compound-Plugin Conversion

The `--permissions` flag configures **OpenCode permission settings** (and equivalent settings for other target formats that reuse the same conversion logic). According to the source code in [`src/converters/claude-to-opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/converters/claude-to-opencode.ts), the flag accepts one of three string values defined by the `PermissionMode` type:

- `"none"` – Generates no permission entries
- `"broad"` – Grants blanket access to all available tools
- `"from-commands"` – Extracts specific tool permissions from each Claude command's `allowedTools` declarations

## How the `--permissions` Flag Is Parsed and Validated

The flag processing begins in **[`src/commands/convert.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/commands/convert.ts)**, where the CLI argument is validated against a strict allowlist:

```typescript
const permissionModes: PermissionMode[] = ["none", "broad", "from-commands"];

const permissions = String(args.permissions);
if (!permissionModes.includes(permissions as PermissionMode)) {
  throw new Error(`Unknown permissions mode: ${permissions}`);
}

const options = {
  // …
  permissions: permissions as PermissionMode, // Passed to all converters
};

```

This validation ensures that only supported permission modes reach the conversion logic. The `options` object containing the validated `permissions` value is then forwarded to every target converter (OpenCode, Codex, Droid, Cursor, Pi, and Gemini).

## Three Permission Modes Explained

Inside **[`src/converters/claude-to-opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/converters/claude-to-opencode.ts)**, the `applyPermissions` function implements the logic for each mode. The function receives the OpenCode configuration object, the array of Claude commands, and the selected `PermissionMode`.

### none Mode

When `mode === "none"`, the function returns immediately without modifying the configuration:

```typescript
if (mode === "none") return; // No permission entries added

```

This produces an OpenCode plugin with no `permission` or `tools` sections, effectively requiring users to manually configure access rights after installation.

### broad Mode

When `mode === "broad"`, the converter enables every tool in the `sourceTools` array (which includes `"read"`, `"write"`, `"edit"`, `"bash"`, and others):

```typescript
if (mode === "broad") {
  enabled = new Set(sourceTools);
}

// Later in the function:
for (const tool of sourceTools) {
  tools[tool] = true;           // All tools enabled
  permission[tool] = "allow";   // All permissions set to "allow"
}

```

This generates a permissive plugin where all tools are allowed by default, suitable for internal or trusted environments.

### from-commands Mode

The most granular mode, `from-commands`, parses the `allowedTools` property from each Claude command definition:

```typescript
// Collect allowedTools from each command
for (const command of commands) {
  if (!command.allowedTools) continue;
  for (const tool of command.allowedTools) {
    const parsed = parseToolSpec(tool);
    if (!parsed.tool) continue;
    enabled.add(parsed.tool);
    if (parsed.pattern) {
      const normalizedPattern = normalizePattern(parsed.tool, parsed.pattern);
      if (!patterns[parsed.tool]) patterns[parsed.tool] = new Set();
      patterns[parsed.tool].add(normalizedPattern);
    }
  }
}

```

This mode supports **fine-grained pattern matching**. For example, a Claude command might specify `write:src/**/*.md`, which the converter translates into a permission object like:

```json
{
  "write": {
    "*": "deny",
    "src/**/*.md": "allow"
  }
}

```

The `from-commands` mode ensures the converted plugin respects the original security boundaries defined by the plugin author.

## Code Examples for Using `--permissions`

### CLI Usage Examples

Convert a plugin with different permission modes:

```bash

# Default behavior (broad permissions)

bunx @every-env/compound-plugin convert ./plugins/compound-engineering --to opencode

# Restrictive mode - no tools allowed

bunx @every-env/compound-plugin convert ./plugins/compound-engineering --to opencode --permissions none

# Inherit permissions from Claude command definitions

bunx @every-env/compound-plugin convert ./plugins/compound-engineering --to opencode --permissions from-commands

```

### Internal Implementation Details

The validation logic in [`src/commands/convert.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/commands/convert.ts) ensures type safety:

```typescript
const permissionModes: PermissionMode[] = ["none", "broad", "from-commands"];

const permissions = String(args.permissions);
if (!permissionModes.includes(permissions as PermissionMode)) {
  throw new Error(`Unknown permissions mode: ${permissions}`);
}

```

The `applyPermissions` helper in [`src/converters/claude-to-opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/converters/claude-to-opencode.ts) handles the three modes:

```typescript
function applyPermissions(
  config: OpenCodeConfig,
  commands: ClaudeCommand[],
  mode: PermissionMode,
) {
  if (mode === "none") return;
  
  const sourceTools = ["read", "write", "edit", "bash"];
  const enabled = new Set<string>();
  const patterns: Record<string, Set<string>> = {};
  
  if (mode === "broad") {
    sourceTools.forEach(t => enabled.add(t));
  } else {
    // from-commands logic
    commands.forEach(cmd => {
      cmd.allowedTools?.forEach(tool => {
        const parsed = parseToolSpec(tool);
        if (parsed.tool) enabled.add(parsed.tool);
        if (parsed.pattern) {
          patterns[parsed.tool] = patterns[parsed.tool] || new Set();
          patterns[parsed.tool].add(normalizePattern(parsed.tool, parsed.pattern));
        }
      });
    });
  }
  
  // Build final config...
}

```

## Summary

- The `--permissions` flag in compound-plugin conversion controls how tool access rights are generated for OpenCode and other target formats.
- Three modes are available: **`none`** (no permissions), **`broad`** (allow all tools), and **`from-commands`** (inherit from Claude command definitions).
- The flag is validated in [`src/commands/convert.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/commands/convert.ts) and processed by the `applyPermissions` helper in [`src/converters/claude-to-opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/converters/claude-to-opencode.ts).
- **`from-commands`** mode supports fine-grained pattern matching (e.g., `write:src/**/*.md`) for security-sensitive conversions.
- All target converters (Codex, Droid, Cursor, Pi, Gemini) reuse the OpenCode permission logic for consistency.

## Frequently Asked Questions

### What is the default value for the --permissions flag in compound-plugin conversion?

If you omit the `--permissions` flag, the converter defaults to **`broad`** mode. This means the generated OpenCode plugin will have all tools enabled (`"allow"`) and the `tools` map set to `true` for every available tool, creating a permissive configuration suitable for trusted environments.

### How does the from-commands permission mode handle tool patterns?

The `from-commands` mode parses tool specifications that include path patterns (e.g., `write:src/**/*.md` or `read:docs/**`). When the `applyPermissions` function in [`src/converters/claude-to-opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/converters/claude-to-opencode.ts) encounters these patterns, it normalizes them and constructs a fine-grained permission object where specific paths can be allowed while others are denied, preserving the security boundaries defined in the original Claude plugin.

### Can I use the --permissions flag when converting to formats other than OpenCode?

Yes. While the permission logic is implemented in [`src/converters/claude-to-opencode.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/converters/claude-to-opencode.ts), other target converters—including those for Codex, Droid, Cursor, Pi, and Gemini—delegate to the same `applyPermissions` helper or reuse the OpenCode conversion as an intermediate step. This ensures that the `--permissions` flag behaves consistently across all output formats, generating the appropriate access controls for each target platform.

### What happens if I provide an invalid value to the --permissions flag?

The converter validates the flag value in [`src/commands/convert.ts`](https://github.com/EveryInc/compound-engineering-plugin/blob/main/src/commands/convert.ts) against the `permissionModes` array, which only accepts `"none"`, `"broad"`, or `"from-commands"`. If you provide any other value, the CLI throws an error with the message `Unknown permissions mode: ${permissions}` and halts execution, preventing invalid configurations from reaching the conversion logic.