SDK for building AI agents with Claude Code's capabilities to programmatically interact with Claude and build autonomous agents that can understand codebases, edit files, and execute workflows.
Intercept and modify agent behavior at key execution points.
type HookEvent =
| 'PreToolUse' // Before tool execution
| 'PostToolUse' // After successful execution
| 'PostToolUseFailure' // After execution failure
| 'PermissionRequest' // During permission request
| 'UserPromptSubmit' // User submits prompt
| 'SessionStart' // Session starts
| 'SessionEnd' // Session ends
| 'Stop' // Execution stops
| 'SubagentStart' // Subagent starts
| 'SubagentStop' // Subagent stops
| 'PreCompact' // Before compaction
| 'Notification'; // System notificationtype HookCallback = (
input: HookInput,
toolUseID: string | undefined,
options: {signal: AbortSignal}
) => Promise<HookJSONOutput>;
interface HookCallbackMatcher {
matcher?: string;
hooks: HookCallback[];
timeout?: number;
}interface PreToolUseHookInput {
hook_event_name: 'PreToolUse';
tool_name: string;
tool_input: unknown;
tool_use_id: string;
session_id: string;
transcript_path: string;
cwd: string;
permission_mode?: string;
}
interface PostToolUseHookInput {
hook_event_name: 'PostToolUse';
tool_name: string;
tool_input: unknown;
tool_response: unknown;
tool_use_id: string;
// ... base fields
}
interface PostToolUseFailureHookInput {
hook_event_name: 'PostToolUseFailure';
tool_name: string;
tool_input: unknown;
tool_use_id: string;
error: string;
is_interrupt?: boolean;
// ... base fields
}
// See types.md for complete listinterface SyncHookJSONOutput {
continue?: boolean;
suppressOutput?: boolean;
stopReason?: string;
decision?: 'approve' | 'block';
systemMessage?: string;
reason?: string;
hookSpecificOutput?:
| {hookEventName: 'PreToolUse'; permissionDecision?: 'allow' | 'deny' | 'ask'; permissionDecisionReason?: string; updatedInput?: Record<string, unknown>}
| {hookEventName: 'UserPromptSubmit'; additionalContext?: string}
| {hookEventName: 'SessionStart'; additionalContext?: string}
| {hookEventName: 'PostToolUse'; additionalContext?: string; updatedMCPToolOutput?: unknown}
| {hookEventName: 'PostToolUseFailure'; additionalContext?: string}
| {hookEventName: 'PermissionRequest'; decision: {behavior: 'allow'; updatedInput?: Record<string, unknown>} | {behavior: 'deny'; message?: string; interrupt?: boolean}};
}
interface AsyncHookJSONOutput {
async: true;
asyncTimeout?: number;
}
type HookJSONOutput = SyncHookJSONOutput | AsyncHookJSONOutput;hooks: {
PreToolUse: [{
hooks: [async (input) => {
if (input.hook_event_name === 'PreToolUse') {
console.log(`[${input.tool_name}]`, input.tool_input);
}
return {continue: true};
}]
}],
PostToolUse: [{
hooks: [async (input) => {
if (input.hook_event_name === 'PostToolUse') {
console.log(`[${input.tool_name} ✓]`);
}
return {continue: true};
}]
}]
}hooks: {
PreToolUse: [{
hooks: [async (input) => {
if (input.hook_event_name === 'PreToolUse') {
if (input.tool_name === 'Bash' &&
(input.tool_input as any).command?.includes('rm -rf')) {
return {
continue: false,
stopReason: 'Dangerous command blocked',
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'deny',
permissionDecisionReason: 'rm -rf not allowed'
}
};
}
}
return {continue: true};
}]
}]
}hooks: {
SessionStart: [{
hooks: [async (input) => {
if (input.hook_event_name === 'SessionStart') {
return {
continue: true,
hookSpecificOutput: {
hookEventName: 'SessionStart',
additionalContext: 'Project uses TypeScript. Follow TS best practices.'
}
};
}
return {continue: true};
}]
}]
}const result = query({
prompt: 'Build project',
options: {
hooks: {
PreToolUse: [{
hooks: [async (input) => {
if (input.hook_event_name === 'PreToolUse') {
console.log(`[PRE] ${input.tool_name}:`, input.tool_input);
}
return {continue: true};
}],
timeout: 5
}],
PostToolUse: [{
hooks: [async (input) => {
if (input.hook_event_name === 'PostToolUse') {
console.log(`[POST] ${input.tool_name} completed`);
}
return {continue: true};
}]
}],
PostToolUseFailure: [{
hooks: [async (input) => {
if (input.hook_event_name === 'PostToolUseFailure') {
console.error(`[FAIL] ${input.tool_name}:`, input.error);
}
return {continue: true};
}]
}],
SessionStart: [{
hooks: [async (input) => {
if (input.hook_event_name === 'SessionStart') {
console.log('Session started:', input.source);
return {
continue: true,
hookSpecificOutput: {
hookEventName: 'SessionStart',
additionalContext: 'Use TypeScript best practices'
}
};
}
return {continue: true};
}]
}]
}
}
});
for await (const msg of result) {
if (msg.type === 'system' && msg.subtype === 'hook_response') {
console.log('Hook:', msg.hook_name, msg.hook_event);
if (msg.exit_code !== 0) {
console.error('Hook failed:', msg.stderr);
}
}
}const HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "Notification", "UserPromptSubmit", "SessionStart", "SessionEnd", "Stop", "SubagentStart", "SubagentStop", "PreCompact", "PermissionRequest"];Install with Tessl CLI
npx tessl i tessl/npm-anthropic-ai--claude-agent-sdk