opencode-hooks
v1.1.0
Published
OpenCode hooks plugin - all hooks from oh-my-opencode without agents/tools/MCPs
Downloads
192
Maintainers
Readme
Opencode Hooks
A minimal fork of oh-my-opencode containing only the hook infrastructure - the Claude Code compatible hook system for OpenCode plugins.
TABLE OF CONTENTS
- 📖 OVERVIEW
- 🔧 HOOK EVENTS
- ✅ INSTALLATION & SETUP
- 📦 USAGE PATTERNS
- ⚙️ CONFIGURATION
- 🔨 API REFERENCE
- 📋 WHAT'S INCLUDED VS EXCLUDED
- 📚 RESOURCES
1. 📖 OVERVIEW
What This Package Provides
This package extracts the hook infrastructure from oh-my-opencode, giving you Claude Code compatible hooks without the full plugin's agents, tools, and MCP integrations.
┌─────────────────────────────────────────────────────────────────────────────┐
│ HOOK EXECUTION FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User Prompt → UserPromptSubmit hooks → Claude processes │
│ │ │
│ ▼ │
│ Tool Call Requested │
│ │ │
│ ▼ │
│ PreToolUse hooks │
│ (can block/modify) │
│ │ │
│ ▼ │
│ Tool Executes │
│ │ │
│ ▼ │
│ PostToolUse hooks │
│ (can modify output) │
│ │ │
│ ▼ │
│ Session Idle │
│ │ │
│ ▼ │
│ Stop hooks │
│ (can inject prompts) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘Why Use This?
| Use Case | Full oh-my-opencode | oh-my-opencode-hooks | | -------------------------- | ------------------- | -------------------- | | Claude Code hook compat | ✅ | ✅ | | Custom agents (OmO, etc.) | ✅ | ❌ | | MCP integrations | ✅ | ❌ | | Minimal dependencies | ❌ (7+ deps) | ✅ (1 dep) | | Bundle size | ~500KB | ~48KB | | Build your own hook plugin | Overkill | ✅ Perfect |
2. 🔧 HOOK EVENTS
The Four Hook Types
| Hook Event | When It Fires | Can Block? | Can Modify? |
| ------------------ | ------------------------ | ---------- | ---------------- |
| PreToolUse | Before tool execution | ✅ Yes | ✅ Tool input |
| PostToolUse | After tool execution | ⚠️ Warn | ✅ Tool output |
| UserPromptSubmit | When user submits prompt | ✅ Yes | ✅ Inject content |
| Stop | When session goes idle | ✅ Yes | ✅ Inject prompt |
Hook Input/Output Structure
// PreToolUse - Runs before each tool call
interface PreToolUseInput {
session_id: string
tool_name: string // e.g., "Edit", "Bash", "Read"
tool_input: Record<string, unknown>
cwd: string
hook_event_name: "PreToolUse"
}
// Hook can return:
// - exit code 0: allow
// - exit code 1: ask user
// - exit code 2: deny/block
// PostToolUse - Runs after each tool call
interface PostToolUseInput {
session_id: string
tool_name: string
tool_input: Record<string, unknown>
tool_response: { title?: string; output?: string }
transcript_path?: string // Full session transcript
cwd: string
hook_event_name: "PostToolUse"
}
// UserPromptSubmit - Runs when user sends a message
interface UserPromptSubmitInput {
session_id: string
prompt: string
cwd: string
hook_event_name: "UserPromptSubmit"
}
// Stop - Runs when session becomes idle
interface StopInput {
session_id: string
stop_hook_active: boolean
todo_path?: string // Path to todo file
cwd: string
hook_event_name: "Stop"
}3. ✅ INSTALLATION & SETUP
Installation
# Using bun (recommended)
bun add oh-my-opencode-hooks
# Using npm
npm install oh-my-opencode-hooks
# Local development (already built)
# Just reference the dist folder in opencode.jsonOpenCode Configuration
Add to your opencode.json:
{
"plugin": [
".opencode/oh-my-opencode-hooks/dist/index.js"
]
}Build From Source
cd .opencode/oh-my-opencode-hooks
bun install
bun run build4. 📦 USAGE PATTERNS
Pattern 1: Direct Plugin Export (Simplest)
Use the default export as your OpenCode plugin:
import OhMyOpenCodeHooksPlugin from "oh-my-opencode-hooks"
// This enables all Claude Code hooks automatically
export default OhMyOpenCodeHooksPluginPattern 2: Hook Factory (Recommended)
Use the hook factory for more control:
import type { Plugin } from "@opencode-ai/plugin"
import { createClaudeCodeHooksHook } from "oh-my-opencode-hooks"
const MyPlugin: Plugin = async (ctx) => {
// Create hooks with optional configuration
const claudeHooks = createClaudeCodeHooksHook(ctx, {
// Disable specific hook types if needed
disabledHooks: ["Stop"] // or true to disable all
})
return {
"chat.message": claudeHooks["chat.message"],
"tool.execute.before": claudeHooks["tool.execute.before"],
"tool.execute.after": claudeHooks["tool.execute.after"],
event: claudeHooks.event,
// Add your own hooks alongside
config: async (config) => {
// Your config modifications
}
}
}
export default MyPluginPattern 3: Individual Hook Executors (Advanced)
Execute hooks manually for custom integrations:
import {
executePreToolUseHooks,
executePostToolUseHooks,
executeUserPromptSubmitHooks,
executeStopHooks,
loadClaudeHooksConfig,
loadPluginExtendedConfig
} from "oh-my-opencode-hooks"
// Load hook configurations
const claudeConfig = await loadClaudeHooksConfig()
const extendedConfig = await loadPluginExtendedConfig()
// Execute PreToolUse hooks
const preResult = await executePreToolUseHooks({
sessionId: "session-123",
toolName: "Edit",
toolInput: { file_path: "/src/index.ts", content: "..." },
cwd: process.cwd(),
}, claudeConfig, extendedConfig)
if (preResult.decision === "deny") {
console.log("Blocked by hook:", preResult.reason)
// Don't execute the tool
}
if (preResult.modifiedInput) {
// Use modified input instead
}Pattern 4: Message Injection
Inject synthetic messages into the conversation:
import { injectHookMessage } from "oh-my-opencode-hooks"
// Inject a message that appears as if the user sent it
const success = injectHookMessage(
sessionID,
"<system-reminder>Remember to follow the coding standards.</system-reminder>",
{
agent: "general",
model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
path: { cwd: process.cwd(), root: "/" }
}
)5. ⚙️ CONFIGURATION
Configuration File Locations
Hooks are configured via Claude Code compatible settings files:
| Location | Scope | Priority |
| ----------------------------- | ------------- | -------- |
| ~/.claude/settings.json | User (global) | Base |
| .claude/settings.json | Project | Override |
| .claude/settings.local.json | Local | Highest |
Settings File Structure
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "~/hooks/validate-edit.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "~/hooks/log-tool-use.sh" }
]
}
],
"UserPromptSubmit": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "~/hooks/inject-context.sh" }
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{ "type": "command", "command": "~/hooks/session-end.sh" }
]
}
]
}
}Matcher Patterns
| Pattern | Matches | Example Tools |
| ------------- | ------------------- | ----------------- |
| * | All tools | Any |
| Edit | Exact match | Edit only |
| Edit\|Write | Multiple tools (OR) | Edit or Write |
| Bash* | Wildcard | Bash, BashSession |
| *Read* | Contains | Read, TodoRead |
Extended Configuration
Additional configuration via .opencode/opencode-cc-plugin.json:
{
"disabledHooks": {
"PreToolUse": [".*secret.*"],
"PostToolUse": [],
"UserPromptSubmit": [],
"Stop": []
}
}6. 🔨 API REFERENCE
Core Exports
// Main hook factory
export { createClaudeCodeHooksHook } from "./hooks/claude-code-hooks"
// Hook executors
export { executePreToolUseHooks } from "./hooks/claude-code-hooks/pre-tool-use"
export { executePostToolUseHooks } from "./hooks/claude-code-hooks/post-tool-use"
export { executeUserPromptSubmitHooks } from "./hooks/claude-code-hooks/user-prompt-submit"
export { executeStopHooks } from "./hooks/claude-code-hooks/stop"
// Config loaders
export { loadClaudeHooksConfig } from "./hooks/claude-code-hooks/config"
export { loadPluginExtendedConfig } from "./hooks/claude-code-hooks/config-loader"
// Message injection
export { injectHookMessage } from "./features/hook-message-injector"
// Utilities
export { log } from "./shared/logger"
export { executeHookCommand } from "./shared/command-executor"
export { transformToolName } from "./shared/tool-name"Type Exports
// Hook event types
export type { ClaudeHookEvent } // "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "Stop"
// Input types (what hooks receive)
export type { PreToolUseInput, PostToolUseInput, UserPromptSubmitInput, StopInput }
// Output types (what hooks return)
export type { PreToolUseOutput, PostToolUseOutput, StopOutput }
// Result types (after execution)
export type { PreToolUseResult, PostToolUseResult, UserPromptSubmitResult, StopResult }
// Context types
export type { PreToolUseContext, PostToolUseContext, UserPromptSubmitContext, StopContext }
// Config types
export type { ClaudeHooksConfig, HookMatcher, HookCommand, PluginConfig }Utility Functions
| Function | Purpose | Example |
| -------------------- | ------------------------------------ | ------------------------------------- |
| log(msg, data?) | Write to plugin log file | log("Hook fired", { tool: "Edit" }) |
| executeHookCommand | Run a hook command with stdin | Execute shell scripts |
| transformToolName | Convert to Claude Code format | "webfetch" → "WebFetch" |
| injectHookMessage | Inject message into conversation | Add synthetic user messages |
| cacheToolInput | Cache input between pre/post hooks | Internal state management |
| getTranscriptPath | Get transcript file path for session | ~/.claude/transcripts/{id}.jsonl |
| getTodoPath | Get todo file path for session | ~/.claude/todos/{id}.json |
7. 📋 WHAT'S INCLUDED VS EXCLUDED
Included (28 files)
src/
├── hooks/claude-code-hooks/ # Core hook system (12 files)
│ ├── index.ts # createClaudeCodeHooksHook factory
│ ├── types.ts # All type definitions
│ ├── config.ts # Claude settings loader
│ ├── config-loader.ts # Extended config loader
│ ├── plugin-config.ts # Default config (zsh settings)
│ ├── pre-tool-use.ts # PreToolUse execution
│ ├── post-tool-use.ts # PostToolUse execution
│ ├── user-prompt-submit.ts # UserPromptSubmit execution
│ ├── stop.ts # Stop hook execution
│ ├── tool-input-cache.ts # Input caching between hooks
│ ├── transcript.ts # Transcript management
│ └── todo.ts # Todo file management
│
├── features/hook-message-injector/ # Message injection (4 files)
│ ├── index.ts
│ ├── injector.ts
│ ├── types.ts
│ └── constants.ts
│
└── shared/ # Utilities (8 files)
├── index.ts
├── logger.ts
├── command-executor.ts
├── hook-disabled.ts
├── pattern-matcher.ts
├── snake-case.ts
├── tool-name.ts
└── deep-merge.tsExcluded (from oh-my-opencode)
| Component | Files | Why Excluded |
| ----------------------- | ----- | --------------------------------- |
| src/agents/ | 12 | OmO, oracle, librarian agents |
| src/auth/ | 15 | Google Antigravity authentication |
| src/tools/ | 5 | Background tools, OmO caller |
| src/mcp/ | 3 | MCP server integrations |
| src/features/loaders/ | 12 | Agent/command/MCP/skill loaders |
| src/hooks/ (specific) | 70+ | Individual hook implementations |
| Heavy dependencies | 6 | ast-grep, picomatch, hono, etc. |
Dependency Comparison
| Dependency | oh-my-opencode | oh-my-opencode-hooks |
| ------------------------------- | -------------- | -------------------- |
| @opencode-ai/plugin | ✅ | ✅ |
| @ast-grep/cli | ✅ | ❌ |
| @ast-grep/napi | ✅ | ❌ |
| @code-yeongyu/comment-checker | ✅ | ❌ |
| @openauthjs/openauth | ✅ | ❌ |
| hono | ✅ | ❌ |
| picomatch | ✅ | ❌ |
| zod | ✅ | ❌ |
8. 📚 RESOURCES
Related Documentation
| Resource | URL | | ----------------------- | ---------------------------------------------------- | | oh-my-opencode (parent) | https://github.com/code-yeongyu/oh-my-opencode | | OpenCode Plugin Docs | https://opencode.ai/docs/plugins | | OpenCode Plugin Events | https://opencode.ai/docs/plugins#events | | Claude Code Hooks Spec | https://docs.anthropic.com/en/docs/claude-code/hooks |
File Locations
| File Type | Location |
| --------------- | --------------------------------------- |
| Plugin log | /tmp/oh-my-opencode.log |
| Transcripts | ~/.claude/transcripts/{session}.jsonl |
| Todos | ~/.claude/todos/{session}.json |
| User config | ~/.claude/settings.json |
| Project config | .claude/settings.json |
| Extended config | .opencode/opencode-cc-plugin.json |
Summary
oh-my-opencode-hooks gives you Claude Code compatible hooks for OpenCode without the full oh-my-opencode plugin overhead:
// Minimal usage - just import and export
import OhMyOpenCodeHooksPlugin from "oh-my-opencode-hooks"
export default OhMyOpenCodeHooksPluginConfigure hooks in ~/.claude/settings.json and they'll execute automatically on tool calls, prompts, and session events.
MIT License - Forked from oh-my-opencode by YeonGyu-Kim
