# How to Create Custom Commands for OfficeCLI: A Complete Plugin Development Guide

> Learn how to create custom commands for OfficeCLI by following our complete plugin development guide. Implement a plugin binary and expose a JSON manifest to extend OfficeCLI functionality.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: how-to-guide
- Published: 2026-07-15

---

**You create custom commands for OfficeCLI by implementing a plugin binary that adheres to the plugin protocol, exposing a JSON manifest via `--info`, and handling subcommands like `open` through stdin/stdout request envelopes.**

OfficeCLI (iOfficeAI/OfficeCLI) is built around a core binary that discovers and delegates operations to external plugins. Each plugin implements a specific **kind** (`docx`, `xlsx`, `pptx`, etc.) and declares the file extensions it handles, allowing you to extend the CLI with domain-specific functionality without modifying the core codebase.

## Understanding the OfficeCLI Plugin Architecture

OfficeCLI follows a plugin-based architecture where the core binary locates executables in specific discovery directories and communicates with them via a standardized protocol defined in [`/plugins/plugin-protocol.md`](https://github.com/iOfficeAI/OfficeCLI/blob/main//plugins/plugin-protocol.md). When the CLI needs to perform an operation on a file, it invokes the appropriate plugin binary with subcommands like `dump`, `export`, or `open`, then exchanges JSON request envelopes over **stdin** and **stdout**.

The core guarantees that only one request is in flight at a time, serializing operations to prevent race conditions. If a plugin returns an invalid manifest or uses an unsupported protocol version, the core aborts with exit code 5, ensuring strict compatibility.

## Step-by-Step Guide to Creating Custom Commands

### Choose a Document Kind

First, determine which document format your custom command targets. The **kind** dictates the native format the plugin produces and must match the `target` field in your manifest. Available kinds include:

- `docx` for Word documents
- `xlsx` for Excel spreadsheets  
- `pptx` for PowerPoint presentations
- Custom kinds you define for specialized formats

This decision affects where you install the plugin and which files the CLI associates with your command.

### Define the Plugin Manifest

Every plugin must output a valid JSON manifest when invoked with `--info`. According to `/plugins/plugin-protocol.md#L168-L171`, the manifest requires these fields:

- `name`: Human-readable identifier for the plugin
- `version`: SemVer string (e.g., `"1.0.0"`)
- `protocol`: Integer `1` (current protocol version)
- `extensions`: Array of handled extensions (e.g., `[".docx"]`)
- `target`: Native format produced (`"docx"`, `"xlsx"`, or `"pptx"`)

```json
{
  "name": "replace-placeholder",
  "version": "1.0.0",
  "protocol": 1,
  "extensions": [".docx"],
  "target": "docx"
}

```

The core validates this manifest at `/plugins/plugin-protocol.md#L160-L173`; missing required fields cause immediate termination with exit code 5.

### Implement Subcommand Handlers

Custom commands typically implement the `open` subcommand, which starts an interactive session where the CLI sends request envelopes over stdin and expects replies over stdout. As documented in `/plugins/plugin-protocol.md#L311-L358`, your plugin must handle:

- `dump <source>`: Emit a JSON-L batch for replay operations
- `export <source> --out <target>`: Convert to a different native format
- `open <file>`: Start the interactive request/response loop

For custom operations, parse the request envelope, execute your logic (accessing properties passed via `--prop` flags), and return one of the protocol envelopes: `{"type":"ok"}`, `{"type":"error", ...}`, or a data payload.

### Package and Install the Plugin

Compile your plugin to a binary (Go, .NET, Rust, etc.) and place it in one of the discovery locations specified in `/plugins/plugin-protocol.md#L140-L144`:

1. User plugins directory: `~/.officecli/plugins/<kind>/<ext>/plugin(.exe)`
2. Bundled plugins directory: `<install-dir>/plugins/<kind>/<ext>/plugin(.exe)`

Ensure the binary is executable. The CLI scans these directories at runtime to build the available command registry.

## Complete Working Example

Here is a minimal Go plugin that implements a custom `replace-placeholder` command for Word documents:

```go
package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type Info struct {
    Name       string   `json:"name"`
    Version    string   `json:"version"`
    Protocol   int      `json:"protocol"`
    Extensions []string `json:"extensions"`
    Target     string   `json:"target"`
}

func main() {
    // Respond to `--info`
    if len(os.Args) > 1 && os.Args[1] == "--info" {
        i := Info{
            Name:       "replace-placeholder",
            Version:    "1.0.0",
            Protocol:   1,
            Extensions: []string{".docx"},
            Target:     "docx",
        }
        json.NewEncoder(os.Stdout).Encode(i)
        os.Exit(0)
    }

    // Handle `open` subcommand for custom operations
    if len(os.Args) > 1 && os.Args[1] == "open" {
        // Read request envelope from stdin (implementation omitted)
        // Perform placeholder replacement based on properties
        fmt.Fprintln(os.Stdout, `{"type":"ok"}`)
        os.Exit(0)
    }

    fmt.Fprintln(os.Stderr, "unknown command")
    os.Exit(1)
}

```

After installing to `~/.officecli/plugins/docx/.docx/plugin`, invoke your custom command:

```bash
officecli open report.docx --plugin replace-placeholder \
    --prop find=«NAME» --prop replace="Acme Corp"
officecli save report.docx

```

The CLI forwards the request envelope to your plugin, which processes the replacement and returns `ok`. The change persists when you run `officecli save` as shown in `/plugins/plugin-protocol.md#L437-L440`.

## Testing and Validation

Validate your plugin before distribution by running the `--info` check manually:

```bash
./my-plugin --info

```

Verify that the output JSON contains all required fields and that the `protocol` field equals `1`. Test the discovery mechanism by placing the binary in the correct `~/.officecli/plugins/` subdirectory and running `officecli plugins` to confirm it appears in the list.

For debugging, examine the Node entry point at [`npm/officecli.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/npm/officecli.js) to understand how the core spawns plugin processes and handles their stdout/stderr streams.

## Summary

- **OfficeCLI uses a plugin protocol** where external binaries handle document operations via stdin/stdout JSON envelopes.
- **Plugins require a manifest** output via `--info` containing `name`, `version`, `protocol: 1`, `extensions`, and `target` fields.
- **Install plugins** in `~/.officecli/plugins/<kind>/<ext>/plugin` or alongside the main binary in `<install-dir>/plugins/`.
- **Implement the `open` subcommand** to handle custom logic, reading request envelopes and returning `ok` or error responses.
- **Reference the protocol specification** at [`/plugins/plugin-protocol.md`](https://github.com/iOfficeAI/OfficeCLI/blob/main//plugins/plugin-protocol.md) for exact envelope formats and validation rules.

## Frequently Asked Questions

### What programming languages can I use to create OfficeCLI plugins?

You can use any language that compiles to a native executable or can be invoked as a script. Go, Rust, C#, and Python (with a wrapper) are common choices. The only requirement is that the binary must output valid JSON to stdout when invoked with `--info` and handle the subcommand arguments specified in the protocol.

### Where does OfficeCLI look for custom plugins?

The CLI searches two discovery locations as defined in `/plugins/plugin-protocol.md#L140-L144`: the user directory at `~/.officecli/plugins/<kind>/<ext>/plugin(.exe)` and the bundled directory at `<install-dir>/plugins/<kind>/<ext>/plugin(.exe)`. Plugins must be organized by kind and extension subdirectories.

### Why does my plugin fail with exit code 5?

Exit code 5 indicates a protocol or manifest validation error. Ensure your plugin outputs all required manifest fields (`name`, `version`, `protocol`, `extensions`, `target`) when invoked with `--info`, and verify that the `protocol` field is set to integer `1`. Missing fields or type mismatches trigger this error at `/plugins/plugin-protocol.md#L160-L173`.

### Can I create custom commands for multiple file types with one plugin?

No, each plugin binary targets a specific kind and extension combination. To support multiple formats (e.g., both `.docx` and `.xlsx`), create separate plugins or use symlinks with distinct manifests, placing each in the appropriate `~/.officecli/plugins/<kind>/<ext>/` directory structure.