When behavior varies (output format, hook origin, execution state), avoid scattering conditionals and duplicated helpers/formatters. Instead:
Example (formatter with a discriminated union and one escaping path):
type ToolHookCtx = {
kind: "tool";
source: string;
toolName: string;
toolCallId: string;
body: string;
};
type RunHookCtx = {
kind: "runStart";
body: string;
};
type HookCtx = ToolHookCtx | RunHookCtx;
function formatHookContext(ctx: HookCtx): string {
const body = ctx.body
.trim()
.replace(/<(\/? )hook_context/gi, "<\\$1hook_context");
if (ctx.kind === "tool") {
return `<hook_context source="${ctx.source}" tool_name="${ctx.toolName}" tool_call_id="${ctx.toolCallId}">\n${body}\n</hook_context>`;
}
return `<hook_context source="RunStart">\n${body}\n</hook_context>`;
}
Apply this to keep code easier to read, test, and extend: the “what varies” becomes explicit in types/roles, while the “how it’s formatted/processed” stays centralized.