Skip to main content

Adding Plugins

Plugins are static manifest files (TypeScript data objects) that declare which skills and tools they bundle. They are NOT executable code. The plugin system is a thin packaging layer over existing skills and tools -- it handles installation, registry tracking, and exec-approvals regeneration.

This guide explains how to create a new plugin manifest and integrate it into the xops.bot plugin system.

Plugin Architecture

A plugin is a TypeScript object that satisfies the PluginManifest schema. It declares:

  • Skills it bundles (by skill directory name)
  • Tools it bundles (by tool definition name)
  • Workspaces where skills are placed (agent workspace names)
  • Dependencies on other plugins (auto-resolved during install)
  • Required binaries that must be present for tools to function
  • Optional binaries that enhance functionality but are not mandatory

The CLI uses this manifest to copy skills, update the registry, and regenerate exec-approvals. No dynamic loading or code execution occurs -- plugins are purely declarative.

Plugin Manifest Schema

The PluginManifest type is defined by a Zod schema in xopsbot/plugins/schema.ts:

FieldTypeRequiredDescription
namestringYesPlugin identifier (e.g., "kubernetes", "docker"). Must be unique.
versionstringYesSemantic version (e.g., "1.0.0").
descriptionstringYesHuman-readable summary of what the plugin provides.
skillsstring[]YesSkill directory names to bundle (e.g., ["k8s-deploy", "k8s-debug"]).
toolsstring[]YesTool definition names to bundle (e.g., ["kubectl"]).
workspacesstring[]YesWorkspace names where skills are installed (e.g., ["k8s-agent"]).
dependenciesstring[]YesOther plugin names that must be installed first. Use [] for no dependencies.
requiredBinsstring[]YesBinary names that must be present for core functionality.
optionalBinsstring[]NoBinary names that enhance the plugin but are not mandatory. Defaults to [].

Creating a New Plugin

Follow this checklist to add a plugin:

Step 1: Create the manifest file

Create xopsbot/plugins/definitions/{name}.ts:

import type { PluginManifest } from '../schema';

/**
* My Plugin manifest.
* Brief description of what it bundles.
*/
export const myPlugin = {
name: 'my-plugin',
version: '1.0.0',
description: 'What this plugin provides',
skills: ['skill-a', 'skill-b'],
tools: ['tool-a'],
workspaces: ['my-agent'],
dependencies: [],
requiredBins: ['tool-binary'],
optionalBins: [],
} as const satisfies PluginManifest;

Use as const satisfies PluginManifest to preserve literal types while ensuring schema compliance. This matches the pattern used by tool definitions.

Step 2: Add to collections

In xopsbot/plugins/index.ts, add the export and include in ALL_PLUGINS and PLUGIN_MAP:

// Export the definition
export { myPlugin } from './definitions/my-plugin';

// Import for collections
import { myPlugin } from './definitions/my-plugin';

// Add to ALL_PLUGINS array
export const ALL_PLUGINS: PluginManifest[] = [
kubernetesPlugin,
dockerPlugin,
awsPlugin,
terraformPlugin,
observabilityPlugin,
myPlugin, // <-- add here
];

PLUGIN_MAP is derived automatically from ALL_PLUGINS via Object.fromEntries, so adding to the array is sufficient.

Step 3: Verify referenced assets exist

Ensure all skills, tools, and workspaces referenced by the manifest exist:

  • Skills: Each skill in the skills array must have a directory in xopsbot/skills/{skill-name}/ containing a SKILL.md file.
  • Tools: Each tool in the tools array must have a definition in xopsbot/tools/definitions/{tool}.ts and be included in the ALL_TOOLS array.
  • Workspaces: Each workspace in the workspaces array must exist in xopsbot/workspaces/{workspace}/.

Step 4: Add tests (if plugin has dependencies)

If your plugin declares dependencies, add test coverage for the dependency resolution path:

import { resolveDependencies } from '../resolve-dependencies';

test('my-plugin resolves observability dependency', () => {
const plugins = new Map([
['observability', observabilityPlugin],
['my-plugin', myPlugin],
]);
const installed = new Set<string>();
const order = resolveDependencies('my-plugin', plugins, installed);
expect(order).toEqual(['observability', 'my-plugin']);
});

Step 5: Update documentation

Add the new plugin to the user guide at docs-site/docs/user-guide/plugins.md:

  • Add a row to the "Available Plugins" table
  • Add a description paragraph under the table
  • Add a row to the "Required Binaries" table

Example Plugin

A complete example of a hypothetical "monitoring" plugin that depends on the observability plugin:

import type { PluginManifest } from '../schema';

/**
* Monitoring plugin manifest.
* Bundles Grafana dashboard operations with observability tools.
* Depends on the observability plugin for Prometheus and Loki access.
*/
export const monitoringPlugin = {
name: 'monitoring',
version: '1.0.0',
description: 'Monitoring stack: Grafana dashboards and alerting',
skills: ['grafana-ops'],
tools: ['grafana'],
workspaces: ['rca-agent'],
dependencies: ['observability'],
requiredBins: ['grafana-cli'],
optionalBins: [],
} as const satisfies PluginManifest;

This plugin:

  • Bundles a grafana-ops skill and grafana tool
  • Targets the rca-agent workspace (same as observability)
  • Depends on observability -- installing "monitoring" auto-installs "observability" first
  • Requires the grafana-cli binary

Plugin Registry

The plugin registry tracks installation state in ~/.xopsbot/plugins/registry.json:

{
"version": 1,
"plugins": {
"kubernetes": {
"installed": "2026-02-06T10:00:00Z",
"enabled": true,
"version": "1.0.0",
"source": "builtin"
},
"docker": {
"installed": "2026-02-06T10:01:00Z",
"enabled": false,
"version": "1.0.0",
"source": "builtin"
}
}
}

Registry fields per plugin:

FieldDescription
installedISO timestamp of installation
enabledWhether the plugin's tools are active in exec-approvals
versionInstalled manifest version
sourceAlways "builtin" for bundled plugins

How operations affect the registry:

OperationEffect
installAdds entry with enabled: true
removeDeletes entry entirely
enableSets enabled: true, regenerates exec-approvals
disableSets enabled: false, regenerates exec-approvals

The registry file is created automatically on first install. If the file does not exist, operations treat it as an empty registry.

Existing Plugins Reference

The five built-in plugins and their manifest data:

PluginSkillsToolsWorkspacesRequired BinsOptional Bins
kubernetes211kubectl--
docker111docker--
aws112aws--
terraform221terraformansible, ansible-playbook
observability432promtool, logclicurl

Totals: 10 skills, 8 tools, 5 workspaces across all plugins.

Source files: xopsbot/plugins/definitions/ (one file per plugin).

Checklist

Before submitting a new plugin, verify:

  • Manifest file created in xopsbot/plugins/definitions/{name}.ts
  • Uses as const satisfies PluginManifest for type safety with literal preservation
  • All referenced skills exist in xopsbot/skills/
  • All referenced tools exist in xopsbot/tools/definitions/
  • All referenced workspaces exist in xopsbot/workspaces/
  • Added to ALL_PLUGINS array in xopsbot/plugins/index.ts
  • Dependencies are valid -- all dependency names are existing plugins
  • Tests added if plugin has dependencies
  • User guide updated at docs-site/docs/user-guide/plugins.md