import type { PluginInput } from "@opencode-ai/plugin"; import { createDynamicTruncator } from "../../shared/dynamic-truncator"; import { resolveSessionEventID } from "../../shared/event-session-id"; import { getRuleInjectionFilePath } from "./output-path"; import { createSessionCacheStore, createSessionRuleScanCacheStore } from "./cache"; import { createRuleInjectionProcessor } from "./injector"; import { clearProjectRootCache } from "./project-root-finder"; interface ToolExecuteInput { tool: string; sessionID: string; callID: string; } interface ToolExecuteOutput { title: string; output: string; metadata: unknown; } interface ToolExecuteBeforeOutput { args: unknown; } interface EventInput { event: { type: string; properties?: unknown; }; } const TRACKED_TOOLS = ["read", "write", "edit", "multiedit"]; export function createRulesInjectorHook( ctx: PluginInput, modelCacheState?: { anthropicContext1MEnabled: boolean }, options?: { skipClaudeUserRules?: boolean }, ) { const truncator = createDynamicTruncator(ctx, modelCacheState); const { getSessionCache, clearSessionCache } = createSessionCacheStore(); const { getSessionRuleScanCache, clearSessionRuleScanCache } = createSessionRuleScanCacheStore(); const { processFilePathForInjection } = createRuleInjectionProcessor({ workspaceDirectory: ctx.directory, truncator, getSessionCache, getSessionRuleScanCache, ruleFinderOptions: options?.skipClaudeUserRules ? { skipClaudeUserRules: true } : undefined, }); function clearSessionState(sessionID: string): void { clearSessionCache(sessionID); clearSessionRuleScanCache(sessionID); } const toolExecuteAfter = async ( input: ToolExecuteInput, output: ToolExecuteOutput ) => { const toolName = input.tool.toLowerCase(); if (TRACKED_TOOLS.includes(toolName)) { const filePath = getRuleInjectionFilePath(output); if (!filePath) return; await processFilePathForInjection(filePath, input.sessionID, output); return; } }; const toolExecuteBefore = async ( input: ToolExecuteInput, output: ToolExecuteBeforeOutput ): Promise => { void input; void output; }; const eventHandler = async ({ event }: EventInput) => { const props = event.properties as Record | undefined; if (event.type === "session.deleted") { const sessionID = resolveSessionEventID(props); if (sessionID) { clearSessionState(sessionID); } clearProjectRootCache(); } if (event.type === "session.compacted") { const sessionID = resolveSessionEventID(props); if (sessionID) { clearSessionState(sessionID); } clearProjectRootCache(); } }; return { "tool.execute.before": toolExecuteBefore, "tool.execute.after": toolExecuteAfter, event: eventHandler, }; }