Plugins

Plugins extend the Creor engine with custom tools, authentication providers, lifecycle hooks, and more. They can be npm packages, local TypeScript files, or file:// URL references.

Overview

The Creor engine has a plugin system that lets you extend its capabilities without modifying the core codebase. Plugins can:

  • Add custom tools that the agent can use
  • Register authentication providers for custom LLM endpoints
  • Hook into lifecycle events (tool execution, chat messages, system prompt generation)
  • Transform system prompts before they're sent to the model
  • React to internal bus events

Plugins are loaded during engine initialization. Each plugin exports a function that receives a context object and returns a hooks object.

Plugin Sources

Plugins can come from three sources:

SourceFormatExample
npm packagepackage-name@version"devflow-rag@latest"
Scoped npm package@scope/package@version"@myorg/creor-plugin@1.0.0"
Local filefile:// URL (auto-detected from .creor/plugins/)"file:///path/to/plugin.ts"

Creor automatically installs npm packages using Bun. Local plugins are loaded directly from the filesystem.

Configuration

Add plugins to the plugin array in your creor.json:

1
2
3
4
5
6
7
{
"plugin": [
"devflow-rag@latest",
"@myorg/custom-tools@2.0.0",
"file:///absolute/path/to/plugin.ts"
]
}

Plugins can also be auto-discovered from the .creor/plugins/ directory. Any .ts or .js file in this directory is automatically loaded as a plugin.

1
2
3
4
.creor/
plugins/
my-custom-tool.ts # auto-discovered as a plugin
lint-reporter.ts # auto-discovered as a plugin

Note

The plugin array from all config levels is concatenated, not replaced. Plugins defined globally in ~/.creor/creor.json are always included alongside project-level plugins.

Plugin API

A plugin is a function that receives an input context and returns a hooks object. The input provides access to the engine's SDK client and project information.

Plugin Input

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface PluginInput {
// Creor SDK client for interacting with the engine
client: OpencodeClient
 
// Project paths
project: string // project root directory
worktree: string // git worktree root
directory: string // engine instance directory
 
// Server URL for API calls
serverUrl: string
 
// Bun shell for running commands
$: typeof Bun.$
}

Hooks Object

The returned hooks object can implement any combination of lifecycle hooks:

HookDescription
tool.execute.beforeCalled before any tool executes. Can modify args or block execution.
tool.execute.afterCalled after a tool completes. Can add context to the response.
tool.execute.failureCalled when a tool fails.
shell.envSet environment variables for shell commands.
command.execute.beforeCalled before a slash command executes.
chat.messageCalled when a new chat message is sent.
session.startCalled when a new session begins.
session.endCalled when a session ends.
notificationCalled when the agent sends a notification.
pre.compactCalled before context compaction.
experimental.chat.system.transformTransform the system prompt before it's sent to the model.
authProvide custom authentication for LLM providers.
toolRegister custom tools for the agent.
eventReact to internal bus events.
configCalled with the resolved config after initialization.

Creating a Plugin

Here is a complete plugin that adds a custom tool and hooks into tool execution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import type { PluginInput, Hooks } from "@creor-ai/plugin"
 
export default function myPlugin(input: PluginInput): Hooks {
return {
// Register a custom tool
tool: () => [
{
name: "check_coverage",
description: "Run test coverage and report results",
parameters: {
type: "object",
properties: {
path: {
type: "string",
description: "Path to check coverage for",
},
},
required: ["path"],
},
execute: async (args) => {
const result = await input.$`npx vitest --coverage --reporter=json ${args.path}`
return {
content: [
{
type: "text",
text: result.stdout.toString(),
},
],
}
},
},
],
 
// Hook into post-tool execution
"tool.execute.after": async (hookInput, output) => {
if (hookInput.tool === "Edit") {
console.log(`File edited in session ${hookInput.sessionID}`)
}
},
 
// Transform system prompt
"experimental.chat.system.transform": async (hookInput, output) => {
output.system.push(
"Always check test coverage after making changes to src/ files."
)
},
}
}

Installing the Plugin SDK

If you're developing a plugin as an npm package, install the plugin SDK:

bun add @creor-ai/plugin

For local plugins in .creor/plugins/, Creor automatically installs @creor-ai/plugin as a dependency in the .creor/ directory. A package.json, .gitignore, and node_modules/ are created automatically.

Local Plugins

The simplest way to extend Creor is with local plugins in your project's .creor/plugins/ directory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// .creor/plugins/notify-slack.ts
import type { PluginInput, Hooks } from "@creor-ai/plugin"
 
export default function slackNotifier(input: PluginInput): Hooks {
const webhookUrl = process.env.SLACK_WEBHOOK_URL
 
return {
notification: async (hookInput) => {
if (!webhookUrl) return
await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `Creor: ${(hookInput as any).message}`,
}),
})
},
}
}

Local plugins are loaded as file:// URLs internally. They have full access to the Node.js runtime and can import any packages installed in the .creor/node_modules/ directory.

Tip

Add external dependencies by creating a .creor/package.json with a dependencies field. Creor runs bun install automatically when it detects a package.json in the .creor/ directory.

Built-in Plugins

Creor ships with several built-in plugins that are loaded automatically:

PluginDescription
opencode-anthropic-authHandles Anthropic OAuth authentication flows
Codex Auth (internal)OpenAI Codex / Responses API authentication
Copilot Auth (internal)GitHub Copilot authentication integration

To disable built-in plugins, set the CREOR_DISABLE_DEFAULT_PLUGINS environment variable to true.

Plugin Deduplication

When the same plugin is referenced at multiple config levels (e.g., global and project), Creor deduplicates by canonical name. The higher-priority source wins.

Priority order (highest to lowest):

  • Project .creor/plugins/ directory
  • Project creor.json plugin array
  • Global .creor/plugins/ directory
  • Global creor.json plugin array

For example, if ~/.creor/creor.json lists my-plugin@1.0.0 and .creor/creor.json lists my-plugin@2.0.0, only version 2.0.0 is loaded.