What it is

A hook is a function that runs automatically when a specific event happens inside OpenCode. Instead of polling for changes or manually triggering actions, you subscribe to events like "file edited" or "tool executed" and your code runs at exactly the right moment.

What it is in real OpenCode terms

In OpenCode, hooks are objects returned by a plugin's main function. Each key in the object is an event name, and each value is a callback function. When OpenCode emits that event, your callback runs.

For example, a plugin might return a hooks object with a "tool.execute.after" key. Every time any tool finishes running, your function is called with the tool's result. You can inspect it, log it, or even modify the output before it reaches the LLM.

Why it exists

Anatomy / main elements

A hook object is just key-value pairs. The key is the event name; the value is an async function.

return {
  hooks: {
    "file.edited": async (event) => {
      console.log("File changed:", event.path);
    },
    "tool.execute.after": async (event) => {
      // inspect the result after the tool ran
      console.log("Tool:", event.tool, "Result length:", event.result.length);
    },
  },
};

Available event categories include:

Where it lives

Hooks live inside plugin files. A plugin is a JavaScript or TypeScript module placed in:

The plugin's main function returns an object with a hooks key. OpenCode reads that and wires up the event listeners.

How to create one

Create a file in your plugins directory, export a default function, and return hooks:

// .opencode/plugins/my-hook.ts
export default function myPlugin({ project, client, $, directory, worktree }) {
  return {
    hooks: {
      "session.created": async () => {
        console.log("New session started in", directory);
      },
    },
  };
}

Restart OpenCode (or reload) and the hook is active.

Minimal example

A plugin that logs every file edit and injects a custom environment variable into the shell:

// .opencode/plugins/log-and-env.ts
export default function({ directory }) {
  return {
    hooks: {
      "file.edited": async (event) => {
        console.log(`[edit] ${event.path}`);
      },
      "shell.env": async () => {
        return { MY_VAR: "hello from hook" };
      },
    },
  };
}

Related concepts

Common confusion

Do hooks block OpenCode?
Hooks run in sequence from all loaded plugins. Keep them fast. If a hook takes too long, it delays the event pipeline.
Can I have multiple hooks for the same event?
Yes. Each plugin can define a hook for the same event. They run in load order (global config first, then project config, then plugin directories).
What is experimental.session.compacting?
A special hook that lets you customize what happens during context compaction. It is experimental and its API may change.