OfficeCLI Core Plugins Extensibility: 6 Mechanisms for Extending the CLI

OfficeCLI Core Plugins extensibility is achieved through a language-agnostic, process-isolated architecture that enables external executables to register new command verbs, handle file extensions via manifest declarations, and execute in both short-lived and persistent session modes.

The iOfficeAI/OfficeCLI repository implements a modular plugin framework in the Core/Plugins directory that allows developers to extend CLI functionality without modifying the core codebase. This system supports automatic discovery, manifest-based capability validation, and subprocess isolation for handling diverse document operations and data transformations.

Plugin Discovery and Registration

The extensibility system automatically locates plugins using a hierarchical directory structure scanned by PluginRegistry.cs in src/officecli/Core/Plugins/PluginRegistry.cs. This class resolves plugins by kind (command verb) and file extension, caching ResolvedPlugin objects for performance.

Plugins reside in either user-wide (~/.officecli/plugins) or application-wide ($APPDIR/plugins) directories. The registry expects a specific layout:


<root>/<kind>/<ext>/plugin(.exe)

When scanning, the registry executes each plugin with the --info flag to retrieve its manifest, validates the protocol version, and registers the executable. This discovery mechanism enables zero-configuration installation—simply placing a plugin in the correct directory makes it available to the CLI.

Manifest-Driven Capability Declaration

Each plugin declares its capabilities through a JSON manifest processed by PluginManifest.cs in src/officecli/Core/Plugins/PluginManifest.cs. The deserializer validates required fields including protocol, kinds, vocabulary, and targetExtension.

The manifest serves as the contract between the CLI and the plugin:

  • kinds: Array of verbs the plugin implements (e.g., get, set, save)
  • targetExtension: Native format for format-handler plugins (e.g., .docx, .pdf)
  • vocabulary: Optional metadata enabling richer UI/help output
  • protocol: Version identifier ensuring compatibility with the CLI

The main process rejects manifests with mismatched protocol versions or missing required fields, emitting warnings during discovery to prevent runtime failures.

Process Isolation Architecture

OfficeCLI employs two distinct execution models to balance safety and performance, both implemented in the Core/Plugins layer to protect the host process from plugin crashes.

Short-Lived Plugin Execution

For transient operations, PluginProcess.cs in src/officecli/Core/Plugins/PluginProcess.cs spawns the plugin executable in a subprocess with configurable idle timeouts. The class enforces the plugin_idle_timeout setting (overridable via the OFFICECLI_PLUGIN_IDLE_TIMEOUT environment variable), killing unresponsive processes to protect the CLI from hangs or crashes.

Persistent Format Handler Sessions

Complex document operations require stateful interaction. The FormatHandlerSession class in src/officecli/Core/Plugins/FormatHandlerSession.cs maintains a persistent pipe to format-handler plugins. When opening a document, the CLI spawns the plugin, sends an open handshake, then streams commands like get and set over the pipe until the session closes.

Command Verb Routing

The CLI delegates operations to plugins through a resolution system defined in DocumentHandlerFactory.cs in src/officecli/Handlers/DocumentHandlerFactory.cs. When processing a command like officecli myfile.myext get, the factory calls PluginRegistry.ResolvePlugin(kind, ext).

If a matching plugin exists, the CLI forwards the verb to the plugin executable; otherwise, it falls back to built-in handlers or reports "unsupported". This routing mechanism allows developers to add new verbs by simply listing them in the manifest's kinds array and implementing the corresponding logic in the plugin executable.

Runtime Configuration Options

Plugins expose custom runtime settings through the manifest or environment variables. The plugin-runner concept (utilized by McpInstaller.cs) allows specification of execution bridges like mcpBridge for alternative execution contexts.

Configuration options include:

  • idleTimeout: Per-plugin timeout overrides for long-running operations
  • runner: Alternative execution contexts (e.g., for MCP integration)

The installer writes a manifest.json and optional bridge configuration, enabling the main process to invoke the plugin via the specified runner.

Implementation Examples

The following snippets demonstrate how to interact with the plugin system using the Core/Plugins classes:

// Resolve a plugin for a given kind and file extension
var registry = new PluginRegistry();
ResolvedPlugin? plugin = registry.ResolvePlugin(kind: "format-handler", ext: ".docx");
if (plugin == null) {
    Console.WriteLine("No plugin found for .docx");
    return;
}
// Start a short-lived plugin process with timeout protection
var proc = new PluginProcess(plugin.ExecutablePath);
var result = await proc.RunAsync(new[] { "dump-reader", "--info" });
Console.WriteLine(result.StdOut);
// Open a persistent session with a format-handler plugin
using var session = new FormatHandlerSession(
    filePath: "report.docx",
    plugin: plugin
);
await session.SendAsync(new { verb = "open", args = new {} });
var getResult = await session.SendAsync(new { verb = "get", args = new { path = "body/text" }});
Console.WriteLine(getResult);

Summary

  • Plugin Discovery: Automatic registration via PluginRegistry.cs scanning ~/.officecli/plugins and $APPDIR/plugins with layout <root>/<kind>/<ext>/plugin(.exe)
  • Manifest Validation: JSON manifests declare capabilities through PluginManifest.cs, requiring protocol, kinds, and targetExtension fields
  • Process Isolation: Short-lived plugins run via PluginProcess.cs with configurable timeouts; long-running operations use FormatHandlerSession.cs for persistent pipes
  • Verb Extension: New commands are added by implementing verbs in the manifest's kinds array, routed through DocumentHandlerFactory.cs
  • Configuration: Runtime behavior is controlled via manifest fields or the OFFICECLI_PLUGIN_IDLE_TIMEOUT environment variable
  • Language Agnostic: External executables communicate via stdin/stdout streams, making the system compatible with any programming language

Frequently Asked Questions

How do I add support for a new file type in OfficeCLI?

Place your plugin executable in the directory structure <root>/format-handler/<ext>/plugin.exe (where <root> is either ~/.officecli/plugins or $APPDIR/plugins). Declare the targetExtension in your manifest's JSON output when invoked with --info. The PluginRegistry will automatically discover and register the handler for that extension.

What is the difference between short-lived and long-running plugins?

Short-lived plugins are executed via PluginProcess.cs for single operations like metadata extraction, with automatic termination after plugin_idle_timeout. Long-running plugins maintain persistent connections through FormatHandlerSession.cs, enabling stateful document editing via streaming commands over pipes.

How can I implement custom command verbs beyond get, set, and save?

The manifest's kinds array accepts any verb string. When you run officecli <file> <verb>, the DocumentHandlerFactory resolves the plugin by matching the verb against registered kinds and the file extension. Implement the verb logic in your plugin executable to handle the specific command.

How does the system prevent plugin crashes from affecting the core CLI?

Each plugin runs in its own subprocess managed by PluginProcess.cs or FormatHandlerSession.cs. The CLI monitors these processes and enforces idle timeouts. If a plugin crashes or hangs, the parent process kills the subprocess and returns an error, protecting the CLI's stability.

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 →