@cleocode/caamp 1.8.0 → 1.9.0
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.
- package/dist/chunk-ER3FIOTM.js +244 -0
- package/dist/chunk-ER3FIOTM.js.map +1 -0
- package/dist/{chunk-ZVRDBY6L.js → chunk-MFWBR2NY.js} +110 -370
- package/dist/chunk-MFWBR2NY.js.map +1 -0
- package/dist/{chunk-YWO4I7LI.js → chunk-OLJZ23W3.js} +7 -5
- package/dist/chunk-OLJZ23W3.js.map +1 -0
- package/dist/chunk-TRIXT4T7.js +276 -0
- package/dist/chunk-TRIXT4T7.js.map +1 -0
- package/dist/cli.js +199 -87
- package/dist/cli.js.map +1 -1
- package/dist/hooks-LV6VU7QJ.js +56 -0
- package/dist/index.d.ts +217 -11
- package/dist/index.js +71 -19
- package/dist/index.js.map +1 -1
- package/dist/{injector-XGI7NNZA.js → injector-P2OL6RK3.js} +3 -2
- package/dist/injector-P2OL6RK3.js.map +1 -0
- package/package.json +7 -7
- package/providers/hook-mappings.json +302 -0
- package/providers/registry.json +599 -156
- package/dist/chunk-YWO4I7LI.js.map +0 -1
- package/dist/chunk-ZVRDBY6L.js.map +0 -1
- /package/dist/{injector-XGI7NNZA.js.map → hooks-LV6VU7QJ.js.map} +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveRegistryTemplatePath
|
|
3
|
+
} from "./chunk-TRIXT4T7.js";
|
|
4
|
+
|
|
5
|
+
// src/core/hooks/types.ts
|
|
6
|
+
var HOOK_CATEGORIES = ["session", "prompt", "tool", "agent", "context"];
|
|
7
|
+
var CANONICAL_HOOK_EVENTS = [
|
|
8
|
+
"SessionStart",
|
|
9
|
+
"SessionEnd",
|
|
10
|
+
"PromptSubmit",
|
|
11
|
+
"ResponseComplete",
|
|
12
|
+
"PreToolUse",
|
|
13
|
+
"PostToolUse",
|
|
14
|
+
"PostToolUseFailure",
|
|
15
|
+
"PermissionRequest",
|
|
16
|
+
"SubagentStart",
|
|
17
|
+
"SubagentStop",
|
|
18
|
+
"PreModel",
|
|
19
|
+
"PostModel",
|
|
20
|
+
"PreCompact",
|
|
21
|
+
"PostCompact",
|
|
22
|
+
"Notification",
|
|
23
|
+
"ConfigChange"
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// src/core/hooks/normalizer.ts
|
|
27
|
+
import { readFileSync } from "fs";
|
|
28
|
+
import { dirname, join } from "path";
|
|
29
|
+
import { fileURLToPath } from "url";
|
|
30
|
+
var _mappings = null;
|
|
31
|
+
function findMappingsPath() {
|
|
32
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
return join(thisDir, "..", "..", "..", "providers", "hook-mappings.json");
|
|
34
|
+
}
|
|
35
|
+
function loadMappings() {
|
|
36
|
+
if (_mappings) return _mappings;
|
|
37
|
+
const raw = readFileSync(findMappingsPath(), "utf-8");
|
|
38
|
+
_mappings = JSON.parse(raw);
|
|
39
|
+
return _mappings;
|
|
40
|
+
}
|
|
41
|
+
function resetHookMappings() {
|
|
42
|
+
_mappings = null;
|
|
43
|
+
}
|
|
44
|
+
function getCanonicalEvent(event) {
|
|
45
|
+
const data = loadMappings();
|
|
46
|
+
return data.canonicalEvents[event];
|
|
47
|
+
}
|
|
48
|
+
function getAllCanonicalEvents() {
|
|
49
|
+
return loadMappings().canonicalEvents;
|
|
50
|
+
}
|
|
51
|
+
function getCanonicalEventsByCategory(category) {
|
|
52
|
+
const data = loadMappings();
|
|
53
|
+
return CANONICAL_HOOK_EVENTS.filter(
|
|
54
|
+
(event) => data.canonicalEvents[event].category === category
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function getProviderHookProfile(providerId) {
|
|
58
|
+
const data = loadMappings();
|
|
59
|
+
return data.providerMappings[providerId];
|
|
60
|
+
}
|
|
61
|
+
function getMappedProviderIds() {
|
|
62
|
+
return Object.keys(loadMappings().providerMappings);
|
|
63
|
+
}
|
|
64
|
+
function toNative(canonical, providerId) {
|
|
65
|
+
const profile = getProviderHookProfile(providerId);
|
|
66
|
+
if (!profile) return null;
|
|
67
|
+
const mapping = profile.mappings[canonical];
|
|
68
|
+
return mapping?.supported ? mapping.nativeName : null;
|
|
69
|
+
}
|
|
70
|
+
function toCanonical(nativeName, providerId) {
|
|
71
|
+
const profile = getProviderHookProfile(providerId);
|
|
72
|
+
if (!profile) return null;
|
|
73
|
+
for (const [canonical, mapping] of Object.entries(profile.mappings)) {
|
|
74
|
+
if (mapping.supported && mapping.nativeName === nativeName) {
|
|
75
|
+
return canonical;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
function toNativeBatch(canonicals, providerId) {
|
|
81
|
+
const data = loadMappings();
|
|
82
|
+
const profile = data.providerMappings[providerId];
|
|
83
|
+
if (!profile) return [];
|
|
84
|
+
const results = [];
|
|
85
|
+
for (const canonical of canonicals) {
|
|
86
|
+
const mapping = profile.mappings[canonical];
|
|
87
|
+
if (mapping?.supported && mapping.nativeName) {
|
|
88
|
+
results.push({
|
|
89
|
+
canonical,
|
|
90
|
+
native: mapping.nativeName,
|
|
91
|
+
providerId,
|
|
92
|
+
category: data.canonicalEvents[canonical].category,
|
|
93
|
+
canBlock: data.canonicalEvents[canonical].canBlock
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return results;
|
|
98
|
+
}
|
|
99
|
+
function supportsHook(canonical, providerId) {
|
|
100
|
+
const profile = getProviderHookProfile(providerId);
|
|
101
|
+
if (!profile) return false;
|
|
102
|
+
return profile.mappings[canonical]?.supported ?? false;
|
|
103
|
+
}
|
|
104
|
+
function getHookSupport(canonical, providerId) {
|
|
105
|
+
const profile = getProviderHookProfile(providerId);
|
|
106
|
+
if (!profile) {
|
|
107
|
+
return { canonical, supported: false, native: null };
|
|
108
|
+
}
|
|
109
|
+
const mapping = profile.mappings[canonical];
|
|
110
|
+
return {
|
|
111
|
+
canonical,
|
|
112
|
+
supported: mapping?.supported ?? false,
|
|
113
|
+
native: mapping?.nativeName ?? null,
|
|
114
|
+
notes: mapping?.notes
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function getSupportedEvents(providerId) {
|
|
118
|
+
const profile = getProviderHookProfile(providerId);
|
|
119
|
+
if (!profile) return [];
|
|
120
|
+
return CANONICAL_HOOK_EVENTS.filter(
|
|
121
|
+
(event) => profile.mappings[event]?.supported
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
function getUnsupportedEvents(providerId) {
|
|
125
|
+
const profile = getProviderHookProfile(providerId);
|
|
126
|
+
if (!profile) return [...CANONICAL_HOOK_EVENTS];
|
|
127
|
+
return CANONICAL_HOOK_EVENTS.filter(
|
|
128
|
+
(event) => !profile.mappings[event]?.supported
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
function getProvidersForEvent(canonical) {
|
|
132
|
+
const data = loadMappings();
|
|
133
|
+
return Object.entries(data.providerMappings).filter(([, profile]) => profile.mappings[canonical]?.supported).map(([id]) => id);
|
|
134
|
+
}
|
|
135
|
+
function getCommonEvents(providerIds) {
|
|
136
|
+
if (providerIds.length === 0) return [];
|
|
137
|
+
return CANONICAL_HOOK_EVENTS.filter(
|
|
138
|
+
(event) => providerIds.every((id) => supportsHook(event, id))
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function getProviderSummary(providerId) {
|
|
142
|
+
const profile = getProviderHookProfile(providerId);
|
|
143
|
+
if (!profile) return void 0;
|
|
144
|
+
const supported = getSupportedEvents(providerId);
|
|
145
|
+
const unsupported = getUnsupportedEvents(providerId);
|
|
146
|
+
return {
|
|
147
|
+
providerId,
|
|
148
|
+
hookSystem: profile.hookSystem,
|
|
149
|
+
experimental: profile.experimental,
|
|
150
|
+
supportedCount: supported.length,
|
|
151
|
+
totalCanonical: CANONICAL_HOOK_EVENTS.length,
|
|
152
|
+
supported,
|
|
153
|
+
unsupported,
|
|
154
|
+
providerOnly: profile.providerOnlyEvents,
|
|
155
|
+
coverage: Math.round(supported.length / CANONICAL_HOOK_EVENTS.length * 100)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function buildHookMatrix(providerIds) {
|
|
159
|
+
const data = loadMappings();
|
|
160
|
+
const ids = providerIds ?? Object.keys(data.providerMappings);
|
|
161
|
+
const matrix = {};
|
|
162
|
+
for (const event of CANONICAL_HOOK_EVENTS) {
|
|
163
|
+
matrix[event] = {};
|
|
164
|
+
for (const id of ids) {
|
|
165
|
+
const profile = data.providerMappings[id];
|
|
166
|
+
matrix[event][id] = profile?.mappings[event] ?? {
|
|
167
|
+
nativeName: null,
|
|
168
|
+
supported: false
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
events: [...CANONICAL_HOOK_EVENTS],
|
|
174
|
+
providers: ids,
|
|
175
|
+
matrix
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function getHookSystemType(providerId) {
|
|
179
|
+
const profile = getProviderHookProfile(providerId);
|
|
180
|
+
return profile?.hookSystem ?? "none";
|
|
181
|
+
}
|
|
182
|
+
function getHookConfigPath(providerId) {
|
|
183
|
+
const profile = getProviderHookProfile(providerId);
|
|
184
|
+
if (!profile?.hookConfigPath) return null;
|
|
185
|
+
return resolveRegistryTemplatePath(profile.hookConfigPath);
|
|
186
|
+
}
|
|
187
|
+
function getProviderOnlyEvents(providerId) {
|
|
188
|
+
const profile = getProviderHookProfile(providerId);
|
|
189
|
+
return profile?.providerOnlyEvents ?? [];
|
|
190
|
+
}
|
|
191
|
+
function translateToAll(canonical, providerIds) {
|
|
192
|
+
const result = {};
|
|
193
|
+
for (const id of providerIds) {
|
|
194
|
+
const native = toNative(canonical, id);
|
|
195
|
+
if (native) {
|
|
196
|
+
result[id] = native;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
function resolveNativeEvent(nativeName) {
|
|
202
|
+
const data = loadMappings();
|
|
203
|
+
const results = [];
|
|
204
|
+
for (const [providerId, profile] of Object.entries(data.providerMappings)) {
|
|
205
|
+
for (const [canonical, mapping] of Object.entries(profile.mappings)) {
|
|
206
|
+
if (mapping.supported && mapping.nativeName === nativeName) {
|
|
207
|
+
results.push({ providerId, canonical });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return results;
|
|
212
|
+
}
|
|
213
|
+
function getHookMappingsVersion() {
|
|
214
|
+
return loadMappings().version;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export {
|
|
218
|
+
HOOK_CATEGORIES,
|
|
219
|
+
CANONICAL_HOOK_EVENTS,
|
|
220
|
+
resetHookMappings,
|
|
221
|
+
getCanonicalEvent,
|
|
222
|
+
getAllCanonicalEvents,
|
|
223
|
+
getCanonicalEventsByCategory,
|
|
224
|
+
getProviderHookProfile,
|
|
225
|
+
getMappedProviderIds,
|
|
226
|
+
toNative,
|
|
227
|
+
toCanonical,
|
|
228
|
+
toNativeBatch,
|
|
229
|
+
supportsHook,
|
|
230
|
+
getHookSupport,
|
|
231
|
+
getSupportedEvents,
|
|
232
|
+
getUnsupportedEvents,
|
|
233
|
+
getProvidersForEvent,
|
|
234
|
+
getCommonEvents,
|
|
235
|
+
getProviderSummary,
|
|
236
|
+
buildHookMatrix,
|
|
237
|
+
getHookSystemType,
|
|
238
|
+
getHookConfigPath,
|
|
239
|
+
getProviderOnlyEvents,
|
|
240
|
+
translateToAll,
|
|
241
|
+
resolveNativeEvent,
|
|
242
|
+
getHookMappingsVersion
|
|
243
|
+
};
|
|
244
|
+
//# sourceMappingURL=chunk-ER3FIOTM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/hooks/types.ts","../src/core/hooks/normalizer.ts"],"sourcesContent":["/**\n * CAAMP Hooks Normalizer - Type Definitions\n *\n * Defines the canonical CAAMP hook event taxonomy and provider mapping types.\n * CAAMP provides a unified hook interface across all providers — consumers\n * use canonical event names, and the normalizer translates to/from\n * provider-native names.\n */\n\n// ── Canonical Hook Events ───────────────────────────────────────────\n\nexport const HOOK_CATEGORIES = [\"session\", \"prompt\", \"tool\", \"agent\", \"context\"] as const;\nexport type HookCategory = (typeof HOOK_CATEGORIES)[number];\n\nexport const CANONICAL_HOOK_EVENTS = [\n \"SessionStart\",\n \"SessionEnd\",\n \"PromptSubmit\",\n \"ResponseComplete\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PostToolUseFailure\",\n \"PermissionRequest\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PreModel\",\n \"PostModel\",\n \"PreCompact\",\n \"PostCompact\",\n \"Notification\",\n \"ConfigChange\",\n] as const;\n\nexport type CanonicalHookEvent = (typeof CANONICAL_HOOK_EVENTS)[number];\n\nexport interface CanonicalEventDefinition {\n category: HookCategory;\n description: string;\n canBlock: boolean;\n}\n\n// ── Provider Hook System Types ──────────────────────────────────────\n\nexport type HookSystemType = \"config\" | \"plugin\" | \"none\";\nexport type HookHandlerType = \"command\" | \"http\" | \"prompt\" | \"agent\" | \"plugin\";\n\nexport interface HookMapping {\n nativeName: string | null;\n supported: boolean;\n notes?: string;\n}\n\nexport interface ProviderHookProfile {\n hookSystem: HookSystemType;\n hookConfigPath: string | null;\n hookFormat: string | null;\n handlerTypes: HookHandlerType[];\n experimental: boolean;\n mappings: Record<CanonicalHookEvent, HookMapping>;\n providerOnlyEvents: string[];\n}\n\n// ── Normalization Result Types ──────────────────────────────────────\n\nexport interface NormalizedHookEvent {\n canonical: CanonicalHookEvent;\n native: string;\n providerId: string;\n category: HookCategory;\n canBlock: boolean;\n}\n\nexport interface HookSupportResult {\n canonical: CanonicalHookEvent;\n supported: boolean;\n native: string | null;\n notes?: string;\n}\n\nexport interface ProviderHookSummary {\n providerId: string;\n hookSystem: HookSystemType;\n experimental: boolean;\n supportedCount: number;\n totalCanonical: number;\n supported: CanonicalHookEvent[];\n unsupported: CanonicalHookEvent[];\n providerOnly: string[];\n coverage: number;\n}\n\nexport interface CrossProviderMatrix {\n events: CanonicalHookEvent[];\n providers: string[];\n matrix: Record<CanonicalHookEvent, Record<string, HookMapping>>;\n}\n\n// ── Hook Mappings Data File Types ───────────────────────────────────\n\nexport interface HookMappingsFile {\n version: string;\n lastUpdated: string;\n description: string;\n canonicalEvents: Record<CanonicalHookEvent, CanonicalEventDefinition>;\n providerMappings: Record<string, ProviderHookProfile>;\n}\n","/**\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 { 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, HOOK_CATEGORIES } 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 raw = readFileSync(findMappingsPath(), \"utf-8\");\n _mappings = JSON.parse(raw) as HookMappingsFile;\n return _mappings;\n}\n\n/** Reset cached data (for testing). */\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 */\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 */\nexport function getAllCanonicalEvents(): Record<CanonicalHookEvent, CanonicalEventDefinition> {\n return loadMappings().canonicalEvents;\n}\n\n/**\n * Get canonical events filtered by category.\n */\nexport function getCanonicalEventsByCategory(category: HookCategory): CanonicalHookEvent[] {\n const data = loadMappings();\n return CANONICAL_HOOK_EVENTS.filter(\n (event) => data.canonicalEvents[event].category === category,\n );\n}\n\n/**\n * Get the full hook profile for a provider.\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 */\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 * @returns The native event name, or `null` if unsupported\n *\n * @example\n * ```typescript\n * toNative(\"PreToolUse\", \"claude-code\"); // \"PreToolUse\"\n * toNative(\"PreToolUse\", \"gemini-cli\"); // \"BeforeTool\"\n * toNative(\"PreToolUse\", \"cursor\"); // \"preToolUse\"\n * toNative(\"PreToolUse\", \"kimi\"); // null\n * ```\n */\nexport function toNative(\n canonical: CanonicalHookEvent,\n providerId: string,\n): 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 * @returns The canonical event name, or `null` if no mapping exists\n *\n * @example\n * ```typescript\n * toCanonical(\"BeforeTool\", \"gemini-cli\"); // \"PreToolUse\"\n * toCanonical(\"stop\", \"cursor\"); // \"ResponseComplete\"\n * toCanonical(\"UserPromptSubmit\", \"claude-code\"); // \"PromptSubmit\"\n * ```\n */\nexport function toCanonical(\n nativeName: string,\n providerId: string,\n): 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 * @returns Array of normalized events (only supported ones included)\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 */\nexport function supportsHook(\n canonical: CanonicalHookEvent,\n providerId: string,\n): 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 */\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 */\nexport function getSupportedEvents(providerId: string): CanonicalHookEvent[] {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return [];\n return CANONICAL_HOOK_EVENTS.filter(\n (event) => profile.mappings[event]?.supported,\n );\n}\n\n/**\n * Get all unsupported canonical events for a provider.\n */\nexport function getUnsupportedEvents(providerId: string): CanonicalHookEvent[] {\n const profile = getProviderHookProfile(providerId);\n if (!profile) return [...CANONICAL_HOOK_EVENTS];\n return CANONICAL_HOOK_EVENTS.filter(\n (event) => !profile.mappings[event]?.supported,\n );\n}\n\n/**\n * Get providers that support a specific canonical event.\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 */\nexport function getCommonEvents(providerIds: string[]): CanonicalHookEvent[] {\n if (providerIds.length === 0) return [];\n return CANONICAL_HOOK_EVENTS.filter(\n (event) => providerIds.every((id) => supportsHook(event, id)),\n );\n}\n\n// ── Summary & Matrix Functions ──────────────────────────────────────\n\n/**\n * Get a summary of hook support for a provider.\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: CANONICAL_HOOK_EVENTS.length,\n supported,\n unsupported,\n providerOnly: profile.providerOnlyEvents,\n coverage: Math.round((supported.length / CANONICAL_HOOK_EVENTS.length) * 100),\n };\n}\n\n/**\n * Build a cross-provider hook support matrix.\n *\n * Shows which canonical events are supported by which providers,\n * with native name translations.\n */\nexport function buildHookMatrix(providerIds?: string[]): CrossProviderMatrix {\n const data = loadMappings();\n const ids = providerIds ?? Object.keys(data.providerMappings);\n\n const matrix: Record<string, Record<string, HookMapping>> = {};\n for (const event of CANONICAL_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: [...CANONICAL_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 */\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 */\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 */\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 * Returns only providers that support the event.\n *\n * @example\n * ```typescript\n * const result = translateToAll(\"PreToolUse\", [\"claude-code\", \"gemini-cli\", \"kimi\"]);\n * // { \"claude-code\": \"PreToolUse\", \"gemini-cli\": \"BeforeTool\" }\n * // (kimi excluded — unsupported)\n * ```\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 * Useful when you have a native name but don't know which provider it's from.\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 */\nexport function getHookMappingsVersion(): string {\n return loadMappings().version;\n}\n"],"mappings":"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","names":[]}
|