cc-hooks-ts 2.0.55 → 2.0.65
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/README.md +128 -91
- package/dist/index.d.mts +32 -3
- package/dist/index.mjs +7 -0
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,37 +1,67 @@
|
|
|
1
1
|
# cc-hooks-ts
|
|
2
2
|
|
|
3
|
-
Define Claude Code hooks with full type safety using TypeScript
|
|
3
|
+
Define Claude Code hooks with full type safety using TypeScript.
|
|
4
|
+
|
|
5
|
+
See [examples](./examples) for more usage examples.
|
|
6
|
+
|
|
7
|
+
<!-- TOC -->
|
|
8
|
+
|
|
9
|
+
- [cc-hooks-ts](#cc-hooks-ts)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Basic Usage](#basic-usage)
|
|
12
|
+
- [Define a Hook](#define-a-hook)
|
|
13
|
+
- [Configure Claude Code](#configure-claude-code)
|
|
14
|
+
- [Tool Specific Hooks](#tool-specific-hooks)
|
|
15
|
+
- [Custom Tool Types Support](#custom-tool-types-support)
|
|
16
|
+
- [Advanced Usage](#advanced-usage)
|
|
17
|
+
- [Conditional Hook Execution](#conditional-hook-execution)
|
|
18
|
+
- [Advanced JSON Output](#advanced-json-output)
|
|
19
|
+
- [Documentation](#documentation)
|
|
20
|
+
- [Development](#development)
|
|
21
|
+
- [License](#license)
|
|
22
|
+
- [Contributing](#contributing)
|
|
23
|
+
|
|
24
|
+
<!-- /TOC -->
|
|
4
25
|
|
|
5
26
|
> [!NOTE]
|
|
6
|
-
>
|
|
7
|
-
>
|
|
27
|
+
> Starting with versions 2.0.42, we will raise our version number to match Claude Code whenever Hook-related changes occur.
|
|
28
|
+
>
|
|
29
|
+
> This ensures we can adopt newer type definitions while maintaining compatibility.
|
|
8
30
|
|
|
9
31
|
## Installation
|
|
10
32
|
|
|
11
33
|
```bash
|
|
12
|
-
|
|
34
|
+
# npm
|
|
35
|
+
npm i cc-hooks-ts
|
|
36
|
+
|
|
37
|
+
# yarn
|
|
38
|
+
yarn add cc-hooks-ts
|
|
39
|
+
|
|
40
|
+
# pnpm
|
|
41
|
+
pnpm add cc-hooks-ts
|
|
42
|
+
|
|
43
|
+
# Bun
|
|
44
|
+
bun add cc-hooks-ts
|
|
45
|
+
|
|
46
|
+
# Deno
|
|
47
|
+
deno add npm:cc-hooks-ts
|
|
13
48
|
```
|
|
14
49
|
|
|
15
50
|
## Basic Usage
|
|
16
51
|
|
|
17
52
|
### Define a Hook
|
|
18
53
|
|
|
19
|
-
Example of running a simple SessionStart hook on Bun:
|
|
20
|
-
|
|
21
54
|
```typescript
|
|
22
55
|
import { defineHook } from "cc-hooks-ts";
|
|
23
56
|
|
|
24
|
-
const
|
|
57
|
+
const hook = defineHook({
|
|
58
|
+
// Specify the event(s) that trigger this hook.
|
|
25
59
|
trigger: {
|
|
26
|
-
|
|
27
|
-
SessionStart: true,
|
|
28
|
-
PreToolUse: {
|
|
29
|
-
// PreToolUser and PostToolUse can be tool-specific. (affects type of context)
|
|
30
|
-
Read: true
|
|
31
|
-
}
|
|
60
|
+
SessionStart: true
|
|
32
61
|
},
|
|
62
|
+
// Implement what you want to do.
|
|
33
63
|
run: (context) => {
|
|
34
|
-
//
|
|
64
|
+
// Do something great here
|
|
35
65
|
return context.success({
|
|
36
66
|
messageForUser: "Welcome to your coding session!"
|
|
37
67
|
});
|
|
@@ -41,11 +71,11 @@ const sessionHook = defineHook({
|
|
|
41
71
|
// import.meta.main is available in Node.js 24.2+ and Bun and Deno
|
|
42
72
|
if (import.meta.main) {
|
|
43
73
|
const { runHook } = await import("cc-hooks-ts");
|
|
44
|
-
await runHook(
|
|
74
|
+
await runHook(hook);
|
|
45
75
|
}
|
|
46
76
|
```
|
|
47
77
|
|
|
48
|
-
###
|
|
78
|
+
### Configure Claude Code
|
|
49
79
|
|
|
50
80
|
Then, load defined hooks in your Claude Code settings at `~/.claude/settings.json`.
|
|
51
81
|
|
|
@@ -57,7 +87,7 @@ Then, load defined hooks in your Claude Code settings at `~/.claude/settings.jso
|
|
|
57
87
|
"hooks": [
|
|
58
88
|
{
|
|
59
89
|
"type": "command",
|
|
60
|
-
"command": "bun run --silent path/to/your/sessionHook.ts"
|
|
90
|
+
"command": "bun run -i --silent path/to/your/sessionHook.ts"
|
|
61
91
|
}
|
|
62
92
|
]
|
|
63
93
|
}
|
|
@@ -66,111 +96,122 @@ Then, load defined hooks in your Claude Code settings at `~/.claude/settings.jso
|
|
|
66
96
|
}
|
|
67
97
|
```
|
|
68
98
|
|
|
69
|
-
##
|
|
99
|
+
## Tool Specific Hooks
|
|
70
100
|
|
|
71
|
-
|
|
101
|
+
In `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` events, you can define hooks specific to tools by specifying tool names in the trigger configuration.
|
|
72
102
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Extend the `ToolSchema` interface to add custom tool definitions:
|
|
103
|
+
For example, you can create a hook that only runs before the `Read` tool is used:
|
|
76
104
|
|
|
77
105
|
```typescript
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
};
|
|
106
|
+
const preReadHook = defineHook({
|
|
107
|
+
trigger: { PreToolUse: { Read: true } },
|
|
108
|
+
run: (context) => {
|
|
109
|
+
// context.input.tool_input is typed as { file_path: string; limit?: number; offset?: number; }
|
|
110
|
+
const { file_path } = context.input.tool_input;
|
|
111
|
+
|
|
112
|
+
if (file_path.includes('.env')) {
|
|
113
|
+
return context.blockingError('Cannot read environment files');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return context.success();
|
|
90
117
|
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (import.meta.main) {
|
|
121
|
+
const { runHook } = await import("cc-hooks-ts");
|
|
122
|
+
await runHook(preReadHook);
|
|
91
123
|
}
|
|
92
124
|
```
|
|
93
125
|
|
|
94
|
-
|
|
126
|
+
Then configure it in Claude Code settings:
|
|
95
127
|
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"hooks": {
|
|
131
|
+
"PreToolUse": [
|
|
132
|
+
{
|
|
133
|
+
"matcher": "Read",
|
|
134
|
+
"hooks": [
|
|
135
|
+
{
|
|
136
|
+
"type": "command",
|
|
137
|
+
"command": "bun run -i --silent path/to/your/preReadHook.ts"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
]
|
|
103
142
|
}
|
|
104
|
-
}
|
|
143
|
+
}
|
|
105
144
|
```
|
|
106
145
|
|
|
107
|
-
|
|
146
|
+
### Custom Tool Types Support
|
|
108
147
|
|
|
109
|
-
|
|
148
|
+
You can add support for custom tools by extending the tool type definitions.
|
|
110
149
|
|
|
111
|
-
|
|
150
|
+
This is useful when you want to your MCP-defined tools to have type-safe hook inputs.
|
|
112
151
|
|
|
113
152
|
```typescript
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
153
|
+
import { defineHook } from "cc-hooks-ts";
|
|
154
|
+
|
|
155
|
+
// Example: type-safe hooks for DeepWiki MCP Server tools
|
|
156
|
+
declare module "cc-hooks-ts" {
|
|
157
|
+
interface ToolSchema {
|
|
158
|
+
mcp__deepwiki__ask_question: {
|
|
159
|
+
input: {
|
|
160
|
+
question: string;
|
|
161
|
+
repoName: string;
|
|
162
|
+
};
|
|
163
|
+
response: unknown;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const deepWikiHook = defineHook({
|
|
169
|
+
trigger: { PreToolUse: { mcp__deepwiki__ask_question: true } },
|
|
117
170
|
run: (context) => {
|
|
118
|
-
// context.input.tool_input is typed as {
|
|
119
|
-
const {
|
|
171
|
+
// context.input.tool_input is typed as { question: string; repoName: string; }
|
|
172
|
+
const { question, repoName } = context.input.tool_input;
|
|
120
173
|
|
|
121
|
-
if (
|
|
122
|
-
return context.blockingError('
|
|
174
|
+
if (question.length > 500) {
|
|
175
|
+
return context.blockingError('Question is too long');
|
|
123
176
|
}
|
|
124
177
|
|
|
125
178
|
return context.success();
|
|
126
179
|
}
|
|
127
180
|
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Advanced Usage
|
|
184
|
+
|
|
185
|
+
### Conditional Hook Execution
|
|
186
|
+
|
|
187
|
+
You can conditionally execute hooks based on runtime logic using the `shouldRun` function.
|
|
188
|
+
If `shouldRun` returns `false`, the hook will be skipped.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { defineHook } from "cc-hooks-ts";
|
|
128
192
|
|
|
129
|
-
|
|
130
|
-
const multiEventHook = defineHook({
|
|
193
|
+
const hook = defineHook({
|
|
131
194
|
trigger: {
|
|
132
|
-
|
|
133
|
-
PostToolUse: { Read: true }
|
|
195
|
+
Notification: true
|
|
134
196
|
},
|
|
135
|
-
//
|
|
136
|
-
shouldRun: () => process.
|
|
197
|
+
// Only run this hook on macOS
|
|
198
|
+
shouldRun: () => process.platform === "darwin",
|
|
137
199
|
run: (context) => {
|
|
138
|
-
//
|
|
139
|
-
return context.success()
|
|
200
|
+
// Some macOS-specific logic like sending a notification using AppleScript
|
|
201
|
+
return context.success()
|
|
140
202
|
}
|
|
141
203
|
});
|
|
142
204
|
```
|
|
143
205
|
|
|
144
|
-
###
|
|
145
|
-
|
|
146
|
-
Executes hooks with complete lifecycle management:
|
|
206
|
+
### Advanced JSON Output
|
|
147
207
|
|
|
148
|
-
|
|
149
|
-
- Validates input using Valibot schemas
|
|
150
|
-
- Creates typed context
|
|
151
|
-
- Executes hook handler
|
|
152
|
-
- Formats and outputs results
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
await runHook(hook);
|
|
156
|
-
```
|
|
208
|
+
Use `context.json()` to return structured JSON output with advanced control over hook behavior.
|
|
157
209
|
|
|
158
|
-
|
|
210
|
+
For detailed information about available JSON fields and their behavior, see the [official documentation](https://docs.anthropic.com/en/docs/claude-code/hooks#advanced:-json-output).
|
|
159
211
|
|
|
160
|
-
|
|
212
|
+
## Documentation
|
|
161
213
|
|
|
162
|
-
|
|
163
|
-
run: (context) => {
|
|
164
|
-
// Typed input based on trigger configuration
|
|
165
|
-
const input = context.input;
|
|
166
|
-
|
|
167
|
-
// Response helpers
|
|
168
|
-
return context.success({ messageForUser: "Success!" });
|
|
169
|
-
// or context.blockingError("Error occurred");
|
|
170
|
-
// or context.nonBlockingError("Warning message");
|
|
171
|
-
// or context.json({ event: "EventName", output: {...} });
|
|
172
|
-
}
|
|
173
|
-
```
|
|
214
|
+
For more detailed information about Claude Code hooks, visit the [official documentation](https://docs.anthropic.com/en/docs/claude-code/hooks).
|
|
174
215
|
|
|
175
216
|
## Development
|
|
176
217
|
|
|
@@ -191,10 +232,6 @@ pnpm format
|
|
|
191
232
|
pnpm typecheck
|
|
192
233
|
```
|
|
193
234
|
|
|
194
|
-
## Documentation
|
|
195
|
-
|
|
196
|
-
For more detailed information about Claude Code hooks, visit the [official documentation](https://docs.anthropic.com/en/docs/claude-code/hooks).
|
|
197
|
-
|
|
198
235
|
## License
|
|
199
236
|
|
|
200
237
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
|
-
import { AgentInput, BashInput,
|
|
2
|
+
import { AgentInput, BashInput, ExitPlanModeInput, FileEditInput, FileReadInput, FileWriteInput, GlobInput, GrepInput, KillShellInput, ListMcpResourcesInput, NotebookEditInput, ReadMcpResourceInput, TaskOutputInput, TodoWriteInput, WebFetchInput, WebSearchInput } from "@anthropic-ai/claude-agent-sdk/sdk-tools";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/types.d.ts
|
|
5
5
|
type Awaitable<T> = Promise<T> | T;
|
|
@@ -33,6 +33,19 @@ declare const HookInputSchemas: {
|
|
|
33
33
|
tool_response: v.UnknownSchema;
|
|
34
34
|
tool_use_id: v.StringSchema<undefined>;
|
|
35
35
|
}, undefined>;
|
|
36
|
+
readonly PostToolUseFailure: v.ObjectSchema<{
|
|
37
|
+
readonly cwd: v.StringSchema<undefined>;
|
|
38
|
+
readonly permission_mode: v.ExactOptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
39
|
+
readonly session_id: v.StringSchema<undefined>;
|
|
40
|
+
readonly transcript_path: v.StringSchema<undefined>;
|
|
41
|
+
readonly hook_event_name: v.LiteralSchema<"PostToolUseFailure", undefined>;
|
|
42
|
+
} & {
|
|
43
|
+
tool_name: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TransformAction<string, AutoComplete<string>>]>;
|
|
44
|
+
error: v.StringSchema<undefined>;
|
|
45
|
+
is_interrupt: v.ExactOptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
46
|
+
tool_input: v.UnknownSchema;
|
|
47
|
+
tool_use_id: v.StringSchema<undefined>;
|
|
48
|
+
}, undefined>;
|
|
36
49
|
readonly Notification: v.ObjectSchema<{
|
|
37
50
|
readonly cwd: v.StringSchema<undefined>;
|
|
38
51
|
readonly permission_mode: v.ExactOptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
@@ -166,7 +179,7 @@ declare const HookInputSchemas: {
|
|
|
166
179
|
*
|
|
167
180
|
* @package
|
|
168
181
|
*/
|
|
169
|
-
type SupportedHookEvent = "PreToolUse" | "PostToolUse" | "Notification" | "UserPromptSubmit" | "SessionStart" | "SessionEnd" | "Stop" | "SubagentStart" | "SubagentStop" | "PreCompact" | "PermissionRequest";
|
|
182
|
+
type SupportedHookEvent = "PreToolUse" | "PostToolUse" | "PostToolUseFailure" | "Notification" | "UserPromptSubmit" | "SessionStart" | "SessionEnd" | "Stop" | "SubagentStart" | "SubagentStop" | "PreCompact" | "PermissionRequest";
|
|
170
183
|
//#endregion
|
|
171
184
|
//#region src/hooks/input/types.d.ts
|
|
172
185
|
/**
|
|
@@ -184,6 +197,8 @@ type HookInput = { [EventKey in SupportedHookEvent]: EventKey extends "PreToolUs
|
|
|
184
197
|
default: BaseHookInputs["PreToolUse"];
|
|
185
198
|
} : EventKey extends "PostToolUse" ? ToolSpecificPostToolUseInput & {
|
|
186
199
|
default: BaseHookInputs["PostToolUse"];
|
|
200
|
+
} : EventKey extends "PostToolUseFailure" ? ToolSpecificPostToolUseFailureInput & {
|
|
201
|
+
default: BaseHookInputs["PostToolUseFailure"];
|
|
187
202
|
} : {
|
|
188
203
|
default: BaseHookInputs[EventKey];
|
|
189
204
|
} };
|
|
@@ -240,6 +255,10 @@ type ToolSpecificPostToolUseInput = { [K in keyof ToolSchema]: Omit<BaseHookInpu
|
|
|
240
255
|
tool_name: K;
|
|
241
256
|
tool_response: ToolSchema[K]["response"];
|
|
242
257
|
} };
|
|
258
|
+
type ToolSpecificPostToolUseFailureInput = { [K in keyof ToolSchema]: Omit<BaseHookInputs["PostToolUseFailure"], "tool_input" | "tool_name"> & {
|
|
259
|
+
tool_input: ToolSchema[K]["input"];
|
|
260
|
+
tool_name: K;
|
|
261
|
+
} };
|
|
243
262
|
//#endregion
|
|
244
263
|
//#region src/hooks/permission.d.ts
|
|
245
264
|
/**
|
|
@@ -291,6 +310,7 @@ type PermissionUpdate = v.InferOutput<typeof permissionUpdateSchema>;
|
|
|
291
310
|
type HookOutput = {
|
|
292
311
|
PreToolUse: PreToolUseHookOutput;
|
|
293
312
|
PostToolUse: PostToolUseHookOutput;
|
|
313
|
+
PostToolUseFailure: PostToolUseFailureHookOutput;
|
|
294
314
|
UserPromptSubmit: UserPromptSubmitHookOutput;
|
|
295
315
|
Stop: StopHookOutput;
|
|
296
316
|
SubagentStart: SubagentStartHookOutput;
|
|
@@ -382,6 +402,15 @@ interface PostToolUseHookOutput extends CommonHookOutputs {
|
|
|
382
402
|
};
|
|
383
403
|
reason?: string;
|
|
384
404
|
}
|
|
405
|
+
interface PostToolUseFailureHookOutput extends CommonHookOutputs {
|
|
406
|
+
hookSpecificOutput?: {
|
|
407
|
+
hookEventName: "PostToolUseFailure";
|
|
408
|
+
/**
|
|
409
|
+
* Adds context for Claude to consider.
|
|
410
|
+
*/
|
|
411
|
+
additionalContext?: string;
|
|
412
|
+
};
|
|
413
|
+
}
|
|
385
414
|
/**
|
|
386
415
|
* @see {@link https://docs.anthropic.com/en/docs/claude-code/hooks#userpromptsubmit-decision-control}
|
|
387
416
|
*/
|
|
@@ -718,7 +747,7 @@ interface ToolSchema {
|
|
|
718
747
|
};
|
|
719
748
|
};
|
|
720
749
|
BashOutput: {
|
|
721
|
-
input:
|
|
750
|
+
input: TaskOutputInput;
|
|
722
751
|
response: unknown;
|
|
723
752
|
};
|
|
724
753
|
Edit: {
|
package/dist/index.mjs
CHANGED
|
@@ -110,6 +110,13 @@ const HookInputSchemas = {
|
|
|
110
110
|
tool_response: v.unknown(),
|
|
111
111
|
tool_use_id: v.string()
|
|
112
112
|
}),
|
|
113
|
+
PostToolUseFailure: buildHookInputSchema("PostToolUseFailure", {
|
|
114
|
+
tool_name: v.pipe(v.string(), v.transform((s) => s)),
|
|
115
|
+
error: v.string(),
|
|
116
|
+
is_interrupt: v.exactOptional(v.boolean()),
|
|
117
|
+
tool_input: v.unknown(),
|
|
118
|
+
tool_use_id: v.string()
|
|
119
|
+
}),
|
|
113
120
|
Notification: buildHookInputSchema("Notification", {
|
|
114
121
|
message: v.string(),
|
|
115
122
|
notification_type: v.string(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-hooks-ts",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.65",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Write claude code hooks with type safety",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -43,33 +43,33 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@arethetypeswrong/core": "0.18.2",
|
|
46
|
-
"@biomejs/biome": "2.3.7",
|
|
47
46
|
"@types/node": "24.10.1",
|
|
48
47
|
"@typescript/native-preview": "^7.0.0-dev.20251108.1",
|
|
49
48
|
"@virtual-live-lab/eslint-config": "2.3.1",
|
|
50
49
|
"@virtual-live-lab/tsconfig": "2.1.21",
|
|
51
50
|
"eslint": "9.39.1",
|
|
52
51
|
"eslint-plugin-import-access": "3.1.0",
|
|
53
|
-
"
|
|
52
|
+
"oxfmt": "0.16.0",
|
|
53
|
+
"pkg-pr-new": "0.0.62",
|
|
54
54
|
"publint": "0.3.15",
|
|
55
55
|
"release-it": "19.0.6",
|
|
56
56
|
"release-it-pnpm": "4.6.6",
|
|
57
|
-
"tsdown": "0.
|
|
58
|
-
"type-fest": "5.
|
|
57
|
+
"tsdown": "0.17.0",
|
|
58
|
+
"type-fest": "5.3.1",
|
|
59
59
|
"typescript": "5.9.3",
|
|
60
|
-
"typescript-eslint": "8.
|
|
60
|
+
"typescript-eslint": "8.48.1",
|
|
61
61
|
"unplugin-unused": "0.5.6",
|
|
62
|
-
"vitest": "4.0.
|
|
62
|
+
"vitest": "4.0.15"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@anthropic-ai/claude-agent-sdk": "0.1.
|
|
65
|
+
"@anthropic-ai/claude-agent-sdk": "0.1.65",
|
|
66
66
|
"valibot": "^1.1.0"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"check": "pnpm run format:check && pnpm run lint && pnpm run typecheck && pnpm run test && pnpm run build",
|
|
70
70
|
"lint": "eslint --max-warnings 0",
|
|
71
|
-
"format": "
|
|
72
|
-
"format:check": "
|
|
71
|
+
"format": "oxfmt",
|
|
72
|
+
"format:check": "oxfmt --check",
|
|
73
73
|
"typecheck": "tsgo --noEmit",
|
|
74
74
|
"test": "vitest --run",
|
|
75
75
|
"build": "tsdown",
|