@cleocode/caamp 2026.4.111 → 2026.4.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -291,4 +291,4 @@ export {
291
291
  resolveNativeEvent,
292
292
  getHookMappingsVersion
293
293
  };
294
- //# sourceMappingURL=chunk-AOCNTIKB.js.map
294
+ //# sourceMappingURL=chunk-UZSGHMYW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/hooks/normalizer.ts","../src/core/hooks/generated.ts"],"sourcesContent":["/**\n * CAAMP Hooks Normalizer\n *\n * Translates between CAAMP canonical hook events and provider-native\n * event names. Provides query functions for hook support, cross-provider\n * comparison, and event normalization.\n *\n * This module follows the same pattern as `src/core/mcp/transforms.ts` —\n * a translation layer that lets consumers use one canonical interface\n * while CAAMP handles provider-specific differences.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { resolveRegistryTemplatePath } from '../paths/standard.js';\nimport type {\n CanonicalEventDefinition,\n CanonicalHookEvent,\n CrossProviderMatrix,\n HookCategory,\n HookMapping,\n HookMappingsFile,\n HookSupportResult,\n HookSystemType,\n NormalizedHookEvent,\n ProviderHookProfile,\n ProviderHookSummary,\n} from './types.js';\nimport { CANONICAL_HOOK_EVENTS, PROVIDER_HOOK_EVENTS } from './types.js';\n\n// ── Data Loading ────────────────────────────────────────────────────\n\nlet _mappings: HookMappingsFile | null = null;\n\nfunction findMappingsPath(): string {\n const thisDir = dirname(fileURLToPath(import.meta.url));\n // src/core/hooks/ -> providers/hook-mappings.json\n return join(thisDir, '..', '..', '..', 'providers', 'hook-mappings.json');\n}\n\nfunction loadMappings(): HookMappingsFile {\n if (_mappings) return _mappings;\n const mappingsPath = findMappingsPath();\n if (!existsSync(mappingsPath)) {\n // Return an empty but structurally valid mappings object when the file is missing.\n // This avoids ENOENT crashes in installed environments where the providers/\n // directory may not be bundled (e.g. global npm installs with hoisted deps).\n const empty: HookMappingsFile = {\n version: '0.0.0',\n lastUpdated: new Date().toISOString(),\n description: 'Empty fallback — hook-mappings.json not found',\n canonicalEvents: {} as HookMappingsFile['canonicalEvents'],\n providerMappings: {},\n };\n _mappings = empty;\n return empty;\n }\n const raw = readFileSync(mappingsPath, 'utf-8');\n _mappings = JSON.parse(raw) as HookMappingsFile;\n return _mappings;\n}\n\n/**\n * Reset the cached hook mappings data.\n *\n * @remarks\n * Clears the in-memory cache so the next query function call will\n * re-read `providers/hook-mappings.json` from disk. This is primarily\n * intended for test isolation — production code should not need to call this.\n *\n * @example\n * ```typescript\n * import { resetHookMappings, getHookMappingsVersion } from \"./normalizer.js\";\n *\n * // Force a fresh load from disk\n * resetHookMappings();\n * const version = getHookMappingsVersion();\n * ```\n *\n * @public\n */\nexport function resetHookMappings(): void {\n _mappings = null;\n}\n\n// ── Core Query Functions ────────────────────────────────────────────\n\n/**\n * Get the canonical event definition (category, description, canBlock).\n *\n * @remarks\n * Looks up the definition from the hook mappings data file. The returned\n * object contains the event's category, human-readable description, and\n * whether handlers can block the associated action.\n *\n * @param event - The canonical event name to look up.\n * @returns The event definition containing category, description, and canBlock flag.\n *\n * @example\n * ```typescript\n * import { getCanonicalEvent } from \"./normalizer.js\";\n *\n * const def = getCanonicalEvent(\"PreToolUse\");\n * console.log(def.category); // \"tool\"\n * console.log(def.canBlock); // true\n * ```\n *\n * @public\n */\nexport function getCanonicalEvent(event: CanonicalHookEvent): CanonicalEventDefinition {\n const data = loadMappings();\n return data.canonicalEvents[event];\n}\n\n/**\n * Get all canonical event definitions.\n *\n * @remarks\n * Returns the complete map of canonical event names to their definitions.\n * Useful for iterating over the full taxonomy or building UI displays\n * of all available events.\n *\n * @returns A record mapping every canonical event name to its definition.\n *\n * @example\n * ```typescript\n * import { getAllCanonicalEvents } from \"./normalizer.js\";\n *\n * const events = getAllCanonicalEvents();\n * for (const [name, def] of Object.entries(events)) {\n * console.log(`${name}: ${def.description}`);\n * }\n * ```\n *\n * @public\n */\nexport function getAllCanonicalEvents(): Record<CanonicalHookEvent, CanonicalEventDefinition> {\n return loadMappings().canonicalEvents;\n}\n\n/**\n * Get canonical events filtered by category.\n *\n * @remarks\n * Filters the canonical event list to only those belonging to the specified\n * category. Useful for displaying events grouped by lifecycle phase\n * (e.g. all `\"tool\"` events or all `\"session\"` events).\n *\n * @param category - The hook category to filter by (e.g. `\"session\"`, `\"tool\"`).\n * @returns Array of canonical event names that belong to the specified category.\n *\n * @example\n * ```typescript\n * import { getCanonicalEventsByCategory } from \"./normalizer.js\";\n *\n * const toolEvents = getCanonicalEventsByCategory(\"tool\");\n * // [\"PreToolUse\", \"PostToolUse\", \"PostToolUseFailure\"]\n * ```\n *\n * @public\n */\nexport function getCanonicalEventsByCategory(category: HookCategory): CanonicalHookEvent[] {\n const data = loadMappings();\n return CANONICAL_HOOK_EVENTS.filter((event) => data.canonicalEvents[event].category === category);\n}\n\n/**\n * Get the full hook profile for a provider.\n *\n * @remarks\n * Returns the complete hook configuration for a provider including its\n * hook system type, config path, handler types, and all event mappings.\n * Returns `undefined` if the provider has no hook mappings defined.\n *\n * @param providerId - The provider identifier (e.g. `\"claude-code\"`, `\"gemini-cli\"`).\n * @returns The provider's hook profile, or `undefined` if not found.\n *\n * @example\n * ```typescript\n * import { getProviderHookProfile } from \"./normalizer.js\";\n *\n * const profile = getProviderHookProfile(\"claude-code\");\n * if (profile) {\n * console.log(profile.hookSystem); // \"config\"\n * console.log(profile.experimental); // false\n * }\n * ```\n *\n * @public\n */\nexport function getProviderHookProfile(providerId: string): ProviderHookProfile | undefined {\n const data = loadMappings();\n return data.providerMappings[providerId];\n}\n\n/**\n * Get all provider IDs that have hook mappings.\n *\n * @remarks\n * Returns the list of provider identifiers present in the hook mappings\n * data file. This reflects all providers for which CAAMP has hook\n * translation data, regardless of whether they support any events.\n *\n * @returns Array of provider ID strings.\n *\n * @example\n * ```typescript\n * import { getMappedProviderIds } from \"./normalizer.js\";\n *\n * const ids = getMappedProviderIds();\n * // [\"claude-code\", \"gemini-cli\", \"cursor\", \"kimi\", ...]\n * ```\n *\n * @public\n */\nexport function getMappedProviderIds(): string[] {\n return Object.keys(loadMappings().providerMappings);\n}\n\n// ── Normalization: Canonical → Native ───────────────────────────────\n\n/**\n * Translate a CAAMP canonical event name to the provider's native name.\n *\n * @remarks\n * This is the primary forward-translation function. Given a canonical\n * event and a provider ID, it returns the provider-specific event name\n * that should be used when configuring hooks for that provider.\n * Returns `null` if the provider is unknown or does not support the event.\n *\n * @param canonical - The CAAMP canonical event name to translate.\n * @param providerId - The target provider identifier.\n * @returns The native event name, or `null` if unsupported.\n *\n * @example\n * ```typescript\n * import { toNative } from \"./normalizer.js\";\n *\n * toNative(\"PreToolUse\", \"claude-code\"); // \"PreToolUse\"\n * toNative(\"PreToolUse\", \"gemini-cli\"); // \"BeforeTool\"\n * toNative(\"PreToolUse\", \"cursor\"); // \"preToolUse\"\n * toNative(\"PreToolUse\", \"kimi\"); // null\n * ```\n *\n * @public\n */\nexport function toNative(canonical: CanonicalHookEvent, providerId: string): string | null {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return null;\n const mapping = profile.mappings[canonical];\n return mapping?.supported ? mapping.nativeName : null;\n}\n\n/**\n * Translate a provider-native event name to the CAAMP canonical name.\n *\n * @remarks\n * This is the reverse-translation function. Scans all event mappings\n * for the given provider to find a canonical match for the native name.\n * Returns `null` if no mapping is found or the provider is unknown.\n *\n * @param nativeName - The provider-native event name to look up.\n * @param providerId - The provider identifier to search within.\n * @returns The canonical event name, or `null` if no mapping exists.\n *\n * @example\n * ```typescript\n * import { toCanonical } from \"./normalizer.js\";\n *\n * toCanonical(\"BeforeTool\", \"gemini-cli\"); // \"PreToolUse\"\n * toCanonical(\"stop\", \"cursor\"); // \"ResponseComplete\"\n * toCanonical(\"UserPromptSubmit\", \"claude-code\"); // \"PromptSubmit\"\n * ```\n *\n * @public\n */\nexport function toCanonical(nativeName: string, providerId: string): CanonicalHookEvent | null {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return null;\n\n for (const [canonical, mapping] of Object.entries(profile.mappings)) {\n if (mapping.supported && mapping.nativeName === nativeName) {\n return canonical as CanonicalHookEvent;\n }\n }\n return null;\n}\n\n/**\n * Batch-translate multiple canonical events to native names for a provider.\n *\n * @remarks\n * Translates an array of canonical events in a single call, returning\n * only the events that the provider actually supports. Each result\n * includes category and blocking metadata. Unsupported events are\n * silently excluded from the output.\n *\n * @param canonicals - Array of canonical event names to translate.\n * @param providerId - The target provider identifier.\n * @returns Array of normalized events (only supported ones included).\n *\n * @example\n * ```typescript\n * import { toNativeBatch } from \"./normalizer.js\";\n *\n * const events = toNativeBatch(\n * [\"PreToolUse\", \"PostToolUse\", \"ConfigChange\"],\n * \"claude-code\",\n * );\n * // Returns NormalizedHookEvent[] for supported events only\n * ```\n *\n * @public\n */\nexport function toNativeBatch(\n canonicals: CanonicalHookEvent[],\n providerId: string,\n): NormalizedHookEvent[] {\n const data = loadMappings();\n const profile = data.providerMappings[providerId];\n if (!profile) return [];\n\n const results: NormalizedHookEvent[] = [];\n for (const canonical of canonicals) {\n const mapping = profile.mappings[canonical];\n if (mapping?.supported && mapping.nativeName) {\n results.push({\n canonical,\n native: mapping.nativeName,\n providerId,\n category: data.canonicalEvents[canonical].category,\n canBlock: data.canonicalEvents[canonical].canBlock,\n });\n }\n }\n return results;\n}\n\n// ── Support Queries ─────────────────────────────────────────────────\n\n/**\n * Check if a provider supports a specific canonical hook event.\n *\n * @remarks\n * A quick boolean check that avoids returning the full mapping details.\n * Returns `false` if the provider is unknown or the event is not supported.\n *\n * @param canonical - The canonical event name to check.\n * @param providerId - The provider identifier to check against.\n * @returns `true` if the provider supports this canonical event, `false` otherwise.\n *\n * @example\n * ```typescript\n * import { supportsHook } from \"./normalizer.js\";\n *\n * supportsHook(\"PreToolUse\", \"claude-code\"); // true\n * supportsHook(\"PreToolUse\", \"kimi\"); // false\n * ```\n *\n * @public\n */\nexport function supportsHook(canonical: CanonicalHookEvent, providerId: string): boolean {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return false;\n return profile.mappings[canonical]?.supported ?? false;\n}\n\n/**\n * Get full hook support details for a canonical event on a provider.\n *\n * @remarks\n * Returns a structured result with the native name translation and any\n * notes about support limitations. Unlike `supportsHook()`, this always\n * returns a result object even when the provider is unknown (with\n * `supported: false`).\n *\n * @param canonical - The canonical event name to query.\n * @param providerId - The provider identifier to query against.\n * @returns Support result including native name and optional notes.\n *\n * @example\n * ```typescript\n * import { getHookSupport } from \"./normalizer.js\";\n *\n * const result = getHookSupport(\"PreToolUse\", \"claude-code\");\n * console.log(result.supported); // true\n * console.log(result.native); // \"PreToolUse\"\n * ```\n *\n * @public\n */\nexport function getHookSupport(\n canonical: CanonicalHookEvent,\n providerId: string,\n): HookSupportResult {\n const profile = getProviderHookProfile(providerId);\n if (!profile) {\n return { canonical, supported: false, native: null };\n }\n const mapping = profile.mappings[canonical];\n return {\n canonical,\n supported: mapping?.supported ?? false,\n native: mapping?.nativeName ?? null,\n notes: mapping?.notes,\n };\n}\n\n/**\n * Get all supported canonical events for a provider.\n *\n * @remarks\n * Filters the full canonical event list to only those that the provider\n * supports. Returns an empty array if the provider is unknown.\n *\n * @param providerId - The provider identifier to query.\n * @returns Array of canonical event names the provider supports.\n *\n * @example\n * ```typescript\n * import { getSupportedEvents } from \"./normalizer.js\";\n *\n * const events = getSupportedEvents(\"claude-code\");\n * // [\"SessionStart\", \"SessionEnd\", \"PreToolUse\", ...]\n * ```\n *\n * @public\n */\nexport function getSupportedEvents(providerId: string): CanonicalHookEvent[] {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return [];\n // Only provider events have provider mappings; domain events are not provider-translatable\n return PROVIDER_HOOK_EVENTS.filter((event) => profile.mappings[event]?.supported);\n}\n\n/**\n * Get all unsupported canonical events for a provider.\n *\n * @remarks\n * Returns the complement of `getSupportedEvents()`. If the provider is\n * unknown, all canonical events are returned since none are supported.\n *\n * @param providerId - The provider identifier to query.\n * @returns Array of canonical event names the provider does not support.\n *\n * @example\n * ```typescript\n * import { getUnsupportedEvents } from \"./normalizer.js\";\n *\n * const missing = getUnsupportedEvents(\"kimi\");\n * // Returns all canonical events (kimi has no hook support)\n * ```\n *\n * @public\n */\nexport function getUnsupportedEvents(providerId: string): CanonicalHookEvent[] {\n const profile = getProviderHookProfile(providerId);\n // Only provider events have provider mappings; domain events are not provider-translatable\n if (!profile) return [...PROVIDER_HOOK_EVENTS];\n return PROVIDER_HOOK_EVENTS.filter((event) => !profile.mappings[event]?.supported);\n}\n\n/**\n * Get providers that support a specific canonical event.\n *\n * @remarks\n * Scans all provider mappings and returns the IDs of those that support\n * the given canonical event. Useful for determining which providers can\n * be targeted when configuring a specific hook.\n *\n * @param canonical - The canonical event name to search for.\n * @returns Array of provider IDs that support this event.\n *\n * @example\n * ```typescript\n * import { getProvidersForEvent } from \"./normalizer.js\";\n *\n * const providers = getProvidersForEvent(\"PreToolUse\");\n * // [\"claude-code\", \"gemini-cli\", \"cursor\"]\n * ```\n *\n * @public\n */\nexport function getProvidersForEvent(canonical: CanonicalHookEvent): string[] {\n const data = loadMappings();\n return Object.entries(data.providerMappings)\n .filter(([, profile]) => profile.mappings[canonical]?.supported)\n .map(([id]) => id);\n}\n\n/**\n * Get canonical events common to all specified providers.\n *\n * @remarks\n * Computes the intersection of supported events across multiple providers.\n * Returns only events that every listed provider supports. Useful for\n * determining which hooks can be configured uniformly across a set of\n * target providers.\n *\n * @param providerIds - Array of provider IDs to intersect.\n * @returns Array of canonical events supported by all specified providers.\n *\n * @example\n * ```typescript\n * import { getCommonEvents } from \"./normalizer.js\";\n *\n * const common = getCommonEvents([\"claude-code\", \"gemini-cli\"]);\n * // Returns only events both providers support\n * ```\n *\n * @public\n */\nexport function getCommonEvents(providerIds: string[]): CanonicalHookEvent[] {\n if (providerIds.length === 0) return [];\n // Only provider events are relevant for cross-provider comparison\n return PROVIDER_HOOK_EVENTS.filter((event) => providerIds.every((id) => supportsHook(event, id)));\n}\n\n// ── Summary & Matrix Functions ──────────────────────────────────────\n\n/**\n * Get a summary of hook support for a provider.\n *\n * @remarks\n * Builds an aggregated view of a provider's hook capabilities including\n * counts, coverage percentage, and categorized event lists. Returns\n * `undefined` if the provider has no hook mappings. Used by the CLI\n * `hooks show` command for provider overviews.\n *\n * @param providerId - The provider identifier to summarize.\n * @returns The hook support summary, or `undefined` if the provider is not found.\n *\n * @example\n * ```typescript\n * import { getProviderSummary } from \"./normalizer.js\";\n *\n * const summary = getProviderSummary(\"claude-code\");\n * if (summary) {\n * console.log(`${summary.coverage}% coverage`);\n * console.log(`${summary.supportedCount}/${summary.totalCanonical} events`);\n * }\n * ```\n *\n * @public\n */\nexport function getProviderSummary(providerId: string): ProviderHookSummary | undefined {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return undefined;\n\n const supported = getSupportedEvents(providerId);\n const unsupported = getUnsupportedEvents(providerId);\n\n return {\n providerId,\n hookSystem: profile.hookSystem,\n experimental: profile.experimental,\n supportedCount: supported.length,\n totalCanonical: PROVIDER_HOOK_EVENTS.length,\n supported,\n unsupported,\n providerOnly: profile.providerOnlyEvents,\n coverage: Math.round((supported.length / PROVIDER_HOOK_EVENTS.length) * 100),\n };\n}\n\n/**\n * Build a cross-provider hook support matrix.\n *\n * @remarks\n * Constructs a two-dimensional matrix showing which canonical events are\n * supported by which providers, with native name translations. When no\n * provider IDs are specified, all mapped providers are included. Used by\n * the CLI `hooks matrix` command for comparison tables.\n *\n * @param providerIds - Optional array of provider IDs to include. Defaults to all mapped providers.\n * @returns The cross-provider matrix with events, providers, and mapping data.\n *\n * @example\n * ```typescript\n * import { buildHookMatrix } from \"./normalizer.js\";\n *\n * const matrix = buildHookMatrix([\"claude-code\", \"gemini-cli\"]);\n * for (const event of matrix.events) {\n * for (const provider of matrix.providers) {\n * console.log(`${event} @ ${provider}: ${matrix.matrix[event][provider].supported}`);\n * }\n * }\n * ```\n *\n * @public\n */\nexport function buildHookMatrix(providerIds?: string[]): CrossProviderMatrix {\n const data = loadMappings();\n const ids = providerIds ?? Object.keys(data.providerMappings);\n\n // Provider matrix only covers provider events — domain events have no provider mappings\n const matrix: Record<string, Record<string, HookMapping>> = {};\n for (const event of PROVIDER_HOOK_EVENTS) {\n matrix[event] = {};\n for (const id of ids) {\n const profile = data.providerMappings[id];\n matrix[event][id] = profile?.mappings[event] ?? {\n nativeName: null,\n supported: false,\n };\n }\n }\n\n return {\n events: [...PROVIDER_HOOK_EVENTS],\n providers: ids,\n matrix: matrix as CrossProviderMatrix['matrix'],\n };\n}\n\n/**\n * Get the hook system type for a provider.\n *\n * @remarks\n * Returns the provider's hook system type: `\"config\"` for file-based hooks,\n * `\"plugin\"` for extension-based hooks, or `\"none\"` if the provider does\n * not support hooks. Returns `\"none\"` for unknown providers.\n *\n * @param providerId - The provider identifier to query.\n * @returns The hook system type (`\"config\"`, `\"plugin\"`, or `\"none\"`).\n *\n * @example\n * ```typescript\n * import { getHookSystemType } from \"./normalizer.js\";\n *\n * getHookSystemType(\"claude-code\"); // \"config\"\n * getHookSystemType(\"unknown\"); // \"none\"\n * ```\n *\n * @public\n */\nexport function getHookSystemType(providerId: string): HookSystemType {\n const profile = getProviderHookProfile(providerId);\n return profile?.hookSystem ?? 'none';\n}\n\n/**\n * Get the resolved hook config path for a provider.\n *\n * @remarks\n * Resolves the provider's hook configuration file path by expanding\n * registry template variables (e.g. `~` to the home directory). Returns\n * `null` if the provider has no hook config path defined or is unknown.\n *\n * @param providerId - The provider identifier to query.\n * @returns The resolved filesystem path, or `null` if not available.\n *\n * @example\n * ```typescript\n * import { getHookConfigPath } from \"./normalizer.js\";\n *\n * const path = getHookConfigPath(\"claude-code\");\n * // \"/home/user/.claude/settings.json\" (resolved from template)\n * ```\n *\n * @public\n */\nexport function getHookConfigPath(providerId: string): string | null {\n const profile = getProviderHookProfile(providerId);\n if (!profile?.hookConfigPath) return null;\n return resolveRegistryTemplatePath(profile.hookConfigPath);\n}\n\n/**\n * Get provider-only events (native events with no canonical mapping).\n *\n * @remarks\n * Some providers define hook events that have no equivalent in the CAAMP\n * canonical taxonomy. This function returns those provider-specific event\n * names. Returns an empty array for unknown providers.\n *\n * @param providerId - The provider identifier to query.\n * @returns Array of native event names unique to this provider.\n *\n * @example\n * ```typescript\n * import { getProviderOnlyEvents } from \"./normalizer.js\";\n *\n * const extras = getProviderOnlyEvents(\"claude-code\");\n * // Returns any events specific to Claude Code with no canonical equivalent\n * ```\n *\n * @public\n */\nexport function getProviderOnlyEvents(providerId: string): string[] {\n const profile = getProviderHookProfile(providerId);\n return profile?.providerOnlyEvents ?? [];\n}\n\n// ── Multi-Provider Translation ──────────────────────────────────────\n\n/**\n * Translate a canonical event to native names across multiple providers.\n *\n * @remarks\n * Performs a fan-out translation of a single canonical event to all\n * specified providers simultaneously. The result record only includes\n * providers that actually support the event — unsupported providers\n * are silently excluded.\n *\n * @param canonical - The canonical event name to translate.\n * @param providerIds - Array of provider IDs to translate for.\n * @returns Record mapping provider IDs to their native event names (supported only).\n *\n * @example\n * ```typescript\n * import { translateToAll } from \"./normalizer.js\";\n *\n * const result = translateToAll(\"PreToolUse\", [\"claude-code\", \"gemini-cli\", \"kimi\"]);\n * // { \"claude-code\": \"PreToolUse\", \"gemini-cli\": \"BeforeTool\" }\n * // (kimi excluded — unsupported)\n * ```\n *\n * @public\n */\nexport function translateToAll(\n canonical: CanonicalHookEvent,\n providerIds: string[],\n): Record<string, string> {\n const result: Record<string, string> = {};\n for (const id of providerIds) {\n const native = toNative(canonical, id);\n if (native) {\n result[id] = native;\n }\n }\n return result;\n}\n\n/**\n * Find the best canonical match for a native event name across all providers.\n *\n * @remarks\n * Scans every provider's mappings to find canonical events that match the\n * given native name. Useful when you have a native name but do not know\n * which provider it belongs to. Multiple results are possible if different\n * providers use the same native name for different canonical events.\n *\n * @param nativeName - The provider-native event name to resolve.\n * @returns Array of matches, each containing the provider ID and canonical event name.\n *\n * @example\n * ```typescript\n * import { resolveNativeEvent } from \"./normalizer.js\";\n *\n * const matches = resolveNativeEvent(\"BeforeTool\");\n * // [{ providerId: \"gemini-cli\", canonical: \"PreToolUse\" }]\n * ```\n *\n * @public\n */\nexport function resolveNativeEvent(nativeName: string): Array<{\n providerId: string;\n canonical: CanonicalHookEvent;\n}> {\n const data = loadMappings();\n const results: Array<{ providerId: string; canonical: CanonicalHookEvent }> = [];\n\n for (const [providerId, profile] of Object.entries(data.providerMappings)) {\n for (const [canonical, mapping] of Object.entries(profile.mappings)) {\n if (mapping.supported && mapping.nativeName === nativeName) {\n results.push({ providerId, canonical: canonical as CanonicalHookEvent });\n }\n }\n }\n\n return results;\n}\n\n/**\n * Get the version of the hook mappings data.\n *\n * @remarks\n * Returns the semver version string from the hook mappings JSON file.\n * This can be used to check compatibility or display the data version\n * in diagnostic output.\n *\n * @returns The semver version string of the loaded hook mappings data.\n *\n * @example\n * ```typescript\n * import { getHookMappingsVersion } from \"./normalizer.js\";\n *\n * const version = getHookMappingsVersion();\n * console.log(`Hook mappings v${version}`);\n * ```\n *\n * @public\n */\nexport function getHookMappingsVersion(): string {\n return loadMappings().version;\n}\n","// @generated — DO NOT EDIT. Source: providers/hook-mappings.json\n// Generated by scripts/generate-hook-types.ts\n\n/**\n * All hook event categories (8 total).\n * @public\n */\nexport const HOOK_CATEGORIES = [\n 'agent',\n 'context',\n 'memory',\n 'pipeline',\n 'prompt',\n 'session',\n 'task',\n 'tool',\n] as const;\n\n/** Union type of valid hook category strings. */\nexport type HookCategory = (typeof HOOK_CATEGORIES)[number];\n\n/**\n * Event source types: domain, provider.\n * @public\n */\nexport const EVENT_SOURCES = ['domain', 'provider'] as const;\n\n/** Union type of valid event source types. */\nexport type EventSource = (typeof EVENT_SOURCES)[number];\n\n/**\n * All canonical hook events (31 total: 16 provider, 15 domain).\n *\n * This replaces the previously hardcoded tuple in types.ts.\n * Single source of truth: providers/hook-mappings.json\n * @public\n */\nexport const CANONICAL_HOOK_EVENTS = [\n // Provider events\n 'ConfigChange',\n 'Notification',\n 'PermissionRequest',\n 'PostCompact',\n 'PostModel',\n 'PostToolUse',\n 'PostToolUseFailure',\n 'PreCompact',\n 'PreModel',\n 'PreToolUse',\n 'PromptSubmit',\n 'ResponseComplete',\n 'SessionEnd',\n 'SessionStart',\n 'SubagentStart',\n 'SubagentStop',\n // Domain events\n 'ApprovalExpired',\n 'ApprovalGranted',\n 'ApprovalRequested',\n 'MemoryDecisionStored',\n 'MemoryLearningStored',\n 'MemoryObserved',\n 'MemoryPatternStored',\n 'PipelineManifestAppended',\n 'PipelineStageCompleted',\n 'SessionEnded',\n 'SessionStarted',\n 'TaskBlocked',\n 'TaskCompleted',\n 'TaskCreated',\n 'TaskStarted',\n] as const;\n\n/** Union type of all canonical hook event names. */\nexport type CanonicalHookEvent = (typeof CANONICAL_HOOK_EVENTS)[number];\n\n/** Provider-sourced events only (original 16 CAAMP events). */\nexport const PROVIDER_HOOK_EVENTS = [\n 'ConfigChange',\n 'Notification',\n 'PermissionRequest',\n 'PostCompact',\n 'PostModel',\n 'PostToolUse',\n 'PostToolUseFailure',\n 'PreCompact',\n 'PreModel',\n 'PreToolUse',\n 'PromptSubmit',\n 'ResponseComplete',\n 'SessionEnd',\n 'SessionStart',\n 'SubagentStart',\n 'SubagentStop',\n] as const;\n\n/** Domain-sourced events only (CLEO business events). */\nexport const DOMAIN_HOOK_EVENTS = [\n 'ApprovalExpired',\n 'ApprovalGranted',\n 'ApprovalRequested',\n 'MemoryDecisionStored',\n 'MemoryLearningStored',\n 'MemoryObserved',\n 'MemoryPatternStored',\n 'PipelineManifestAppended',\n 'PipelineStageCompleted',\n 'SessionEnded',\n 'SessionStarted',\n 'TaskBlocked',\n 'TaskCompleted',\n 'TaskCreated',\n 'TaskStarted',\n] as const;\n\n/** Metadata for each canonical event, derived from hook-mappings.json. */\nexport const EVENT_METADATA: Record<\n CanonicalHookEvent,\n {\n category: HookCategory;\n source: EventSource;\n canBlock: boolean;\n description: string;\n }\n> = {\n ConfigChange: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'Configuration file changes during a session',\n },\n Notification: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'System notification or alert is emitted',\n },\n PermissionRequest: {\n category: 'tool',\n source: 'provider',\n canBlock: true,\n description: 'Permission dialog appears for a tool action',\n },\n PostCompact: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'After context window compaction completes',\n },\n PostModel: {\n category: 'agent',\n source: 'provider',\n canBlock: false,\n description: 'After receiving an LLM response',\n },\n PostToolUse: {\n category: 'tool',\n source: 'provider',\n canBlock: false,\n description: 'After a tool call succeeds',\n },\n PostToolUseFailure: {\n category: 'tool',\n source: 'provider',\n canBlock: false,\n description: 'After a tool call fails or times out',\n },\n PreCompact: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'Before context window compaction',\n },\n PreModel: {\n category: 'agent',\n source: 'provider',\n canBlock: true,\n description: 'Before sending a request to the LLM',\n },\n PreToolUse: {\n category: 'tool',\n source: 'provider',\n canBlock: true,\n description: 'Before a tool call executes (can block/modify)',\n },\n PromptSubmit: {\n category: 'prompt',\n source: 'provider',\n canBlock: true,\n description: 'User submits a prompt, before agent processes it',\n },\n ResponseComplete: {\n category: 'prompt',\n source: 'provider',\n canBlock: false,\n description: 'Agent finishes responding to a turn',\n },\n SessionEnd: {\n category: 'session',\n source: 'provider',\n canBlock: false,\n description: 'Session terminates or exits',\n },\n SessionStart: {\n category: 'session',\n source: 'provider',\n canBlock: false,\n description: 'Session begins, resumes, or is cleared',\n },\n SubagentStart: {\n category: 'agent',\n source: 'provider',\n canBlock: true,\n description: 'A subagent is spawned',\n },\n SubagentStop: {\n category: 'agent',\n source: 'provider',\n canBlock: false,\n description: 'A subagent finishes execution',\n },\n ApprovalExpired: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A workflow approval token has expired without response',\n },\n ApprovalGranted: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A workflow approval has been granted via /approve token',\n },\n ApprovalRequested: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A workflow approval gate has fired, awaiting human approval',\n },\n MemoryDecisionStored: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'A decision has been stored via memory.decision.store',\n },\n MemoryLearningStored: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'A learning has been stored via memory.learning.store',\n },\n MemoryObserved: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'An observation has been recorded to brain.db via memory.observe',\n },\n MemoryPatternStored: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'A pattern has been stored via memory.pattern.store',\n },\n PipelineManifestAppended: {\n category: 'pipeline',\n source: 'domain',\n canBlock: false,\n description: 'A manifest entry has been appended to MANIFEST.jsonl',\n },\n PipelineStageCompleted: {\n category: 'pipeline',\n source: 'domain',\n canBlock: false,\n description: 'A LOOM lifecycle gate has passed validation',\n },\n SessionEnded: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO session has ended via session.end',\n },\n SessionStarted: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO session has been started via session.start',\n },\n TaskBlocked: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been blocked',\n },\n TaskCompleted: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been marked complete via tasks.complete',\n },\n TaskCreated: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been created via tasks.add',\n },\n TaskStarted: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been started via tasks.start',\n },\n} as const;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/core/hooks/normalizer.ts","../src/core/hooks/generated.ts"],"sourcesContent":["/**\n * CAAMP Hooks Normalizer\n *\n * Translates between CAAMP canonical hook events and provider-native\n * event names. Provides query functions for hook support, cross-provider\n * comparison, and event normalization.\n *\n * This module follows the same pattern as `src/core/mcp/transforms.ts` —\n * a translation layer that lets consumers use one canonical interface\n * while CAAMP handles provider-specific differences.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { resolveRegistryTemplatePath } from '../paths/standard.js';\nimport type {\n CanonicalEventDefinition,\n CanonicalHookEvent,\n CrossProviderMatrix,\n HookCategory,\n HookMapping,\n HookMappingsFile,\n HookSupportResult,\n HookSystemType,\n NormalizedHookEvent,\n ProviderHookProfile,\n ProviderHookSummary,\n} from './types.js';\nimport { CANONICAL_HOOK_EVENTS, PROVIDER_HOOK_EVENTS } from './types.js';\n\n// ── Data Loading ────────────────────────────────────────────────────\n\nlet _mappings: HookMappingsFile | null = null;\n\nfunction findMappingsPath(): string {\n const thisDir = dirname(fileURLToPath(import.meta.url));\n // src/core/hooks/ -> providers/hook-mappings.json\n return join(thisDir, '..', '..', '..', 'providers', 'hook-mappings.json');\n}\n\nfunction loadMappings(): HookMappingsFile {\n if (_mappings) return _mappings;\n const mappingsPath = findMappingsPath();\n if (!existsSync(mappingsPath)) {\n // Return an empty but structurally valid mappings object when the file is missing.\n // This avoids ENOENT crashes in installed environments where the providers/\n // directory may not be bundled (e.g. global npm installs with hoisted deps).\n const empty: HookMappingsFile = {\n version: '0.0.0',\n lastUpdated: new Date().toISOString(),\n description: 'Empty fallback — hook-mappings.json not found',\n canonicalEvents: {} as HookMappingsFile['canonicalEvents'],\n providerMappings: {},\n };\n _mappings = empty;\n return empty;\n }\n const raw = readFileSync(mappingsPath, 'utf-8');\n _mappings = JSON.parse(raw) as HookMappingsFile;\n return _mappings;\n}\n\n/**\n * Reset the cached hook mappings data.\n *\n * @remarks\n * Clears the in-memory cache so the next query function call will\n * re-read `providers/hook-mappings.json` from disk. This is primarily\n * intended for test isolation — production code should not need to call this.\n *\n * @example\n * ```typescript\n * import { resetHookMappings, getHookMappingsVersion } from \"./normalizer.js\";\n *\n * // Force a fresh load from disk\n * resetHookMappings();\n * const version = getHookMappingsVersion();\n * ```\n *\n * @public\n */\nexport function resetHookMappings(): void {\n _mappings = null;\n}\n\n// ── Core Query Functions ────────────────────────────────────────────\n\n/**\n * Get the canonical event definition (category, description, canBlock).\n *\n * @remarks\n * Looks up the definition from the hook mappings data file. The returned\n * object contains the event's category, human-readable description, and\n * whether handlers can block the associated action.\n *\n * @param event - The canonical event name to look up.\n * @returns The event definition containing category, description, and canBlock flag.\n *\n * @example\n * ```typescript\n * import { getCanonicalEvent } from \"./normalizer.js\";\n *\n * const def = getCanonicalEvent(\"PreToolUse\");\n * console.log(def.category); // \"tool\"\n * console.log(def.canBlock); // true\n * ```\n *\n * @public\n */\nexport function getCanonicalEvent(event: CanonicalHookEvent): CanonicalEventDefinition {\n const data = loadMappings();\n return data.canonicalEvents[event];\n}\n\n/**\n * Get all canonical event definitions.\n *\n * @remarks\n * Returns the complete map of canonical event names to their definitions.\n * Useful for iterating over the full taxonomy or building UI displays\n * of all available events.\n *\n * @returns A record mapping every canonical event name to its definition.\n *\n * @example\n * ```typescript\n * import { getAllCanonicalEvents } from \"./normalizer.js\";\n *\n * const events = getAllCanonicalEvents();\n * for (const [name, def] of Object.entries(events)) {\n * console.log(`${name}: ${def.description}`);\n * }\n * ```\n *\n * @public\n */\nexport function getAllCanonicalEvents(): Record<CanonicalHookEvent, CanonicalEventDefinition> {\n return loadMappings().canonicalEvents;\n}\n\n/**\n * Get canonical events filtered by category.\n *\n * @remarks\n * Filters the canonical event list to only those belonging to the specified\n * category. Useful for displaying events grouped by lifecycle phase\n * (e.g. all `\"tool\"` events or all `\"session\"` events).\n *\n * @param category - The hook category to filter by (e.g. `\"session\"`, `\"tool\"`).\n * @returns Array of canonical event names that belong to the specified category.\n *\n * @example\n * ```typescript\n * import { getCanonicalEventsByCategory } from \"./normalizer.js\";\n *\n * const toolEvents = getCanonicalEventsByCategory(\"tool\");\n * // [\"PreToolUse\", \"PostToolUse\", \"PostToolUseFailure\"]\n * ```\n *\n * @public\n */\nexport function getCanonicalEventsByCategory(category: HookCategory): CanonicalHookEvent[] {\n const data = loadMappings();\n return CANONICAL_HOOK_EVENTS.filter((event) => data.canonicalEvents[event].category === category);\n}\n\n/**\n * Get the full hook profile for a provider.\n *\n * @remarks\n * Returns the complete hook configuration for a provider including its\n * hook system type, config path, handler types, and all event mappings.\n * Returns `undefined` if the provider has no hook mappings defined.\n *\n * @param providerId - The provider identifier (e.g. `\"claude-code\"`, `\"gemini-cli\"`).\n * @returns The provider's hook profile, or `undefined` if not found.\n *\n * @example\n * ```typescript\n * import { getProviderHookProfile } from \"./normalizer.js\";\n *\n * const profile = getProviderHookProfile(\"claude-code\");\n * if (profile) {\n * console.log(profile.hookSystem); // \"config\"\n * console.log(profile.experimental); // false\n * }\n * ```\n *\n * @public\n */\nexport function getProviderHookProfile(providerId: string): ProviderHookProfile | undefined {\n const data = loadMappings();\n return data.providerMappings[providerId];\n}\n\n/**\n * Get all provider IDs that have hook mappings.\n *\n * @remarks\n * Returns the list of provider identifiers present in the hook mappings\n * data file. This reflects all providers for which CAAMP has hook\n * translation data, regardless of whether they support any events.\n *\n * @returns Array of provider ID strings.\n *\n * @example\n * ```typescript\n * import { getMappedProviderIds } from \"./normalizer.js\";\n *\n * const ids = getMappedProviderIds();\n * // [\"claude-code\", \"gemini-cli\", \"cursor\", \"kimi\", ...]\n * ```\n *\n * @public\n */\nexport function getMappedProviderIds(): string[] {\n return Object.keys(loadMappings().providerMappings);\n}\n\n// ── Normalization: Canonical → Native ───────────────────────────────\n\n/**\n * Translate a CAAMP canonical event name to the provider's native name.\n *\n * @remarks\n * This is the primary forward-translation function. Given a canonical\n * event and a provider ID, it returns the provider-specific event name\n * that should be used when configuring hooks for that provider.\n * Returns `null` if the provider is unknown or does not support the event.\n *\n * @param canonical - The CAAMP canonical event name to translate.\n * @param providerId - The target provider identifier.\n * @returns The native event name, or `null` if unsupported.\n *\n * @example\n * ```typescript\n * import { toNative } from \"./normalizer.js\";\n *\n * toNative(\"PreToolUse\", \"claude-code\"); // \"PreToolUse\"\n * toNative(\"PreToolUse\", \"gemini-cli\"); // \"BeforeTool\"\n * toNative(\"PreToolUse\", \"cursor\"); // \"preToolUse\"\n * toNative(\"PreToolUse\", \"kimi\"); // null\n * ```\n *\n * @public\n */\nexport function toNative(canonical: CanonicalHookEvent, providerId: string): string | null {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return null;\n const mapping = profile.mappings[canonical];\n return mapping?.supported ? mapping.nativeName : null;\n}\n\n/**\n * Translate a provider-native event name to the CAAMP canonical name.\n *\n * @remarks\n * This is the reverse-translation function. Scans all event mappings\n * for the given provider to find a canonical match for the native name.\n * Returns `null` if no mapping is found or the provider is unknown.\n *\n * @param nativeName - The provider-native event name to look up.\n * @param providerId - The provider identifier to search within.\n * @returns The canonical event name, or `null` if no mapping exists.\n *\n * @example\n * ```typescript\n * import { toCanonical } from \"./normalizer.js\";\n *\n * toCanonical(\"BeforeTool\", \"gemini-cli\"); // \"PreToolUse\"\n * toCanonical(\"stop\", \"cursor\"); // \"ResponseComplete\"\n * toCanonical(\"UserPromptSubmit\", \"claude-code\"); // \"PromptSubmit\"\n * ```\n *\n * @public\n */\nexport function toCanonical(nativeName: string, providerId: string): CanonicalHookEvent | null {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return null;\n\n for (const [canonical, mapping] of Object.entries(profile.mappings)) {\n if (mapping.supported && mapping.nativeName === nativeName) {\n return canonical as CanonicalHookEvent;\n }\n }\n return null;\n}\n\n/**\n * Batch-translate multiple canonical events to native names for a provider.\n *\n * @remarks\n * Translates an array of canonical events in a single call, returning\n * only the events that the provider actually supports. Each result\n * includes category and blocking metadata. Unsupported events are\n * silently excluded from the output.\n *\n * @param canonicals - Array of canonical event names to translate.\n * @param providerId - The target provider identifier.\n * @returns Array of normalized events (only supported ones included).\n *\n * @example\n * ```typescript\n * import { toNativeBatch } from \"./normalizer.js\";\n *\n * const events = toNativeBatch(\n * [\"PreToolUse\", \"PostToolUse\", \"ConfigChange\"],\n * \"claude-code\",\n * );\n * // Returns NormalizedHookEvent[] for supported events only\n * ```\n *\n * @public\n */\nexport function toNativeBatch(\n canonicals: CanonicalHookEvent[],\n providerId: string,\n): NormalizedHookEvent[] {\n const data = loadMappings();\n const profile = data.providerMappings[providerId];\n if (!profile) return [];\n\n const results: NormalizedHookEvent[] = [];\n for (const canonical of canonicals) {\n const mapping = profile.mappings[canonical];\n if (mapping?.supported && mapping.nativeName) {\n results.push({\n canonical,\n native: mapping.nativeName,\n providerId,\n category: data.canonicalEvents[canonical].category,\n canBlock: data.canonicalEvents[canonical].canBlock,\n });\n }\n }\n return results;\n}\n\n// ── Support Queries ─────────────────────────────────────────────────\n\n/**\n * Check if a provider supports a specific canonical hook event.\n *\n * @remarks\n * A quick boolean check that avoids returning the full mapping details.\n * Returns `false` if the provider is unknown or the event is not supported.\n *\n * @param canonical - The canonical event name to check.\n * @param providerId - The provider identifier to check against.\n * @returns `true` if the provider supports this canonical event, `false` otherwise.\n *\n * @example\n * ```typescript\n * import { supportsHook } from \"./normalizer.js\";\n *\n * supportsHook(\"PreToolUse\", \"claude-code\"); // true\n * supportsHook(\"PreToolUse\", \"kimi\"); // false\n * ```\n *\n * @public\n */\nexport function supportsHook(canonical: CanonicalHookEvent, providerId: string): boolean {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return false;\n return profile.mappings[canonical]?.supported ?? false;\n}\n\n/**\n * Get full hook support details for a canonical event on a provider.\n *\n * @remarks\n * Returns a structured result with the native name translation and any\n * notes about support limitations. Unlike `supportsHook()`, this always\n * returns a result object even when the provider is unknown (with\n * `supported: false`).\n *\n * @param canonical - The canonical event name to query.\n * @param providerId - The provider identifier to query against.\n * @returns Support result including native name and optional notes.\n *\n * @example\n * ```typescript\n * import { getHookSupport } from \"./normalizer.js\";\n *\n * const result = getHookSupport(\"PreToolUse\", \"claude-code\");\n * console.log(result.supported); // true\n * console.log(result.native); // \"PreToolUse\"\n * ```\n *\n * @public\n */\nexport function getHookSupport(\n canonical: CanonicalHookEvent,\n providerId: string,\n): HookSupportResult {\n const profile = getProviderHookProfile(providerId);\n if (!profile) {\n return { canonical, supported: false, native: null };\n }\n const mapping = profile.mappings[canonical];\n return {\n canonical,\n supported: mapping?.supported ?? false,\n native: mapping?.nativeName ?? null,\n notes: mapping?.notes,\n };\n}\n\n/**\n * Get all supported canonical events for a provider.\n *\n * @remarks\n * Filters the full canonical event list to only those that the provider\n * supports. Returns an empty array if the provider is unknown.\n *\n * @param providerId - The provider identifier to query.\n * @returns Array of canonical event names the provider supports.\n *\n * @example\n * ```typescript\n * import { getSupportedEvents } from \"./normalizer.js\";\n *\n * const events = getSupportedEvents(\"claude-code\");\n * // [\"SessionStart\", \"SessionEnd\", \"PreToolUse\", ...]\n * ```\n *\n * @public\n */\nexport function getSupportedEvents(providerId: string): CanonicalHookEvent[] {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return [];\n // Only provider events have provider mappings; domain events are not provider-translatable\n return PROVIDER_HOOK_EVENTS.filter((event) => profile.mappings[event]?.supported);\n}\n\n/**\n * Get all unsupported canonical events for a provider.\n *\n * @remarks\n * Returns the complement of `getSupportedEvents()`. If the provider is\n * unknown, all canonical events are returned since none are supported.\n *\n * @param providerId - The provider identifier to query.\n * @returns Array of canonical event names the provider does not support.\n *\n * @example\n * ```typescript\n * import { getUnsupportedEvents } from \"./normalizer.js\";\n *\n * const missing = getUnsupportedEvents(\"kimi\");\n * // Returns all canonical events (kimi has no hook support)\n * ```\n *\n * @public\n */\nexport function getUnsupportedEvents(providerId: string): CanonicalHookEvent[] {\n const profile = getProviderHookProfile(providerId);\n // Only provider events have provider mappings; domain events are not provider-translatable\n if (!profile) return [...PROVIDER_HOOK_EVENTS];\n return PROVIDER_HOOK_EVENTS.filter((event) => !profile.mappings[event]?.supported);\n}\n\n/**\n * Get providers that support a specific canonical event.\n *\n * @remarks\n * Scans all provider mappings and returns the IDs of those that support\n * the given canonical event. Useful for determining which providers can\n * be targeted when configuring a specific hook.\n *\n * @param canonical - The canonical event name to search for.\n * @returns Array of provider IDs that support this event.\n *\n * @example\n * ```typescript\n * import { getProvidersForEvent } from \"./normalizer.js\";\n *\n * const providers = getProvidersForEvent(\"PreToolUse\");\n * // [\"claude-code\", \"gemini-cli\", \"cursor\"]\n * ```\n *\n * @public\n */\nexport function getProvidersForEvent(canonical: CanonicalHookEvent): string[] {\n const data = loadMappings();\n return Object.entries(data.providerMappings)\n .filter(([, profile]) => profile.mappings[canonical]?.supported)\n .map(([id]) => id);\n}\n\n/**\n * Get canonical events common to all specified providers.\n *\n * @remarks\n * Computes the intersection of supported events across multiple providers.\n * Returns only events that every listed provider supports. Useful for\n * determining which hooks can be configured uniformly across a set of\n * target providers.\n *\n * @param providerIds - Array of provider IDs to intersect.\n * @returns Array of canonical events supported by all specified providers.\n *\n * @example\n * ```typescript\n * import { getCommonEvents } from \"./normalizer.js\";\n *\n * const common = getCommonEvents([\"claude-code\", \"gemini-cli\"]);\n * // Returns only events both providers support\n * ```\n *\n * @public\n */\nexport function getCommonEvents(providerIds: string[]): CanonicalHookEvent[] {\n if (providerIds.length === 0) return [];\n // Only provider events are relevant for cross-provider comparison\n return PROVIDER_HOOK_EVENTS.filter((event) => providerIds.every((id) => supportsHook(event, id)));\n}\n\n// ── Summary & Matrix Functions ──────────────────────────────────────\n\n/**\n * Get a summary of hook support for a provider.\n *\n * @remarks\n * Builds an aggregated view of a provider's hook capabilities including\n * counts, coverage percentage, and categorized event lists. Returns\n * `undefined` if the provider has no hook mappings. Used by the CLI\n * `hooks show` command for provider overviews.\n *\n * @param providerId - The provider identifier to summarize.\n * @returns The hook support summary, or `undefined` if the provider is not found.\n *\n * @example\n * ```typescript\n * import { getProviderSummary } from \"./normalizer.js\";\n *\n * const summary = getProviderSummary(\"claude-code\");\n * if (summary) {\n * console.log(`${summary.coverage}% coverage`);\n * console.log(`${summary.supportedCount}/${summary.totalCanonical} events`);\n * }\n * ```\n *\n * @public\n */\nexport function getProviderSummary(providerId: string): ProviderHookSummary | undefined {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return undefined;\n\n const supported = getSupportedEvents(providerId);\n const unsupported = getUnsupportedEvents(providerId);\n\n return {\n providerId,\n hookSystem: profile.hookSystem,\n experimental: profile.experimental,\n supportedCount: supported.length,\n totalCanonical: PROVIDER_HOOK_EVENTS.length,\n supported,\n unsupported,\n providerOnly: profile.providerOnlyEvents,\n coverage: Math.round((supported.length / PROVIDER_HOOK_EVENTS.length) * 100),\n };\n}\n\n/**\n * Build a cross-provider hook support matrix.\n *\n * @remarks\n * Constructs a two-dimensional matrix showing which canonical events are\n * supported by which providers, with native name translations. When no\n * provider IDs are specified, all mapped providers are included. Used by\n * the CLI `hooks matrix` command for comparison tables.\n *\n * @param providerIds - Optional array of provider IDs to include. Defaults to all mapped providers.\n * @returns The cross-provider matrix with events, providers, and mapping data.\n *\n * @example\n * ```typescript\n * import { buildHookMatrix } from \"./normalizer.js\";\n *\n * const matrix = buildHookMatrix([\"claude-code\", \"gemini-cli\"]);\n * for (const event of matrix.events) {\n * for (const provider of matrix.providers) {\n * console.log(`${event} @ ${provider}: ${matrix.matrix[event][provider].supported}`);\n * }\n * }\n * ```\n *\n * @public\n */\nexport function buildHookMatrix(providerIds?: string[]): CrossProviderMatrix {\n const data = loadMappings();\n const ids = providerIds ?? Object.keys(data.providerMappings);\n\n // Provider matrix only covers provider events — domain events have no provider mappings\n const matrix: Record<string, Record<string, HookMapping>> = {};\n for (const event of PROVIDER_HOOK_EVENTS) {\n matrix[event] = {};\n for (const id of ids) {\n const profile = data.providerMappings[id];\n matrix[event][id] = profile?.mappings[event] ?? {\n nativeName: null,\n supported: false,\n };\n }\n }\n\n return {\n events: [...PROVIDER_HOOK_EVENTS],\n providers: ids,\n matrix: matrix as CrossProviderMatrix['matrix'],\n };\n}\n\n/**\n * Get the hook system type for a provider.\n *\n * @remarks\n * Returns the provider's hook system type: `\"config\"` for file-based hooks,\n * `\"plugin\"` for extension-based hooks, or `\"none\"` if the provider does\n * not support hooks. Returns `\"none\"` for unknown providers.\n *\n * @param providerId - The provider identifier to query.\n * @returns The hook system type (`\"config\"`, `\"plugin\"`, or `\"none\"`).\n *\n * @example\n * ```typescript\n * import { getHookSystemType } from \"./normalizer.js\";\n *\n * getHookSystemType(\"claude-code\"); // \"config\"\n * getHookSystemType(\"unknown\"); // \"none\"\n * ```\n *\n * @public\n */\nexport function getHookSystemType(providerId: string): HookSystemType {\n const profile = getProviderHookProfile(providerId);\n return profile?.hookSystem ?? 'none';\n}\n\n/**\n * Get the resolved hook config path for a provider.\n *\n * @remarks\n * Resolves the provider's hook configuration file path by expanding\n * registry template variables (e.g. `~` to the home directory). Returns\n * `null` if the provider has no hook config path defined or is unknown.\n *\n * @param providerId - The provider identifier to query.\n * @returns The resolved filesystem path, or `null` if not available.\n *\n * @example\n * ```typescript\n * import { getHookConfigPath } from \"./normalizer.js\";\n *\n * const path = getHookConfigPath(\"claude-code\");\n * // \"/home/user/.claude/settings.json\" (resolved from template)\n * ```\n *\n * @public\n */\nexport function getHookConfigPath(providerId: string): string | null {\n const profile = getProviderHookProfile(providerId);\n if (!profile?.hookConfigPath) return null;\n return resolveRegistryTemplatePath(profile.hookConfigPath);\n}\n\n/**\n * Get provider-only events (native events with no canonical mapping).\n *\n * @remarks\n * Some providers define hook events that have no equivalent in the CAAMP\n * canonical taxonomy. This function returns those provider-specific event\n * names. Returns an empty array for unknown providers.\n *\n * @param providerId - The provider identifier to query.\n * @returns Array of native event names unique to this provider.\n *\n * @example\n * ```typescript\n * import { getProviderOnlyEvents } from \"./normalizer.js\";\n *\n * const extras = getProviderOnlyEvents(\"claude-code\");\n * // Returns any events specific to Claude Code with no canonical equivalent\n * ```\n *\n * @public\n */\nexport function getProviderOnlyEvents(providerId: string): string[] {\n const profile = getProviderHookProfile(providerId);\n return profile?.providerOnlyEvents ?? [];\n}\n\n// ── Multi-Provider Translation ──────────────────────────────────────\n\n/**\n * Translate a canonical event to native names across multiple providers.\n *\n * @remarks\n * Performs a fan-out translation of a single canonical event to all\n * specified providers simultaneously. The result record only includes\n * providers that actually support the event — unsupported providers\n * are silently excluded.\n *\n * @param canonical - The canonical event name to translate.\n * @param providerIds - Array of provider IDs to translate for.\n * @returns Record mapping provider IDs to their native event names (supported only).\n *\n * @example\n * ```typescript\n * import { translateToAll } from \"./normalizer.js\";\n *\n * const result = translateToAll(\"PreToolUse\", [\"claude-code\", \"gemini-cli\", \"kimi\"]);\n * // { \"claude-code\": \"PreToolUse\", \"gemini-cli\": \"BeforeTool\" }\n * // (kimi excluded — unsupported)\n * ```\n *\n * @public\n */\nexport function translateToAll(\n canonical: CanonicalHookEvent,\n providerIds: string[],\n): Record<string, string> {\n const result: Record<string, string> = {};\n for (const id of providerIds) {\n const native = toNative(canonical, id);\n if (native) {\n result[id] = native;\n }\n }\n return result;\n}\n\n/**\n * Find the best canonical match for a native event name across all providers.\n *\n * @remarks\n * Scans every provider's mappings to find canonical events that match the\n * given native name. Useful when you have a native name but do not know\n * which provider it belongs to. Multiple results are possible if different\n * providers use the same native name for different canonical events.\n *\n * @param nativeName - The provider-native event name to resolve.\n * @returns Array of matches, each containing the provider ID and canonical event name.\n *\n * @example\n * ```typescript\n * import { resolveNativeEvent } from \"./normalizer.js\";\n *\n * const matches = resolveNativeEvent(\"BeforeTool\");\n * // [{ providerId: \"gemini-cli\", canonical: \"PreToolUse\" }]\n * ```\n *\n * @public\n */\nexport function resolveNativeEvent(nativeName: string): Array<{\n providerId: string;\n canonical: CanonicalHookEvent;\n}> {\n const data = loadMappings();\n const results: Array<{ providerId: string; canonical: CanonicalHookEvent }> = [];\n\n for (const [providerId, profile] of Object.entries(data.providerMappings)) {\n for (const [canonical, mapping] of Object.entries(profile.mappings)) {\n if (mapping.supported && mapping.nativeName === nativeName) {\n results.push({ providerId, canonical: canonical as CanonicalHookEvent });\n }\n }\n }\n\n return results;\n}\n\n/**\n * Get the version of the hook mappings data.\n *\n * @remarks\n * Returns the semver version string from the hook mappings JSON file.\n * This can be used to check compatibility or display the data version\n * in diagnostic output.\n *\n * @returns The semver version string of the loaded hook mappings data.\n *\n * @example\n * ```typescript\n * import { getHookMappingsVersion } from \"./normalizer.js\";\n *\n * const version = getHookMappingsVersion();\n * console.log(`Hook mappings v${version}`);\n * ```\n *\n * @public\n */\nexport function getHookMappingsVersion(): string {\n return loadMappings().version;\n}\n","// @generated — DO NOT EDIT. Source: providers/hook-mappings.json\n// Generated by scripts/generate-hook-types.ts\n\n/**\n * All hook event categories (8 total).\n * @public\n */\nexport const HOOK_CATEGORIES = [\n 'agent',\n 'context',\n 'memory',\n 'pipeline',\n 'prompt',\n 'session',\n 'task',\n 'tool',\n] as const;\n\n/** Union type of valid hook category strings. */\nexport type HookCategory = (typeof HOOK_CATEGORIES)[number];\n\n/**\n * Event source types: domain, provider.\n * @public\n */\nexport const EVENT_SOURCES = ['domain', 'provider'] as const;\n\n/** Union type of valid event source types. */\nexport type EventSource = (typeof EVENT_SOURCES)[number];\n\n/**\n * All canonical hook events (31 total: 16 provider, 15 domain).\n *\n * This replaces the previously hardcoded tuple in types.ts.\n * Single source of truth: providers/hook-mappings.json\n * @public\n */\nexport const CANONICAL_HOOK_EVENTS = [\n // Provider events\n 'ConfigChange',\n 'Notification',\n 'PermissionRequest',\n 'PostCompact',\n 'PostModel',\n 'PostToolUse',\n 'PostToolUseFailure',\n 'PreCompact',\n 'PreModel',\n 'PreToolUse',\n 'PromptSubmit',\n 'ResponseComplete',\n 'SessionEnd',\n 'SessionStart',\n 'SubagentStart',\n 'SubagentStop',\n // Domain events\n 'ApprovalExpired',\n 'ApprovalGranted',\n 'ApprovalRequested',\n 'MemoryDecisionStored',\n 'MemoryLearningStored',\n 'MemoryObserved',\n 'MemoryPatternStored',\n 'PipelineManifestAppended',\n 'PipelineStageCompleted',\n 'SessionEnded',\n 'SessionStarted',\n 'TaskBlocked',\n 'TaskCompleted',\n 'TaskCreated',\n 'TaskStarted',\n] as const;\n\n/** Union type of all canonical hook event names. */\nexport type CanonicalHookEvent = (typeof CANONICAL_HOOK_EVENTS)[number];\n\n/** Provider-sourced events only (original 16 CAAMP events). */\nexport const PROVIDER_HOOK_EVENTS = [\n 'ConfigChange',\n 'Notification',\n 'PermissionRequest',\n 'PostCompact',\n 'PostModel',\n 'PostToolUse',\n 'PostToolUseFailure',\n 'PreCompact',\n 'PreModel',\n 'PreToolUse',\n 'PromptSubmit',\n 'ResponseComplete',\n 'SessionEnd',\n 'SessionStart',\n 'SubagentStart',\n 'SubagentStop',\n] as const;\n\n/** Domain-sourced events only (CLEO business events). */\nexport const DOMAIN_HOOK_EVENTS = [\n 'ApprovalExpired',\n 'ApprovalGranted',\n 'ApprovalRequested',\n 'MemoryDecisionStored',\n 'MemoryLearningStored',\n 'MemoryObserved',\n 'MemoryPatternStored',\n 'PipelineManifestAppended',\n 'PipelineStageCompleted',\n 'SessionEnded',\n 'SessionStarted',\n 'TaskBlocked',\n 'TaskCompleted',\n 'TaskCreated',\n 'TaskStarted',\n] as const;\n\n/** Metadata for each canonical event, derived from hook-mappings.json. */\nexport const EVENT_METADATA: Record<\n CanonicalHookEvent,\n {\n category: HookCategory;\n source: EventSource;\n canBlock: boolean;\n description: string;\n }\n> = {\n ConfigChange: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'Configuration file changes during a session',\n },\n Notification: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'System notification or alert is emitted',\n },\n PermissionRequest: {\n category: 'tool',\n source: 'provider',\n canBlock: true,\n description: 'Permission dialog appears for a tool action',\n },\n PostCompact: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'After context window compaction completes',\n },\n PostModel: {\n category: 'agent',\n source: 'provider',\n canBlock: false,\n description: 'After receiving an LLM response',\n },\n PostToolUse: {\n category: 'tool',\n source: 'provider',\n canBlock: false,\n description: 'After a tool call succeeds',\n },\n PostToolUseFailure: {\n category: 'tool',\n source: 'provider',\n canBlock: false,\n description: 'After a tool call fails or times out',\n },\n PreCompact: {\n category: 'context',\n source: 'provider',\n canBlock: false,\n description: 'Before context window compaction',\n },\n PreModel: {\n category: 'agent',\n source: 'provider',\n canBlock: true,\n description: 'Before sending a request to the LLM',\n },\n PreToolUse: {\n category: 'tool',\n source: 'provider',\n canBlock: true,\n description: 'Before a tool call executes (can block/modify)',\n },\n PromptSubmit: {\n category: 'prompt',\n source: 'provider',\n canBlock: true,\n description: 'User submits a prompt, before agent processes it',\n },\n ResponseComplete: {\n category: 'prompt',\n source: 'provider',\n canBlock: false,\n description: 'Agent finishes responding to a turn',\n },\n SessionEnd: {\n category: 'session',\n source: 'provider',\n canBlock: false,\n description: 'Session terminates or exits',\n },\n SessionStart: {\n category: 'session',\n source: 'provider',\n canBlock: false,\n description: 'Session begins, resumes, or is cleared',\n },\n SubagentStart: {\n category: 'agent',\n source: 'provider',\n canBlock: true,\n description: 'A subagent is spawned',\n },\n SubagentStop: {\n category: 'agent',\n source: 'provider',\n canBlock: false,\n description: 'A subagent finishes execution',\n },\n ApprovalExpired: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A workflow approval token has expired without response',\n },\n ApprovalGranted: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A workflow approval has been granted via /approve token',\n },\n ApprovalRequested: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A workflow approval gate has fired, awaiting human approval',\n },\n MemoryDecisionStored: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'A decision has been stored via memory.decision.store',\n },\n MemoryLearningStored: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'A learning has been stored via memory.learning.store',\n },\n MemoryObserved: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'An observation has been recorded to brain.db via memory.observe',\n },\n MemoryPatternStored: {\n category: 'memory',\n source: 'domain',\n canBlock: false,\n description: 'A pattern has been stored via memory.pattern.store',\n },\n PipelineManifestAppended: {\n category: 'pipeline',\n source: 'domain',\n canBlock: false,\n description:\n 'A manifest entry has been appended to pipeline_manifest (SQLite table per ADR-027)',\n },\n PipelineStageCompleted: {\n category: 'pipeline',\n source: 'domain',\n canBlock: false,\n description: 'A LOOM lifecycle gate has passed validation',\n },\n SessionEnded: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO session has ended via session.end',\n },\n SessionStarted: {\n category: 'session',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO session has been started via session.start',\n },\n TaskBlocked: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been blocked',\n },\n TaskCompleted: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been marked complete via tasks.complete',\n },\n TaskCreated: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been created via tasks.add',\n },\n TaskStarted: {\n category: 'task',\n source: 'domain',\n canBlock: false,\n description: 'A CLEO task has been started via tasks.start',\n },\n} as const;\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -74,7 +74,7 @@ import {
74
74
  getHookSupport,
