import type { HookName, OhMyOpenCodeConfig } from "../../config" import type { ModelCacheState } from "../../plugin-state" import type { PluginContext } from "../types" import { createCommentCheckerHooks, createToolOutputTruncatorHook, createDirectoryAgentsInjectorHook, createDirectoryReadmeInjectorHook, createEmptyTaskResponseDetectorHook, createRulesInjectorHook, createTasksTodowriteDisablerHook, createWriteExistingFileGuardHook, createBashFileReadGuardHook, createHashlineReadEnhancerHook, createReadImageResizerHook, createJsonErrorRecoveryHook, createTodoDescriptionOverrideHook, createWebFetchRedirectGuardHook, createTeamToolGating, createFsyncSkipWarningHook, createNotepadWriteGuardHook, createPlanFormatValidatorHook, } from "../../hooks" import { getOpenCodeVersion, isOpenCodeVersionAtLeast, log, OPENCODE_NATIVE_AGENTS_INJECTION_VERSION, } from "../../shared" import { safeCreateHook } from "../../shared/safe-create-hook" export type ToolGuardHooks = { commentChecker: ReturnType | null toolOutputTruncator: ReturnType | null directoryAgentsInjector: ReturnType | null directoryReadmeInjector: ReturnType | null emptyTaskResponseDetector: ReturnType | null rulesInjector: ReturnType | null tasksTodowriteDisabler: ReturnType | null writeExistingFileGuard: ReturnType | null bashFileReadGuard: ReturnType | null hashlineReadEnhancer: ReturnType | null jsonErrorRecovery: ReturnType | null readImageResizer: ReturnType | null todoDescriptionOverride: ReturnType | null webfetchRedirectGuard: ReturnType | null fsyncSkipWarning: ReturnType | null teamToolGating: ReturnType | null notepadWriteGuard: ReturnType | null planFormatValidator: ReturnType | null } export function createToolGuardHooks(args: { ctx: PluginContext pluginConfig: OhMyOpenCodeConfig modelCacheState: ModelCacheState isHookEnabled: (hookName: HookName) => boolean safeHookEnabled: boolean }): ToolGuardHooks { const { ctx, pluginConfig, modelCacheState, isHookEnabled, safeHookEnabled } = args const safeHook = (hookName: HookName, factory: () => T): T | null => safeCreateHook(hookName, factory, { enabled: safeHookEnabled }) const commentChecker = isHookEnabled("comment-checker") ? safeHook("comment-checker", () => createCommentCheckerHooks(pluginConfig.comment_checker)) : null const toolOutputTruncator = isHookEnabled("tool-output-truncator") ? safeHook("tool-output-truncator", () => createToolOutputTruncatorHook(ctx, { modelCacheState, experimental: pluginConfig.experimental, })) : null let directoryAgentsInjector: ReturnType | null = null if (isHookEnabled("directory-agents-injector")) { const currentVersion = getOpenCodeVersion() const hasNativeSupport = currentVersion !== null && isOpenCodeVersionAtLeast(OPENCODE_NATIVE_AGENTS_INJECTION_VERSION) if (hasNativeSupport) { log("directory-agents-injector auto-disabled due to native OpenCode support", { currentVersion, nativeVersion: OPENCODE_NATIVE_AGENTS_INJECTION_VERSION, }) } else { directoryAgentsInjector = safeHook("directory-agents-injector", () => createDirectoryAgentsInjectorHook(ctx, modelCacheState)) } } const directoryReadmeInjector = isHookEnabled("directory-readme-injector") ? safeHook("directory-readme-injector", () => createDirectoryReadmeInjectorHook(ctx, modelCacheState)) : null const emptyTaskResponseDetector = isHookEnabled("empty-task-response-detector") ? safeHook("empty-task-response-detector", () => createEmptyTaskResponseDetectorHook(ctx)) : null const cc = pluginConfig.claude_code const skipClaudeUserRules = cc?.hooks === false const rulesInjector = isHookEnabled("rules-injector") ? safeHook("rules-injector", () => createRulesInjectorHook(ctx, modelCacheState, { skipClaudeUserRules, })) : null const tasksTodowriteDisabler = isHookEnabled("tasks-todowrite-disabler") ? safeHook("tasks-todowrite-disabler", () => createTasksTodowriteDisablerHook({ experimental: pluginConfig.experimental })) : null const writeExistingFileGuard = isHookEnabled("write-existing-file-guard") ? safeHook("write-existing-file-guard", () => createWriteExistingFileGuardHook(ctx)) : null const bashFileReadGuard = isHookEnabled("bash-file-read-guard") ? safeHook("bash-file-read-guard", () => createBashFileReadGuardHook()) : null const hashlineReadEnhancer = isHookEnabled("hashline-read-enhancer") ? safeHook("hashline-read-enhancer", () => createHashlineReadEnhancerHook(ctx, { hashline_edit: { enabled: pluginConfig.hashline_edit ?? false } })) : null const jsonErrorRecovery = isHookEnabled("json-error-recovery") ? safeHook("json-error-recovery", () => createJsonErrorRecoveryHook(ctx)) : null const readImageResizer = isHookEnabled("read-image-resizer") ? safeHook("read-image-resizer", () => createReadImageResizerHook(ctx)) : null const todoDescriptionOverride = isHookEnabled("todo-description-override") ? safeHook("todo-description-override", () => createTodoDescriptionOverrideHook()) : null const webfetchRedirectGuard = isHookEnabled("webfetch-redirect-guard") ? safeHook("webfetch-redirect-guard", () => createWebFetchRedirectGuardHook(ctx)) : null const teamToolGating = isHookEnabled("team-tool-gating") ? safeHook("team-tool-gating", () => createTeamToolGating(ctx, pluginConfig.team_mode)) : null const fsyncSkipWarning = isHookEnabled("fsync-skip-warning") ? safeHook("fsync-skip-warning", () => createFsyncSkipWarningHook()) : null const planFormatValidator = isHookEnabled("plan-format-validator") ? safeHook("plan-format-validator", () => createPlanFormatValidatorHook(ctx)) : null const notepadWriteGuard = isHookEnabled("notepad-write-guard") ? safeHook("notepad-write-guard", () => createNotepadWriteGuardHook()) : null return { commentChecker, toolOutputTruncator, directoryAgentsInjector, directoryReadmeInjector, emptyTaskResponseDetector, rulesInjector, tasksTodowriteDisabler, writeExistingFileGuard, bashFileReadGuard, hashlineReadEnhancer, jsonErrorRecovery, readImageResizer, todoDescriptionOverride, webfetchRedirectGuard, fsyncSkipWarning, teamToolGating, notepadWriteGuard, planFormatValidator, } }