/** * Hook Execution Service * * Intercepts tool calls before (PreToolUse) and after (PostToolUse) execution, * running user-configured shell commands that can validate, block, modify, or * observe tool operations. * * Hooks are contributed by plugins (e.g. valora-defaults) or .valora/hooks.json (project-level override). * Hook commands receive tool call info via stdin JSON and control execution * via exit codes (0=allow, 2=deny for PreToolUse; PostToolUse is non-blocking). */ import type { HookCommand, HookEventName, HookExecutionResult, HookInput, HookMatcher, HookOutput, HooksConfig } from '../types/hook.types.js'; import type { LLMToolCall } from '../types/llm.types.js'; export declare class HookExecutionService { private hasWarnedUntrustedProjectHooks; private hooksFileCache; private hooksFileMtime; private readonly logger; private pluginHooks; private readonly registeredPluginHooks; private sessionId?; /** * Register hook configuration contributed by a plugin. * Plugin hooks are merged with lower priority than built-in and config hooks. */ registerPluginHooks(hooks: HooksConfig): void; /** * Fast check to skip processing when no hooks are configured. * Returns false for zero-overhead path when hooks are not in use. */ hasHooks(eventName: HookEventName): boolean; /** * Set the session ID included in hook input payloads. */ setSessionId(sessionId: string): void; /** * Execute PreToolUse hooks for a tool call. * Hooks can block execution (exit code 2) or modify tool input (exit code 0 with updatedInput). * Hooks execute sequentially; first deny stops the chain. */ executePreToolUseHooks(toolCall: LLMToolCall): Promise; /** * Run a single PreToolUse hook and apply its result. * Returns true if the hook blocked execution (deny). */ private runPreToolUseHook; /** * Handle a PreToolUse hook deny (exit code 2). */ private handlePreToolUseDeny; /** * Handle a PreToolUse hook allow (exit code 0), applying any updatedInput. */ private handlePreToolUseAllow; /** * Execute PostToolUse hooks for a tool call (non-blocking, informational only). * Exit codes are logged but never block the pipeline. * Async hooks fire-and-forget without awaiting completion. */ executePostToolUseHooks(toolCall: LLMToolCall, toolResult: string): Promise; /** * Spawn a shell command, writing JSON to stdin. * Returns the exit code, stdout, and stderr. */ executeHookCommand(hook: HookCommand, input: HookInput): Promise<{ exitCode: number; stderr: string; stdout: string; }>; /** * Test regex matchers against tool name and collect matching hook commands. * Invalid regexes are skipped with a warning (graceful degradation). */ findMatchingHooks(toolName: string, matchers: HookMatcher[]): HookCommand[]; /** * Safely parse JSON output from a hook command's stdout. * Returns null if stdout is empty or not valid JSON. */ parseHookOutput(stdout: string): HookOutput | null; /** * Load hooks from data/hooks.default.json with mtime-based caching. * Returns null if the file doesn't exist (e.g. after migration to valora-defaults plugin) or can't be parsed. */ private loadHooksFile; /** * Read hooks config, merging the legacy hooks.default.json (if present) with config.json and any plugin hooks. * Priority (highest first): hooks.default.json → config.json → plugin hooks. */ private getHooksConfig; /** * `config/loader.ts` merges global (`~/.valora/config.json`) and project * (`.valora/config.json`) hooks into one value with no source tracking — * but only the project one is untrusted content (anyone who can get a * victim to clone a repo and run any `valora` command inside it controls * it; global config is the user's own file). Read the project's config * file directly to check whether IT specifically declares hooks; if so, * require the project directory to be explicitly trusted * (`valora config trust`, see `workspace-trust.service.ts`) before any * config-sourced hooks run at all. Global-only hooks (no project * declaration, or no project config file present) are never gated. */ private getTrustGatedConfigHooks; /** * Merge two HooksConfig objects. Primary (hooks.json) takes priority; * config.json matchers with the same pattern are skipped. */ private mergeHooksConfigs; /** * Build the HookInput object sent to hook commands via stdin. */ private buildHookInput; } export declare function getHookExecutionService(): HookExecutionService; export declare function setHookExecutionService(service: HookExecutionService): void; //# sourceMappingURL=hook-execution.service.d.ts.map