75
75
  getProviderSummary,
76
76
  translateToAll
77
- } from "./chunk-AOCNTIKB.js";
77
+ } from "./chunk-UZSGHMYW.js";
78
78
  import {
79
79
  buildSkillSubPathCandidates,
80
80
  resolveProviderConfigPath,
@@ -3349,7 +3349,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3349
3349
  return;
3350
3350
  }
3351
3351
  if (opts.from) {
3352
- const { toCanonical } = await import("./hooks-LLJHGX5J.js");
3352
+ const { toCanonical } = await import("./hooks-4MLYUJJL.js");
3353
3353
  const canonical2 = toCanonical(event, opts.from);
3354
3354
  if (format === "json") {
3355
3355
  const envelope = buildEnvelope2(
@@ -3389,7 +3389,7 @@ CAMP Hook Support (mappings v${getHookMappingsVersion()})
3389
3389
  }
3390
3390
  process.exit(1);
3391
3391
  }
3392
- const { getMappedProviderIds } = await import("./hooks-LLJHGX5J.js");
3392
+ const { getMappedProviderIds } = await import("./hooks-4MLYUJJL.js");
3393
3393
  const allIds = getMappedProviderIds();
3394
3394
  const translations = translateToAll(canonical, allIds);
3395
3395
  if (format === "json") {
@@ -24,7 +24,7 @@ import {
24
24
  toNative,
25
25
  toNativeBatch,
26
26
  translateToAll
27
- } from "./chunk-AOCNTIKB.js";
27
+ } from "./chunk-UZSGHMYW.js";
28
28
  import "./chunk-B5WJ4RDT.js";
29
29
  export {
30
30
  CANONICAL_HOOK_EVENTS,
@@ -53,4 +53,4 @@ export {
53
53
  toNativeBatch,
54
54
  translateToAll
55
55
  };
56
- //# sourceMappingURL=hooks-LLJHGX5J.js.map
56
+ //# sourceMappingURL=hooks-4MLYUJJL.js.map
package/dist/index.js CHANGED
@@ -132,7 +132,7 @@ import {
132
132
  toNative,
133
133
  toNativeBatch,
134
134
  translateToAll
135
- } from "./chunk-AOCNTIKB.js";
135
+ } from "./chunk-UZSGHMYW.js";
136
136
  import {
137
137
  _resetPlatformPathsCache,
138
138
  getAgentsConfigPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/caamp",
3
- "version": "2026.4.111",
3
+ "version": "2026.4.113",
4
4
  "description": "Central AI Agent Managed Packages - unified provider registry and package manager for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -51,8 +51,8 @@
51
51
  "jsonc-parser": "^3.3.1",
52
52
  "picocolors": "^1.1.1",
53
53
  "simple-git": "3.33.0",
54
- "@cleocode/cant": "2026.4.111",
55
- "@cleocode/lafs": "2026.4.111"
54
+ "@cleocode/cant": "2026.4.113",
55
+ "@cleocode/lafs": "2026.4.113"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@biomejs/biome": "2.4.8",
@@ -157,7 +157,7 @@
157
157
  "PipelineManifestAppended": {
158
158
  "category": "pipeline",
159
159
  "source": "domain",
160
- "description": "A manifest entry has been appended to MANIFEST.jsonl",
160
+ "description": "A manifest entry has been appended to pipeline_manifest (SQLite table per ADR-027)",
161
161
  "canBlock": false
162
162
  },
163
163
  "SessionStarted": {