@yankeeinlondon/claudine
Version:
A simple library to help with Claude Code
198 lines (183 loc) • 6.24 kB
text/typescript
import type { EmptyObject, IsUnion, Suggest } from "inferred-types";
/**
* The hook's which Claude Code exposes.
*/
export type HookEventName
= | "PreToolUse"
| "PostToolUse"
| "UserPromptSubmit"
| "Stop"
| "SubagentStope"
| "Notification"
| "PreCompact"
| "SessionStart";
export type Tool
= | "Write"
| "Edit"
| "Read"
| "Glob"
| "Grep"
| "MultiEdit"
| "WebFetch"
| "WebSearch"
| "Task"
| `Bash(ls:*)`
| `Bash(tsc:*)`
| `Bash(rm:*)`
| `Bash(git stash:*)`
| `Bash(pnpm:*)`
| `Bash(sed:*)`;
export type SuggestMatcher = Suggest<
| Tool
| `Write|Edit`
| `WebFetch|WebSearch`
>;
export interface HookCommand {
/** optionally you may filter the Agent commands which will trigger this hook */
matcher?: SuggestMatcher;
command: Suggest<
| "$CLAUDE_PROJECT_DIR/.claude/hooks/check-style.sh"
>;
}
export type Success = 0;
export type Error = 1;
export type BlockingError = 2;
/**
* **HookHandlerConfig**
*
* The structure of what you'd put in your `.settings.json` or `.settings.local.json` file
* to add handling for a specific event.
*
* **Note:** each "handler" is placed inside of the `hooks: { [event]: HookHandlerConfig[] }` part of the configuration file.
*/
export interface HookHandlerConfig {
/**
* You may filter the handler to only execute when one of the
* specified `AgentCommand`'s is the source. You may also use the `|`
* operator inside your string configuration to indicate a _union_ of
* AgentCommand's you will accept.
*/
matcher?: SuggestMatcher;
/** the things you want to execute when */
hooks: HookCommand[];
}
export type Hook<T extends HookEventName = HookEventName> = {
session_id: string;
transcript_path: string;
cwd: string;
hook_event_name: HookEventName;
} & IsUnion<T> extends true
? EmptyObject
: T extends "PreToolUse"
? {
hook_event_name: "PreToolUse";
tool_name: Suggest<Tool>;
tool_input: {
file_path: string;
content: string;
};
}
: T extends "PostToolUse"
? {
hook_event_name: "PostToolUse";
tool_input: {
file_path: string;
content: string;
};
tool_response: {
filePath: string;
success: boolean;
};
}
: T extends "Notification"
? {
hook_event_name: "Notification";
message: string;
}
: T extends "UserPromptSubmit"
? {
hook_event_name: "UserPromptSubmit";
prompt: string;
}
: T extends "Stop"
? {
hook_event_name: "Stop";
stop_hook_active: boolean;
}
: T extends "SubagentStop"
? {
hook_event_name: "SubagentStop";
stop_hook_active: boolean;
}
: T extends "SessionStart"
? {
hook_event_name: "SessionStart";
source: Suggest<"startup">;
}
: EmptyObject;
export type DecisionStrategy = "allow" | "deny" | "ask";
/**
* The RAW response of a hook response is either a Unix exit code:
*
* - `0` - Success
* - `1` - Error
* - `2` - Blocking Error
*
* Or a JSON stringified string of `HookResponse` sent to STDOUT.
*/
export type HookResponseRaw = string | Success | Error | BlockingError;
export type HookResponse<T extends HookEventName> = (
{
continue?: boolean;
stopReason?: string;
suppressOutput?: boolean;
} & T extends "PreToolUse"
? {
hookSpecificOutput?: {
hookEventName: "PreToolUse";
permissionDecision: DecisionStrategy;
permissionDecisionReason: string;
};
/** @deprecated */
decision?: DecisionStrategy;
/** @deprecated */
reason?: string;
}
: T extends "PostToolUse"
? {
decision?: "block" | undefined;
reason: string;
}
: T extends "UserPromptSubmit"
? {
decision?: "block" | undefined;
reason: string;
hookSpecificOutput?: {
hookEventName: "UserPromptSubmit";
additionalContext: string;
};
}
: T extends "Stop" | "SubagentStop"
? {
decision?: "block" | undefined;
/**
* Reason for stopping.
*
* **Note:* must be provided when Claude is blocked from stopping
*/
reason?: string;
}
: T extends "SessionStart"
? {
hookSpecificOutput?: {
hookEventName: "SessionStart";
additionalContext: string;
};
}
: EmptyObject
)
| Success | Error | BlockingError;
/**
* A callback function which handle Claude Code hook events.
*/
export type HookHandler<T extends HookEventName> = <E extends Hook<T>>(event: E) => Promise<HookResponse<T>>;