{"version":3,"file":"hooks-CReMXrU9.cjs","names":["fs","parseYaml","doomConfigCandidates","spawnProcess","createDoomTelemetry"],"sources":["../src/constants/hookDecisions.ts","../src/services/hookDecisions/index.ts","../src/constants/hookDocuments.ts","../src/constants/telemetry.ts","../src/services/toolNames/index.ts","../src/services/hookRegistry/index.ts","../src/services/hookDocuments/index.ts","../src/constants/hookPayload.ts","../src/services/hookPayload/index.ts","../src/constants/hookRunner.ts","../src/services/hookRunner/index.ts","../src/constants/hookTelemetry.ts","../src/services/hookTelemetry/index.ts","../src/services/pluginHooks/index.ts","../src/constants/hooks.ts"],"sourcesContent":["export const BLOCK_DECISION = 'block';\nexport const DENY_PERMISSION = 'deny';\n","import { BLOCK_DECISION, DENY_PERMISSION } from '../../constants/hookDecisions';\nimport type { HookDecision, HookFailure, HookOutcome } from '../../types/hooks';\n\nexport function decisionsFrom(outcomes: ReadonlyArray<HookOutcome>): HookDecision[] {\n  return outcomes.flatMap((outcome) => (outcome.decision ? [outcome.decision] : []));\n}\n\nexport function failuresFrom(outcomes: ReadonlyArray<HookOutcome>): HookFailure[] {\n  return outcomes.flatMap((outcome) => (outcome.failure ? [outcome.failure] : []));\n}\n\n/** Both spellings a hook may use to refuse the call it observed. */\nexport function isDenied(decision: HookDecision | undefined): boolean {\n  return decision?.decision === BLOCK_DECISION || decision?.hookSpecificOutput?.permissionDecision === DENY_PERMISSION;\n}\n\nexport function decisionReason(decision: HookDecision | undefined): string | undefined {\n  return decision?.reason ?? decision?.hookSpecificOutput?.reason;\n}\n\n/** Context a hook wants added to the conversation rather than used to block. */\nexport function additionalContextsFrom(decisions: ReadonlyArray<HookDecision>): string[] {\n  return decisions\n    .map((decision) => decision.hookSpecificOutput?.additionalContext)\n    .filter((value): value is string => Boolean(value));\n}\n\n/** What a post-tool hook has to say, whichever field it said it in. */\nexport function toolResultMessages(decisions: ReadonlyArray<HookDecision>): string[] {\n  return decisions\n    .map((decision) => decision.hookSpecificOutput?.additionalContext ?? decisionReason(decision))\n    .filter((value): value is string => Boolean(value));\n}\n\n/**\n * A guardrail that never ran looks exactly like one that passed, so the agent is\n * told which checks were missed and what its options are.\n */\nexport function hookFailureMessage(failures: ReadonlyArray<HookFailure>): string {\n  return [\n    'One or more repository hooks did not complete, so their checks were not applied:',\n    ...failures.map((failure) => `- ${failure.command}: ${failure.message}`),\n    'Options:',\n    '- Inspect the hook command or configuration and rerun after correcting it.',\n    '- Continue only if the missed advisory check is acceptable.',\n    '- Ask the user before bypassing a hook that may enforce a guardrail.',\n  ].join('\\n');\n}\n","export const HOOKS_FILE = 'hooks.yaml';\nexport const FILE_ENCODING = 'utf8';\nexport const FILE_NOT_FOUND_ERROR = 'ENOENT';\nexport const REGISTRY_SOURCE_ATTRIBUTE = 'repository';\n","export const HOOK_TELEMETRY_EVENT = {\n  hookFailed: 'doom_pi_hook.failed',\n  hookRegistryReadFailed: 'doom_pi_hook.registry_read_failed',\n} as const;\n","/**\n * Pi's tool names translated into the Claude names hook matchers are written\n * against.\n *\n * Hook authors write `matcher: Bash`, and Pi reports `bash`, so a matcher would\n * never fire without this. Only this direction belongs here: rewriting Claude\n * names for Pi is an agent-definition concern the harness owns.\n */\nconst PI_TO_CLAUDE = new Map([\n  ['read', 'Read'],\n  ['edit', 'Edit'],\n  ['write', 'Write'],\n  ['bash', 'Bash'],\n  ['find', 'Glob'],\n  ['grep', 'Grep'],\n  ['subagent', 'Agent'],\n]);\n\n/** The Claude name a hook matcher expects for a tool Pi just ran. */\nexport function toClaudeToolName(tool: string): string {\n  return PI_TO_CLAUDE.get(tool) ?? tool;\n}\n\n/** Whether a matcher, if the row declares one, accepts the tool that fired. */\nexport function matchesTool(matcher: string | undefined, toolName: string | undefined): boolean {\n  if (!toolName || !matcher) return true;\n  return new RegExp(matcher).test(toClaudeToolName(toolName));\n}\n","import type { HookDocumentSource, ParsedRegistrySource, RegistryEntry, ResolvedHook } from '../../types/hooks';\nimport { matchesTool } from '../toolNames';\n\n/** Which rows a dispatch keeps, once the session's selection is known. */\nexport interface RegistrySelection {\n  event: string;\n  toolName?: string;\n  /** Selected group ids, or undefined for \"every group\", the standalone default. */\n  allowedGroups?: readonly string[];\n  inSubagent: boolean;\n}\n\n/**\n * Identity of a set of registry sources, for callers that cache the parse.\n *\n * Keyed on contents rather than mtime, so it stays correct for an in-place\n * rewrite and needs no invalidation hook. Reading both files is cheap; parsing\n * is what a cache built on this skips.\n */\nexport function registryCacheKey(sources: ReadonlyArray<HookDocumentSource>): string {\n  return sources.map((source) => `${source.baseDirectory} ${source.text}`).join(' ');\n}\n\n/**\n * Flattens parsed registry documents into sorted rows, keeping only bindings\n * that declare a `pi` frontend.\n *\n * A later source replaces the group of the same id outright rather than merging\n * into it, so a repository that redefines a group owns it completely.\n */\nexport function registryEntries(sources: ReadonlyArray<ParsedRegistrySource>): RegistryEntry[] {\n  const groups = new Map<string, RegistryEntry[]>();\n  let position = 0;\n  for (const source of sources) {\n    for (const [groupId, group] of Object.entries(source.document.groups ?? {})) {\n      const entries: RegistryEntry[] = [];\n      for (const hook of group.hooks ?? []) {\n        // Every declared hook advances the position, whether or not it is kept,\n        // so the sort tiebreaker stays the order the file declares them in.\n        position += 1;\n        if (!hook.pi) continue;\n        entries.push({\n          ...hook.pi,\n          event: hook.event,\n          order: hook.pi.order ?? 0,\n          position,\n          groupId,\n          core: group.core === true,\n          baseDirectory: source.baseDirectory,\n        });\n      }\n      groups.set(groupId, entries);\n    }\n  }\n\n  const entries = [...groups.values()].flat();\n  entries.sort((left, right) => left.order - right.order || left.position - right.position);\n  return entries;\n}\n\n/**\n * The rows one dispatch runs.\n *\n * Group selection changes when /mode switches mid-session, so inclusion is\n * applied per call rather than being baked into a cached parse. Core groups\n * always load; the rest are gated by the layers the harness resolved.\n */\nexport function selectRegistryHooks(\n  entries: ReadonlyArray<RegistryEntry>,\n  selection: RegistrySelection,\n): ResolvedHook[] {\n  const allowed = selection.allowedGroups === undefined ? undefined : new Set(selection.allowedGroups);\n  return entries\n    .filter((entry) => entry.core || !allowed || allowed.has(entry.groupId))\n    .filter((entry) => entry.event === selection.event)\n    .filter((entry) => !(entry.skipInSubagent && selection.inSubagent))\n    .filter((entry) => matchesTool(entry.matcher, selection.toolName))\n    .map((entry) => ({\n      hook: { command: entry.command, timeout: entry.timeout },\n      // The declaring config's root, so a global hook can reach its own scripts\n      // through CLAUDE_PLUGIN_ROOT while still running against this repository.\n      root: entry.baseDirectory,\n    }));\n}\n","import fs from 'node:fs';\n\nimport { doomConfigCandidates } from '@agimon-ai/doompi-config/layeredConfig';\nimport { parse as parseYaml } from 'yaml';\n\nimport {\n  HOOKS_FILE,\n  FILE_ENCODING,\n  FILE_NOT_FOUND_ERROR,\n  REGISTRY_SOURCE_ATTRIBUTE,\n} from '../../constants/hookDocuments';\nimport { HOOK_TELEMETRY_EVENT } from '../../constants/telemetry';\nimport type {\n  HookDocumentReader,\n  HookDocumentSource,\n  PluginDocumentRead,\n  PluginHookConfig,\n  PluginHookDocument,\n  PluginHookSourceRef,\n  RegistryDocument,\n  RegistryEntry,\n  RegistryRead,\n} from '../../types/hooks';\nimport { type HookTelemetry } from '../../types/telemetry';\nimport { registryCacheKey, registryEntries } from '../hookRegistry';\n\nexport interface HookDocumentReaderOptions {\n  telemetry?: HookTelemetry;\n  /** Where the global `.doom` directory lives. Defaults to the user's home. */\n  homeDirectory?: string;\n  readFile?: (filePath: string) => Promise<string>;\n  warn?: (message: string) => void;\n}\n\nfunction reason(error: unknown): string {\n  return error instanceof Error ? error.message : String(error);\n}\n\nfunction isMissingFile(error: unknown): boolean {\n  return (error as NodeJS.ErrnoException | undefined)?.code === FILE_NOT_FOUND_ERROR;\n}\n\n/**\n * Reads `.doom/hooks.yaml` from the global config directory and the repository,\n * and the plugin hook configs the harness resolved.\n *\n * Every file is re-read on every dispatch: it costs almost nothing and keeps an\n * in-place rewrite visible, which an mtime check would miss when two writes land\n * inside the same filesystem timestamp granularity. Both caches are keyed on\n * contents instead, so parsing — the expensive half — is what they skip.\n */\nexport function createHookDocumentReader(options: HookDocumentReaderOptions = {}): HookDocumentReader {\n  const readFile = options.readFile ?? ((filePath: string) => fs.promises.readFile(filePath, FILE_ENCODING));\n  const telemetry = options.telemetry;\n  const warn = options.warn ?? ((message: string) => void process.stderr.write(message));\n  let cachedRegistryKey: string | undefined;\n  let cachedRegistryEntries: RegistryEntry[] = [];\n  const pluginConfigCache = new Map<string, { source: string; config: PluginHookConfig }>();\n\n  const readSource = async (candidate: {\n    filePath: string;\n    baseDirectory: string;\n  }): Promise<HookDocumentSource | undefined> => {\n    try {\n      return { baseDirectory: candidate.baseDirectory, text: await readFile(candidate.filePath) };\n    } catch (error) {\n      if (isMissingFile(error)) return undefined;\n      throw error;\n    }\n  };\n\n  const parseSources = (sources: ReadonlyArray<HookDocumentSource>): RegistryEntry[] => {\n    const key = registryCacheKey(sources);\n    if (cachedRegistryKey === key) return cachedRegistryEntries;\n    cachedRegistryEntries = registryEntries(\n      sources.map((source) => ({\n        baseDirectory: source.baseDirectory,\n        document: (parseYaml(source.text) ?? {}) as RegistryDocument,\n      })),\n    );\n    cachedRegistryKey = key;\n    return cachedRegistryEntries;\n  };\n\n  return {\n    async registry(repoRoot: string): Promise<RegistryRead> {\n      const candidates = doomConfigCandidates(HOOKS_FILE, repoRoot, options.homeDirectory);\n      // Named for the error path only. A registry absent from both locations is\n      // a repository with no hooks, not a failure, so only a file that exists\n      // and cannot be read or parsed reaches the catch below.\n      const registryPath = candidates.map((candidate) => candidate.filePath).join(' and ');\n      try {\n        const sources = (await Promise.all(candidates.map(readSource))).filter(\n          (source): source is HookDocumentSource => source !== undefined,\n        );\n        return { entries: parseSources(sources) };\n      } catch (error) {\n        // This empties the registry for the whole dispatch, so every repository\n        // hook silently stops running rather than any one of them failing.\n        void telemetry?.recordError(HOOK_TELEMETRY_EVENT.hookRegistryReadFailed, error, {\n          'hook.registry.source': REGISTRY_SOURCE_ATTRIBUTE,\n        });\n        warn(`[pi-hook] could not read ${registryPath}: ${reason(error)}\\n`);\n        return { entries: [], failure: { command: registryPath, message: reason(error), reason: 'registry_read' } };\n      }\n    },\n\n    async plugins(sources: readonly PluginHookSourceRef[]): Promise<PluginDocumentRead> {\n      const documents: PluginHookDocument[] = [];\n      const failures: PluginDocumentRead['failures'] = [];\n      for (const source of sources) {\n        try {\n          const text = await readFile(source.configPath);\n          const cached = pluginConfigCache.get(source.configPath);\n          const config = cached?.source === text ? cached.config : (JSON.parse(text) as PluginHookConfig);\n          pluginConfigCache.set(source.configPath, { source: text, config });\n          documents.push({ pluginRoot: source.pluginRoot, config });\n        } catch (error) {\n          failures.push({ command: source.configPath, message: reason(error), reason: 'plugin_config' });\n        }\n      }\n      return { documents, failures };\n    },\n  };\n}\n","export const TOOL_RESULT_EVENT = 'tool_result';\n","import { TOOL_RESULT_EVENT } from '../../constants/hookPayload';\nimport type { HookPayload, HookToolEvent } from '../../types/hooks';\nimport { toClaudeToolName } from '../toolNames';\n\n/** The payload shape a Claude Code session hook is written against. */\nexport function toolHookPayload(\n  event: HookToolEvent,\n  hookEventName: string,\n  repoRoot: string,\n  sessionId: string,\n): HookPayload {\n  return {\n    session_id: sessionId,\n    transcript_path: '',\n    cwd: repoRoot,\n    hook_event_name: hookEventName,\n    tool_name: toClaudeToolName(event.toolName),\n    tool_input: event.input,\n    ...(event.type === TOOL_RESULT_EVENT ? { tool_response: { success: !event.isError, content: event.content } } : {}),\n  };\n}\n\n/** Session lifecycle hooks observe no tool, so they only need where and who. */\nexport function sessionHookPayload(sessionId: string, repoRoot: string): HookPayload {\n  return { session_id: sessionId, cwd: repoRoot };\n}\n","export const HOOK_SHELL = '/bin/bash';\nexport const HOOK_SHELL_COMMAND_FLAG = '-c';\nexport const HOOK_TERMINATION_GRACE_MS = 2_000;\nexport const DEFAULT_HOOK_TIMEOUT_SECONDS = 10;\nexport const MILLISECONDS_PER_SECOND = 1_000;\nexport const PROCESS_NOT_FOUND_ERROR = 'ESRCH';\nexport const WINDOWS_PLATFORM = 'win32';\nexport const UNKNOWN_EXIT_CODE = -1;\nexport const JSON_LINE_START = '{';\nexport const LINE_BREAK = /\\r?\\n/;\n","import { spawn as spawnProcess } from 'node:child_process';\n\nimport {\n  HOOK_SHELL,\n  HOOK_SHELL_COMMAND_FLAG,\n  HOOK_TERMINATION_GRACE_MS,\n  DEFAULT_HOOK_TIMEOUT_SECONDS,\n  MILLISECONDS_PER_SECOND,\n  PROCESS_NOT_FOUND_ERROR,\n  WINDOWS_PLATFORM,\n  UNKNOWN_EXIT_CODE,\n  JSON_LINE_START,\n  LINE_BREAK,\n} from '../../constants/hookRunner';\nimport { HOOK_TELEMETRY_EVENT } from '../../constants/telemetry';\nimport type {\n  HookCommand,\n  HookDecision,\n  HookOutcome,\n  HookPayload,\n  HookRunOptions,\n  HookRunner,\n} from '../../types/hooks';\nimport { type HookTelemetry } from '../../types/telemetry';\n\nexport interface BashHookRunnerOptions {\n  telemetry?: HookTelemetry;\n  env?: NodeJS.ProcessEnv;\n  platform?: NodeJS.Platform;\n  spawn?: typeof spawnProcess;\n  warn?: (message: string) => void;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n  return error instanceof Error && 'code' in error ? String(error.code) : undefined;\n}\n\n/**\n * Runs advisory hook commands through bash and reports what each one decided.\n *\n * A hook is a command the repository owns, so it is spawned in its own process\n * group and terminated as one: a hook that starts a server and stalls should\n * not leave the server behind when the timeout fires.\n */\nexport function createBashHookRunner(options: BashHookRunnerOptions = {}): HookRunner {\n  const spawn = options.spawn ?? spawnProcess;\n  const telemetry = options.telemetry;\n  const warn = options.warn ?? ((message: string) => void process.stderr.write(message));\n  const useProcessGroup = (options.platform ?? process.platform) !== WINDOWS_PLATFORM;\n\n  const signalOwnedProcess = (\n    pid: number | undefined,\n    kill: (signal: NodeJS.Signals) => void,\n  ): ((signal: NodeJS.Signals) => void) => {\n    return (signal) => {\n      try {\n        if (useProcessGroup && pid !== undefined) {\n          process.kill(-pid, signal);\n          return;\n        }\n        kill(signal);\n      } catch (error) {\n        if (errorCode(error) !== PROCESS_NOT_FOUND_ERROR) {\n          process.emitWarning(`Could not signal repository hook process: ${String(error)}`);\n        }\n      }\n    };\n  };\n\n  return {\n    run(hook: HookCommand, payload: HookPayload, runOptions: HookRunOptions): Promise<HookOutcome> {\n      const timeoutSeconds = hook.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS;\n      const environment = options.env ?? process.env;\n      return new Promise((resolve) => {\n        // -c, not -lc. Hooks run on every tool call, and a login shell would\n        // source the developer's profile each time: per-call latency plus hook\n        // behavior that varies with their dotfiles. The child inherits the\n        // environment below, so it keeps the PATH the launcher started with.\n        const child = spawn(HOOK_SHELL, [HOOK_SHELL_COMMAND_FLAG, hook.command], {\n          cwd: runOptions.repoRoot,\n          env: {\n            ...environment,\n            CLAUDE_PROJECT_DIR: runOptions.repoRoot,\n            CODEX_REPO_ROOT: runOptions.repoRoot,\n            ORIGINAL_REPO_PATH: environment.ORIGINAL_REPO_PATH ?? runOptions.repoRoot,\n            ...(runOptions.pluginRoot ? { CLAUDE_PLUGIN_ROOT: runOptions.pluginRoot } : {}),\n          },\n          stdio: ['pipe', 'pipe', 'pipe'],\n          detached: useProcessGroup,\n        });\n        let stdout = '';\n        let stderr = '';\n        let settled = false;\n        let timedOut = false;\n        let childExited = false;\n        let timeoutExitCode: number | null = null;\n        let escalationTimer: NodeJS.Timeout | undefined;\n\n        const signal = signalOwnedProcess(child.pid, (value) => child.kill(value));\n\n        const ownedProcessIsAlive = (): boolean => {\n          if (!useProcessGroup || child.pid === undefined) return !childExited;\n          try {\n            process.kill(-child.pid, 0);\n            return true;\n          } catch (error) {\n            return errorCode(error) !== PROCESS_NOT_FOUND_ERROR;\n          }\n        };\n\n        const finish = (outcome: HookOutcome = {}): void => {\n          if (settled) return;\n          settled = true;\n          clearTimeout(timer);\n          if (escalationTimer) clearTimeout(escalationTimer);\n          resolve(outcome);\n        };\n\n        const finishTimeout = (): void => {\n          const message = `Hook timed out after ${timeoutSeconds} seconds.`;\n          void telemetry?.recordWarning(HOOK_TELEMETRY_EVENT.hookFailed, message, {\n            'hook.reason': 'timeout',\n            'hook.exit_code': timeoutExitCode ?? UNKNOWN_EXIT_CODE,\n          });\n          warn(`[pi-hook] advisory hook timed out: ${hook.command}\\n${stderr}`);\n          finish({ failure: { command: hook.command, message, reason: 'timeout' } });\n        };\n\n        const timer = setTimeout(() => {\n          timedOut = true;\n          signal('SIGTERM');\n          escalationTimer = setTimeout(() => {\n            if (ownedProcessIsAlive()) signal('SIGKILL');\n            finishTimeout();\n          }, HOOK_TERMINATION_GRACE_MS);\n        }, timeoutSeconds * MILLISECONDS_PER_SECOND);\n\n        child.stdout?.on('data', (chunk: Buffer) => {\n          stdout += chunk.toString();\n        });\n        child.stderr?.on('data', (chunk: Buffer) => {\n          stderr += chunk.toString();\n        });\n        child.once('error', (error: Error) => {\n          if (timedOut) return;\n          // A guardrail that never ran is indistinguishable from one that\n          // passed, which is the whole reason these are worth reporting.\n          void telemetry?.recordError(HOOK_TELEMETRY_EVENT.hookFailed, error, { 'hook.reason': 'spawn_failed' });\n          warn(`[pi-hook] advisory hook failed: ${error.message}\\n`);\n          finish({ failure: { command: hook.command, message: error.message, reason: 'spawn_failed' } });\n        });\n        child.once('exit', (code: number | null) => {\n          childExited = true;\n          timeoutExitCode = code;\n          if (timedOut) return;\n          if (code !== 0) {\n            const message = stderr.trim() || `Advisory hook exited with code ${code ?? 'unknown'}`;\n            void telemetry?.recordWarning(HOOK_TELEMETRY_EVENT.hookFailed, message, {\n              'hook.reason': 'non_zero_exit',\n              'hook.exit_code': code ?? UNKNOWN_EXIT_CODE,\n            });\n            warn(`[pi-hook] advisory hook failed (${code}): ${hook.command}\\n${stderr}`);\n            finish({ failure: { command: hook.command, message, reason: 'non_zero_exit' } });\n            return;\n          }\n          const jsonLine = stdout\n            .trim()\n            .split(LINE_BREAK)\n            .reverse()\n            .find((line) => line.startsWith(JSON_LINE_START));\n          if (!jsonLine) {\n            finish();\n            return;\n          }\n          try {\n            finish({ decision: JSON.parse(jsonLine) as HookDecision });\n          } catch (error) {\n            // The hook ran and had an opinion, and it was dropped.\n            void telemetry?.recordWarning(HOOK_TELEMETRY_EVENT.hookFailed, error, { 'hook.reason': 'invalid_json' });\n            warn(`[pi-hook] advisory hook returned invalid JSON: ${hook.command}\\n`);\n            finish({\n              failure: {\n                command: hook.command,\n                message: error instanceof Error ? error.message : String(error),\n                reason: 'invalid_json',\n              },\n            });\n          }\n        });\n        child.stdin?.end(JSON.stringify(payload));\n      });\n    },\n  };\n}\n","export const SERVICE_NAME = 'doom-pi-hook';\nexport const PACKAGE_NAME = '@agimon-ai/doompi-hook';\n","import { createDoomTelemetry, type DoomTelemetry, type DoomTelemetryOptions } from '@agimon-ai/doompi-telemetry';\n\nimport { SERVICE_NAME, PACKAGE_NAME } from '../../constants/hookTelemetry';\nimport type { HookTelemetry } from '../../types/telemetry';\n\nexport interface HookTelemetryOptions {\n  cwd?: string;\n  workspaceRoot?: string;\n  env?: NodeJS.ProcessEnv;\n  telemetryFactory?: NonNullable<DoomTelemetryOptions['telemetryFactory']>;\n  warn?: (message: string) => void;\n}\n\nexport function createHookTelemetry(options: HookTelemetryOptions = {}): HookTelemetry {\n  const telemetry: DoomTelemetry = createDoomTelemetry({\n    serviceName: SERVICE_NAME,\n    packageName: PACKAGE_NAME,\n    cwd: options.cwd,\n    workspaceRoot: options.workspaceRoot,\n    env: options.env,\n    telemetryFactory: options.telemetryFactory,\n    warn: options.warn,\n    enableLogs: true,\n    enableTraces: true,\n  });\n  return {\n    recordError: (event, error, attributes) => telemetry.recordError(event, error, attributes),\n    recordWarning: (event, error, attributes) => telemetry.recordWarning(event, error, attributes),\n  };\n}\n","import type { PluginHookDocument, ResolvedHook } from '../../types/hooks';\nimport { matchesTool } from '../toolNames';\n\n/**\n * The plugin hooks one dispatch runs.\n *\n * Plugin configs are the Claude Code `hooks.json` shape verbatim: an event name\n * maps to groups, each with an optional matcher and a list of commands. Order\n * follows the declaration order of the plugins the harness resolved.\n */\nexport function selectPluginHooks(\n  documents: ReadonlyArray<PluginHookDocument>,\n  eventName: string,\n  toolName?: string,\n): ResolvedHook[] {\n  const matches: ResolvedHook[] = [];\n  for (const document of documents) {\n    for (const group of document.config.hooks?.[eventName] ?? []) {\n      if (!matchesTool(group.matcher, toolName)) continue;\n      for (const hook of group.hooks ?? []) matches.push({ hook, root: document.pluginRoot });\n    }\n  }\n  return matches;\n}\n","export const HOOK_EVENT = {\n  sessionStart: 'SessionStart',\n  preToolUse: 'PreToolUse',\n  postToolUse: 'PostToolUse',\n  stop: 'Stop',\n  sessionEnd: 'SessionEnd',\n} as const;\n"],"mappings":"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"}