From f7cab16b06d510ed4ed151793660eda5ce49905a Mon Sep 17 00:00:00 2001 From: Roberto Mello Date: Sun, 8 Feb 2026 15:59:57 -0700 Subject: [PATCH] Fix crash when hook entries have no matcher (#160) Claude Code allows hook entries without a `matcher` field (e.g., SessionStart and SubagentStop hooks don't need one). The OpenCode converter assumed `matcher.matcher` was always present, causing "undefined is not an object (evaluating 'matcher.matcher.split')" when converting plugins with matcher-less hooks. Make `matcher` optional in the type and guard all accesses. --- src/converters/claude-to-opencode.ts | 12 +++++++----- src/types/claude.ts | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/converters/claude-to-opencode.ts b/src/converters/claude-to-opencode.ts index ad8cc00..5e3cf71 100644 --- a/src/converters/claude-to-opencode.ts +++ b/src/converters/claude-to-opencode.ts @@ -209,9 +209,11 @@ function renderHookStatements( ): string[] { if (!matcher.hooks || matcher.hooks.length === 0) return [] const tools = matcher.matcher - .split("|") - .map((tool) => tool.trim().toLowerCase()) - .filter(Boolean) + ? matcher.matcher + .split("|") + .map((tool) => tool.trim().toLowerCase()) + .filter(Boolean) + : [] const useMatcher = useToolMatcher && tools.length > 0 && !tools.includes("*") const condition = useMatcher @@ -232,10 +234,10 @@ function renderHookStatements( continue } if (hook.type === "prompt") { - statements.push(`// Prompt hook for ${matcher.matcher}: ${hook.prompt.replace(/\n/g, " ")}`) + statements.push(`// Prompt hook for ${matcher.matcher ?? "*"}: ${hook.prompt.replace(/\n/g, " ")}`) continue } - statements.push(`// Agent hook for ${matcher.matcher}: ${hook.agent}`) + statements.push(`// Agent hook for ${matcher.matcher ?? "*"}: ${hook.agent}`) } return statements diff --git a/src/types/claude.ts b/src/types/claude.ts index 4b1c050..a878274 100644 --- a/src/types/claude.ts +++ b/src/types/claude.ts @@ -79,7 +79,7 @@ export type ClaudeHookAgent = { export type ClaudeHookEntry = ClaudeHookCommand | ClaudeHookPrompt | ClaudeHookAgent export type ClaudeHookMatcher = { - matcher: string + matcher?: string hooks: ClaudeHookEntry[] }