Skip to main content

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:

costrict.json
{
"$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

  1. Global config (~/.config/costrict/costrict.json)
  2. Project config (costrict.json)
  3. Global plugins directory (~/.config/costrict/plugins/)
  4. 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

.costrict/plugins/example.js
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
return {
// Implement event hooks here
}
}

Context parameter reference:

  • project — Current project information
  • directory — Current working directory
  • worktree — Git worktree path
  • client — CoStrict SDK client
  • $ — Bun Shell API for executing commands

TypeScript Support

.costrict/plugins/my-plugin.ts
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:

.costrict/package.json
{
"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 execution
  • tool.execute.after — Fires after a tool executes

Session Events

  • session.created — Session created
  • session.idle — Session idle (AI finished responding)
  • session.compacted — Session was compacted
  • session.deleted — Session was deleted
  • session.error — Session encountered an error

File Events

  • file.edited — A file was edited
  • file.watcher.updated — File watcher detected a change

TUI Events

  • tui.prompt.append — Append content to the prompt input
  • tui.command.execute — Execute a TUI command
  • tui.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 changes
  • permission.asked / permission.replied — Permission requests
  • lsp.updated / lsp.client.diagnostics — LSP status
  • command.executed — Command executed
  • experimental.session.compacting — Before session compaction (experimental)

Plugin Examples

Session Completion Notification

.costrict/plugins/notification.js
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:

.costrict/plugins/env-protection.js
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:

.costrict/plugins/inject-env.js
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

.costrict/plugins/custom-tools.ts
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:

.costrict/plugins/compaction.ts
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.