Plugins
Plugins extend CoStrict's functionality by hooking into various events. They can add new features, integrate external services, or modify default behavior.
Loading Plugins
From Local Files
Place JavaScript or TypeScript files in a plugins directory — they are loaded automatically at startup:
- Project-level:
.costrict/plugins/ - Global:
~/.config/costrict/plugins/
From npm
Specify npm packages in your config file:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-helicone-session", "opencode-wakatime", "@my-org/custom-plugin"]
}
npm plugins are automatically installed using Bun at startup, cached in ~/.cache/costrict/node_modules/.
Load Order
- Global config (
~/.config/costrict/costrict.json) - Project config (
costrict.json) - Global plugins directory (
~/.config/costrict/plugins/) - Project plugins directory (
.costrict/plugins/)
Writing Plugins
A plugin is a JS/TS module that exports one or more functions. Each function receives a context object and returns event hooks.
Basic Structure
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
return {
// Implement event hooks here
}
}
Context parameter reference:
project— Current project informationdirectory— Current working directoryworktree— Git worktree pathclient— CoStrict SDK client$— Bun Shell API for executing commands
TypeScript Support
import type { Plugin } from "@opencode-ai/plugin"
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
// Type-safe hook implementations
}
}
Plugin Dependencies
If a local plugin requires external npm packages, add a package.json to the config directory:
{
"dependencies": {
"shescape": "^2.1.0"
}
}
CoStrict automatically runs bun install at startup.
Available Events
Tool Events
tool.execute.before— Fires before a tool executes; can modify arguments or prevent executiontool.execute.after— Fires after a tool executes
Session Events
session.created— Session createdsession.idle— Session idle (AI finished responding)session.compacted— Session was compactedsession.deleted— Session was deletedsession.error— Session encountered an error
File Events
file.edited— A file was editedfile.watcher.updated— File watcher detected a change
TUI Events
tui.prompt.append— Append content to the prompt inputtui.command.execute— Execute a TUI commandtui.toast.show— Show a toast notification
Shell Events
shell.env— Inject environment variables into the shell execution environment
Other Events
message.updated/message.removed— Message changespermission.asked/permission.replied— Permission requestslsp.updated/lsp.client.diagnostics— LSP statuscommand.executed— Command executedexperimental.session.compacting— Before session compaction (experimental)
Plugin Examples
Session Completion Notification
export const NotificationPlugin = async ({ $ }) => {
return {
event: async ({ event }) => {
if (event.type === "session.idle") {
// macOS notification
await $`osascript -e 'display notification "Session complete!" with title "CoStrict"'`
}
},
}
}
Protect .env Files
Prevent the LLM from reading .env files:
export const EnvProtection = async () => {
return {
"tool.execute.before": async (input, output) => {
if (input.tool === "read" && output.args.filePath.includes(".env")) {
throw new Error("Reading .env files is not allowed")
}
},
}
}
Inject Environment Variables
Inject environment variables into all shell executions:
export const InjectEnvPlugin = async () => {
return {
"shell.env": async (input, output) => {
output.env.MY_API_KEY = "secret"
output.env.PROJECT_ROOT = input.cwd
},
}
}
Define Custom Tools in a Plugin
import { type Plugin, tool } from "@opencode-ai/plugin"
export const CustomToolsPlugin: Plugin = async (ctx) => {
return {
tool: {
mytool: tool({
description: "Example custom tool",
args: {
message: tool.schema.string().describe("Message content"),
},
async execute(args, context) {
return `Received message: ${args.message}, directory: ${context.directory}`
},
}),
},
}
}
Custom Compaction Prompt
Inject additional information when the session context is compacted:
import type { Plugin } from "@opencode-ai/plugin"
export const CompactionPlugin: Plugin = async () => {
return {
"experimental.session.compacting": async (input, output) => {
output.context.push(`
## Custom Context
Record important state to preserve after compaction:
- Current task progress
- Key decisions made
- List of files being modified
`)
},
}
}
Set output.prompt to completely replace the default compaction prompt.