sdk-hooks-development
Use this skill when implementing TypeScript hook callbacks for the Claude Agent SDK — creating PreToolUse hooks to allow/deny tool calls, PostToolUse hooks to inject additionalContext, building factory functions for parameterized hooks, using HookCallback and HookJSONOutput types, applying isPreToolUseInput and isPostToolUseInput type guards, or designing a hooks strategy for an Agent SDK platform. Hooks in the TypeScript SDK are async functions, NOT JSON config files.
What this skill does
# TypeScript SDK Hook Development
**Critical distinction**: Agent SDK hooks (TypeScript `HookCallback` functions) are different from Claude Code plugin hooks (JSON config). This skill covers the TypeScript SDK programmatic API.
## Core Types
```typescript
// From @anthropic-ai/claude-agent-sdk (via your types.ts re-export)
type HookCallback = (
input: HookInput,
toolUseId: string,
context: { signal: AbortSignal }
) => Promise<HookJSONOutput>;
type HookJSONOutput = {
hookSpecificOutput?: {
hookEventName: string;
// PreToolUse:
permissionDecision?: "allow" | "deny";
permissionDecisionReason?: string;
// PostToolUse:
additionalContext?: string;
};
};
```
## Pattern: Basic Hook Structure
Every hook follows this exact pattern:
```typescript
import type { HookCallback, HookJSONOutput } from "../types";
import { isPreToolUseInput, getToolInputFilePath } from "../types";
export const myHook: HookCallback = async (input, _toolUseId, { signal }): Promise<HookJSONOutput> => {
// 1. Always check abort first
if (signal.aborted) return {};
// 2. Guard: only handle the right event type
if (!isPreToolUseInput(input)) return {};
// 3. Extract data
const filePath = getToolInputFilePath(input);
if (!filePath) return {};
// 4. Apply logic
if (filePath.endsWith(".env")) {
return {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Reading .env files is not allowed — they may contain secrets",
},
};
}
return {}; // empty = allow
};
```
## Pattern: Factory Function (Parameterized Hooks)
Use factory functions when a hook needs runtime parameters:
```typescript
import path from "node:path";
import type { HookCallback, HookJSONOutput } from "../types";
import { getToolInputFilePath, isPreToolUseInput } from "../types";
export function createFileRestrictionHook(allowedFilePath: string): HookCallback {
const normalized = path.resolve(allowedFilePath);
return async (input, _toolUseId, { signal }): Promise<HookJSONOutput> => {
if (signal.aborted) return {};
if (!isPreToolUseInput(input)) return {};
const filePath = getToolInputFilePath(input);
if (!filePath) return {};
if (path.resolve(filePath) === normalized) return {};
return {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: `Only ${allowedFilePath} can be modified`,
},
};
};
}
```
## Pattern: PostToolUse — additionalContext
Inject feedback into tool results to guide the agent:
```typescript
import type { HookCallback, HookJSONOutput } from "../types";
import { getToolInputCommand, isPostToolUseInput } from "../types";
export const testReminderHook: HookCallback = async (input, _toolUseId, { signal }): Promise<HookJSONOutput> => {
if (signal.aborted) return {};
if (!isPostToolUseInput(input)) return {};
const command = getToolInputCommand(input);
if (!isTestCommand(command)) return {};
return {
hookSpecificOutput: {
hookEventName: input.hook_event_name,
additionalContext: "REMINDER: If tests pass, stop. If 10+ pass with failures, prune.",
},
};
};
```
## Pattern: PostToolUse — Auto-Fix with Feedback
Run linters/typecheck after edits and inject remaining errors:
```typescript
import { execSync } from "node:child_process";
import type { HookCallback, HookJSONOutput } from "../types";
import { getExecOutput, getToolInputFilePath, isPostToolUseInput } from "../types";
export function createLintFixHook(workingDirectory: string, targetFile: string): HookCallback | null {
const eslintBin = path.join(workingDirectory, "node_modules", ".bin", "eslint");
if (!existsSync(eslintBin)) return null; // graceful disable
return async (input, _toolUseId, { signal }): Promise<HookJSONOutput> => {
if (signal.aborted) return {};
if (!isPostToolUseInput(input)) return {};
const filePath = getToolInputFilePath(input);
if (path.resolve(filePath) !== path.resolve(targetFile)) return {};
try {
execSync(`${eslintBin} --fix "${targetFile}" 2>&1`, {
cwd: workingDirectory,
encoding: "utf8",
timeout: 30_000,
});
return {};
} catch (error: unknown) {
const output = getExecOutput(error);
if (!output) return {};
return {
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: `LINT ERRORS after auto-fix:\n${output.slice(0, 2000)}`,
},
};
}
};
}
```
## Hook Registration
Register hooks in the `query()` call options:
```typescript
const fileRestrictionHook = createFileRestrictionHook(params.testFilePath);
const lintFixHook = createLintFixHook(params.workingDirectory, params.testFilePath);
await query({
prompt,
options: {
// ...
hooks: {
PreToolUse: [
{ matcher: "Write|Edit", hooks: [fileRestrictionHook] },
{ matcher: "Read", hooks: [envProtectionHook] },
],
PostToolUse: [
{ matcher: "Bash", hooks: [testPruneHook] },
// Conditionally include lintFixHook if binary exists
...(lintFixHook ? [{ matcher: "Write|Edit", hooks: [lintFixHook] }] : []),
],
},
},
});
```
## Utility Functions
Keep these in your `types.ts` — they centralize unsafe casts:
```typescript
// Safe extraction of file_path from PreToolUse or PostToolUse input
export function getToolInputFilePath(input: PreToolUseHookInput | PostToolUseHookInput): string {
const toolInput = input.tool_input as Record<string, unknown> | undefined;
const filePath = toolInput?.file_path;
return typeof filePath === "string" ? filePath : "";
}
// Safe extraction of command from PostToolUse Bash input
export function getToolInputCommand(input: PostToolUseHookInput): string {
const toolInput = input.tool_input as Record<string, unknown> | undefined;
const command = toolInput?.command;
return typeof command === "string" ? command : "";
}
// Safe extraction of execSync error output
export function getExecOutput(error: unknown): string {
const execError = error as { stdout?: string; stderr?: string };
return ((execError.stdout ?? "") + (execError.stderr ?? "")).trim();
}
```
## Type Guards
```typescript
export function isPreToolUseInput(input: HookInput): input is PreToolUseHookInput {
return input.hook_event_name === "PreToolUse";
}
export function isPostToolUseInput(input: HookInput): input is PostToolUseHookInput {
return input.hook_event_name === "PostToolUse";
}
```
## Pattern: Modify Tool Input (updatedInput)
Redirect or sanitize tool inputs before execution. Requires `permissionDecision: "allow"`. Never mutate `tool_input` — always return a new object:
```typescript
return {
hookSpecificOutput: {
hookEventName: input.hook_event_name, // always use input.hook_event_name, not hardcoded string
permissionDecision: "allow", // required when using updatedInput
updatedInput: {
...(input.tool_input as Record<string, unknown>),
file_path: `/sandbox${filePath}`, // redirect writes to sandbox
},
},
};
```
## Pattern: Stop the Agent
Return `continue: false` to halt the agent entirely (different from denying a single tool):
```typescript
return {
continue: false,
stopReason: "Budget exhausted — stopping before incurring more cost.",
};
```
Top-level output fields (outside `hookSpecificOutput`):
- `continue: boolean` — whether the agent continues (default `true`)
- `stopReason: string` — message shown when `continue` is `false`
- `suppressOutput: boolean` — hide hook stdout from transcript
- `systemMessage: string` — inject a message directly into Claude's conversation
## Pattern: PostToolUseFailure
Handle tool execution failures. TypeScript-only event. Use top-level `systemMessage` — `hookSpecificOutput` is **not** supported for this event type:
```tyRelated in Backend & APIs
jfrog
IncludedInteract with the JFrog Platform via the JFrog CLI and REST/GraphQL APIs. Use this skill when the user wants to manage Artifactory repositories, upload or download artifacts, manage builds, configure permissions, manage users and groups, work with access tokens, configure JFrog CLI servers, search artifacts, manage properties, set up replication, manage JFrog Projects, run security audits or scans, look up CVE details, query exposures scan results from JFrog Advanced Security, manage release bundles and lifecycle operations, aggregate or export platform data, or perform any JFrog Platform administration task. Also use when the user mentions jf, jfrog, artifactory, xray, distribution, evidence, apptrust, onemodel, graphql, workers, mission control, curation, advanced security, exposures, or any JFrog product name.
cupynumeric-migration-readiness
IncludedPre-migration readiness assessor for porting NumPy to cuPyNumeric. Use BEFORE substantial porting work begins when the user asks whether code will scale on GPU, whether they should migrate to cuPyNumeric, which NumPy patterns transfer cleanly, what must be refactored before porting, or mentions pre-port assessment, scaling analysis, or refactor planning. Inspect the user's source code, look up NumPy usage, cross-reference the cuPyNumeric API support manifest, and distinguish distributed-scaling-friendly patterns from blockers such as unsupported APIs, scalar synchronization, host round-trips, Python/object-heavy control flow, shape/data-dependent branching, and in-place mutation hazards. Produce a verdict of READY, LIGHT REFACTOR, SIGNIFICANT REFACTOR, or NOT RECOMMENDED, with concrete refactor pointers.
alibabacloud-data-agent-skill
IncludedInvoke Alibaba Cloud Apsara Data Agent for Analytics via CLI to perform natural language-driven data analysis on enterprise databases. Data Agent for Analytics is an intelligent data analysis agent developed by Alibaba Cloud Database team for enterprise users. It automatically completes requirement analysis, data understanding, analysis insights, and report generation based on natural language descriptions. This tool supports: discovering data resources (instances/databases/tables) managed in DMS, initiating query or deep analysis sessions, real-time progress tracking, and retrieving analysis conclusions and generated reports. Use this Skill when users need to query databases, analyze data trends, generate data reports, ask questions in natural language, or mention "Data Agent", "data analysis", "database query", "SQL analysis", "data insights".
token-optimizer
IncludedReduce OpenClaw token usage and API costs through smart model routing, heartbeat optimization, budget tracking, and native 2026.2.15 features (session pruning, bootstrap size limits, cache TTL alignment). Use when token costs are high, API rate limits are being hit, or hosting multiple agents at scale. The 4 executable scripts (context_optimizer, model_router, heartbeat_optimizer, token_tracker) are local-only — no network requests, no subprocess calls, no system modifications. Reference files (PROVIDERS.md, config-patches.json) document optional multi-provider strategies that require external API keys and network access if you choose to use them. See SECURITY.md for full breakdown.
resend-cli
IncludedUse this skill when the task is specifically about operating Resend from an AI agent, terminal session, or CI job via the official resend CLI: installing/authenticating the CLI, sending/listing/updating/cancelling emails, batch sends, domains and DNS, webhooks and local listeners, inbound receiving, contacts, topics, segments, broadcasts, templates, API keys, profiles, or debugging Resend CLI/API failures. Trigger on mentions of Resend CLI, `resend`, `resend doctor`, `resend emails send`, `resend domains`, `resend webhooks listen`, `resend emails receiving`, or agent-friendly terminal automation.
alibabacloud-odps-maxframe-coding
IncludedUse this skill for MaxFrame SDK development and documentation navigation on Alibaba Cloud MaxCompute (ODPS). Helps answer MaxFrame API, concept, official example, and supported pandas API questions; create data processing programs; read/write MaxCompute tables; debug jobs (remote or local); and build custom DPE runtime images. Trigger when users mention MaxFrame, MaxCompute with MaxFrame, ODPS table processing, DPE runtime, MaxFrame docs/examples, DataFrame/Tensor operations, or GPU runtime setup. Works for both English and Chinese queries about Alibaba Cloud data processing with MaxFrame.