cc-hooks-ts 2.0.56 → 2.0.70
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 +150 -89
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -0
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -1,37 +1,68 @@
|
|
|
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
|
+
- [How to follow the upstream changes](#how-to-follow-the-upstream-changes)
|
|
22
|
+
- [License](#license)
|
|
23
|
+
- [Contributing](#contributing)
|
|
24
|
+
|
|
25
|
+
<!-- /TOC -->
|
|
4
26
|
|
|
5
27
|
> [!NOTE]
|
|
6
|
-
>
|
|
7
|
-
>
|
|
28
|
+
> Starting with versions 2.0.42, we will raise our version number to match Claude Code whenever Hook-related changes occur.
|
|
29
|
+
>
|
|
30
|
+
> This ensures we can adopt newer type definitions while maintaining compatibility.
|
|
8
31
|
|
|
9
32
|
## Installation
|
|
10
33
|
|
|
11
34
|
```bash
|
|
12
|
-
|
|
35
|
+
# npm
|
|
36
|
+
npm i cc-hooks-ts
|
|
37
|
+
|
|
38
|
+
# yarn
|
|
39
|
+
yarn add cc-hooks-ts
|
|
40
|
+
|
|
41
|
+
# pnpm
|
|
42
|
+
pnpm add cc-hooks-ts
|
|
43
|
+
|
|
44
|
+
# Bun
|
|
45
|
+
bun add cc-hooks-ts
|
|
46
|
+
|
|
47
|
+
# Deno
|
|
48
|
+
deno add npm:cc-hooks-ts
|
|
13
49
|
```
|
|
14
50
|
|
|
15
51
|
## Basic Usage
|
|
16
52
|
|
|
17
53
|
### Define a Hook
|
|
18
54
|
|
|
19
|
-
Example of running a simple SessionStart hook on Bun:
|
|
20
|
-
|
|
21
55
|
```typescript
|
|
22
56
|
import { defineHook } from "cc-hooks-ts";
|
|
23
57
|
|
|
24
|
-
const
|
|
58
|
+
const hook = defineHook({
|
|
59
|
+
// Specify the event(s) that trigger this hook.
|
|
25
60
|
trigger: {
|
|
26
|
-
|
|
27
|
-
SessionStart: true,
|
|
28
|
-
PreToolUse: {
|
|
29
|
-
// PreToolUser and PostToolUse can be tool-specific. (affects type of context)
|
|
30
|
-
Read: true
|
|
31
|
-
}
|
|
61
|
+
SessionStart: true
|
|
32
62
|
},
|
|
63
|
+
// Implement what you want to do.
|
|
33
64
|
run: (context) => {
|
|
34
|
-
//
|
|
65
|
+
// Do something great here
|
|
35
66
|
return context.success({
|
|
36
67
|
messageForUser: "Welcome to your coding session!"
|
|
37
68
|
});
|
|
@@ -41,11 +72,11 @@ const sessionHook = defineHook({
|
|
|
41
72
|
// import.meta.main is available in Node.js 24.2+ and Bun and Deno
|
|
42
73
|
if (import.meta.main) {
|
|
43
74
|
const { runHook } = await import("cc-hooks-ts");
|
|
44
|
-
await runHook(
|
|
75
|
+
await runHook(hook);
|
|
45
76
|
}
|
|
46
77
|
```
|
|
47
78
|
|
|
48
|
-
###
|
|
79
|
+
### Configure Claude Code
|
|
49
80
|
|
|
50
81
|
Then, load defined hooks in your Claude Code settings at `~/.claude/settings.json`.
|
|
51
82
|
|
|
@@ -57,7 +88,7 @@ Then, load defined hooks in your Claude Code settings at `~/.claude/settings.jso
|
|
|
57
88
|
"hooks": [
|
|
58
89
|
{
|
|
59
90
|
"type": "command",
|
|
60
|
-
"command": "bun run --silent path/to/your/sessionHook.ts"
|
|
91
|
+
"command": "bun run -i --silent path/to/your/sessionHook.ts"
|
|
61
92
|
}
|
|
62
93
|
]
|
|
63
94
|
}
|
|
@@ -66,111 +97,122 @@ Then, load defined hooks in your Claude Code settings at `~/.claude/settings.jso
|
|
|
66
97
|
}
|
|
67
98
|
```
|
|
68
99
|
|
|
69
|
-
##
|
|
70
|
-
|
|
71
|
-
For better type inference in PreToolUse and PostToolUse hooks, you can extend the `ToolSchema` interface to define your own tool types:
|
|
100
|
+
## Tool Specific Hooks
|
|
72
101
|
|
|
73
|
-
|
|
102
|
+
In `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` events, you can define hooks specific to tools by specifying tool names in the trigger configuration.
|
|
74
103
|
|
|
75
|
-
|
|
104
|
+
For example, you can create a hook that only runs before the `Read` tool is used:
|
|
76
105
|
|
|
77
106
|
```typescript
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
};
|
|
107
|
+
const preReadHook = defineHook({
|
|
108
|
+
trigger: { PreToolUse: { Read: true } },
|
|
109
|
+
run: (context) => {
|
|
110
|
+
// context.input.tool_input is typed as { file_path: string; limit?: number; offset?: number; }
|
|
111
|
+
const { file_path } = context.input.tool_input;
|
|
112
|
+
|
|
113
|
+
if (file_path.includes('.env')) {
|
|
114
|
+
return context.blockingError('Cannot read environment files');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return context.success();
|
|
90
118
|
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (import.meta.main) {
|
|
122
|
+
const { runHook } = await import("cc-hooks-ts");
|
|
123
|
+
await runHook(preReadHook);
|
|
91
124
|
}
|
|
92
125
|
```
|
|
93
126
|
|
|
94
|
-
|
|
127
|
+
Then configure it in Claude Code settings:
|
|
95
128
|
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"hooks": {
|
|
132
|
+
"PreToolUse": [
|
|
133
|
+
{
|
|
134
|
+
"matcher": "Read",
|
|
135
|
+
"hooks": [
|
|
136
|
+
{
|
|
137
|
+
"type": "command",
|
|
138
|
+
"command": "bun run -i --silent path/to/your/preReadHook.ts"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
]
|
|
103
143
|
}
|
|
104
|
-
}
|
|
144
|
+
}
|
|
105
145
|
```
|
|
106
146
|
|
|
107
|
-
|
|
147
|
+
### Custom Tool Types Support
|
|
108
148
|
|
|
109
|
-
|
|
149
|
+
You can add support for custom tools by extending the tool type definitions.
|
|
110
150
|
|
|
111
|
-
|
|
151
|
+
This is useful when you want to your MCP-defined tools to have type-safe hook inputs.
|
|
112
152
|
|
|
113
153
|
```typescript
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
154
|
+
import { defineHook } from "cc-hooks-ts";
|
|
155
|
+
|
|
156
|
+
// Example: type-safe hooks for DeepWiki MCP Server tools
|
|
157
|
+
declare module "cc-hooks-ts" {
|
|
158
|
+
interface ToolSchema {
|
|
159
|
+
mcp__deepwiki__ask_question: {
|
|
160
|
+
input: {
|
|
161
|
+
question: string;
|
|
162
|
+
repoName: string;
|
|
163
|
+
};
|
|
164
|
+
response: unknown;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const deepWikiHook = defineHook({
|
|
170
|
+
trigger: { PreToolUse: { mcp__deepwiki__ask_question: true } },
|
|
117
171
|
run: (context) => {
|
|
118
|
-
// context.input.tool_input is typed as {
|
|
119
|
-
const {
|
|
172
|
+
// context.input.tool_input is typed as { question: string; repoName: string; }
|
|
173
|
+
const { question, repoName } = context.input.tool_input;
|
|
120
174
|
|
|
121
|
-
if (
|
|
122
|
-
return context.blockingError('
|
|
175
|
+
if (question.length > 500) {
|
|
176
|
+
return context.blockingError('Question is too long');
|
|
123
177
|
}
|
|
124
178
|
|
|
125
179
|
return context.success();
|
|
126
180
|
}
|
|
127
181
|
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Advanced Usage
|
|
185
|
+
|
|
186
|
+
### Conditional Hook Execution
|
|
128
187
|
|
|
129
|
-
|
|
130
|
-
|
|
188
|
+
You can conditionally execute hooks based on runtime logic using the `shouldRun` function.
|
|
189
|
+
If `shouldRun` returns `false`, the hook will be skipped.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { defineHook } from "cc-hooks-ts";
|
|
193
|
+
|
|
194
|
+
const hook = defineHook({
|
|
131
195
|
trigger: {
|
|
132
|
-
|
|
133
|
-
PostToolUse: { Read: true }
|
|
196
|
+
Notification: true
|
|
134
197
|
},
|
|
135
|
-
//
|
|
136
|
-
shouldRun: () => process.
|
|
198
|
+
// Only run this hook on macOS
|
|
199
|
+
shouldRun: () => process.platform === "darwin",
|
|
137
200
|
run: (context) => {
|
|
138
|
-
//
|
|
139
|
-
return context.success()
|
|
201
|
+
// Some macOS-specific logic like sending a notification using AppleScript
|
|
202
|
+
return context.success()
|
|
140
203
|
}
|
|
141
204
|
});
|
|
142
205
|
```
|
|
143
206
|
|
|
144
|
-
###
|
|
145
|
-
|
|
146
|
-
Executes hooks with complete lifecycle management:
|
|
207
|
+
### Advanced JSON Output
|
|
147
208
|
|
|
148
|
-
|
|
149
|
-
- Validates input using Valibot schemas
|
|
150
|
-
- Creates typed context
|
|
151
|
-
- Executes hook handler
|
|
152
|
-
- Formats and outputs results
|
|
209
|
+
Use `context.json()` to return structured JSON output with advanced control over hook behavior.
|
|
153
210
|
|
|
154
|
-
|
|
155
|
-
await runHook(hook);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Hook Context
|
|
211
|
+
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
212
|
|
|
160
|
-
|
|
213
|
+
## Documentation
|
|
161
214
|
|
|
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
|
-
```
|
|
215
|
+
For more detailed information about Claude Code hooks, visit the [official documentation](https://docs.anthropic.com/en/docs/claude-code/hooks).
|
|
174
216
|
|
|
175
217
|
## Development
|
|
176
218
|
|
|
@@ -191,9 +233,28 @@ pnpm format
|
|
|
191
233
|
pnpm typecheck
|
|
192
234
|
```
|
|
193
235
|
|
|
194
|
-
|
|
236
|
+
### How to follow the upstream changes
|
|
195
237
|
|
|
196
|
-
|
|
238
|
+
1. Install the latest version of `@anthropic-ai/claude-agent-sdk` and run `pnpm run check`.
|
|
239
|
+
- If the command passes without errors, there are no type changes.
|
|
240
|
+
|
|
241
|
+
2. Get diff of the types. This example gets the diff between Claude Code 2.0.69 and 2.0.70:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
npm diff --diff=@anthropic-ai/claude-agent-sdk@0.1.69 --diff=@anthropic-ai/claude-agent-sdk@0.1.70 '**/*.d.ts'
|
|
245
|
+
|
|
246
|
+
# You can use dandavison/delta for better diff visualization
|
|
247
|
+
npm diff --diff=@anthropic-ai/claude-agent-sdk@0.1.69 --diff=@anthropic-ai/claude-agent-sdk@0.1.70 '**/*.d.ts' | delta --side-by-side
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
3. Reflect the changes.
|
|
251
|
+
- Edit `src/hooks/` for changed hook input / output types.
|
|
252
|
+
- No need for adding tests in most cases since we are testing the whole type definitions in these files:
|
|
253
|
+
- `src/hooks/input/schemas.test-d.ts`
|
|
254
|
+
- `src/hooks/output/index.test-d.ts`
|
|
255
|
+
- `src/hooks/event.test-d.ts`
|
|
256
|
+
- `src/hooks/permission.test-d.ts`
|
|
257
|
+
- Edit `src/index.ts` for changed tool input / output types.
|
|
197
258
|
|
|
198
259
|
## License
|
|
199
260
|
|
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;
|
|
@@ -157,7 +157,7 @@ declare const HookInputSchemas: {
|
|
|
157
157
|
readonly type: v.LiteralSchema<"removeRules", undefined>;
|
|
158
158
|
}, undefined>, v.ObjectSchema<{
|
|
159
159
|
readonly destination: v.UnionSchema<[v.LiteralSchema<"userSettings", undefined>, v.LiteralSchema<"projectSettings", undefined>, v.LiteralSchema<"localSettings", undefined>, v.LiteralSchema<"session", undefined>, v.LiteralSchema<"cliArg", undefined>], undefined>;
|
|
160
|
-
readonly mode: v.UnionSchema<[v.LiteralSchema<"acceptEdits", undefined>, v.LiteralSchema<"bypassPermissions", undefined>, v.LiteralSchema<"default", undefined>, v.LiteralSchema<"dontAsk", undefined>, v.LiteralSchema<"plan", undefined>], undefined>;
|
|
160
|
+
readonly mode: v.UnionSchema<[v.LiteralSchema<"acceptEdits", undefined>, v.LiteralSchema<"bypassPermissions", undefined>, v.LiteralSchema<"default", undefined>, v.LiteralSchema<"dontAsk", undefined>, v.LiteralSchema<"delegate", undefined>, v.LiteralSchema<"plan", undefined>], undefined>;
|
|
161
161
|
readonly type: v.LiteralSchema<"setMode", undefined>;
|
|
162
162
|
}, undefined>, v.ObjectSchema<{
|
|
163
163
|
readonly destination: v.UnionSchema<[v.LiteralSchema<"userSettings", undefined>, v.LiteralSchema<"projectSettings", undefined>, v.LiteralSchema<"localSettings", undefined>, v.LiteralSchema<"session", undefined>, v.LiteralSchema<"cliArg", undefined>], undefined>;
|
|
@@ -290,7 +290,7 @@ declare const permissionUpdateSchema: v.VariantSchema<"type", [v.ObjectSchema<{
|
|
|
290
290
|
readonly type: v.LiteralSchema<"removeRules", undefined>;
|
|
291
291
|
}, undefined>, v.ObjectSchema<{
|
|
292
292
|
readonly destination: v.UnionSchema<[v.LiteralSchema<"userSettings", undefined>, v.LiteralSchema<"projectSettings", undefined>, v.LiteralSchema<"localSettings", undefined>, v.LiteralSchema<"session", undefined>, v.LiteralSchema<"cliArg", undefined>], undefined>;
|
|
293
|
-
readonly mode: v.UnionSchema<[v.LiteralSchema<"acceptEdits", undefined>, v.LiteralSchema<"bypassPermissions", undefined>, v.LiteralSchema<"default", undefined>, v.LiteralSchema<"dontAsk", undefined>, v.LiteralSchema<"plan", undefined>], undefined>;
|
|
293
|
+
readonly mode: v.UnionSchema<[v.LiteralSchema<"acceptEdits", undefined>, v.LiteralSchema<"bypassPermissions", undefined>, v.LiteralSchema<"default", undefined>, v.LiteralSchema<"dontAsk", undefined>, v.LiteralSchema<"delegate", undefined>, v.LiteralSchema<"plan", undefined>], undefined>;
|
|
294
294
|
readonly type: v.LiteralSchema<"setMode", undefined>;
|
|
295
295
|
}, undefined>, v.ObjectSchema<{
|
|
296
296
|
readonly destination: v.UnionSchema<[v.LiteralSchema<"userSettings", undefined>, v.LiteralSchema<"projectSettings", undefined>, v.LiteralSchema<"localSettings", undefined>, v.LiteralSchema<"session", undefined>, v.LiteralSchema<"cliArg", undefined>], undefined>;
|
|
@@ -747,7 +747,7 @@ interface ToolSchema {
|
|
|
747
747
|
};
|
|
748
748
|
};
|
|
749
749
|
BashOutput: {
|
|
750
|
-
input:
|
|
750
|
+
input: TaskOutputInput;
|
|
751
751
|
response: unknown;
|
|
752
752
|
};
|
|
753
753
|
Edit: {
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-hooks-ts",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.70",
|
|
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
|
-
"@
|
|
47
|
-
"@types/node": "24.10.1",
|
|
46
|
+
"@types/node": "25.0.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
|
-
"eslint": "9.39.
|
|
50
|
+
"eslint": "9.39.2",
|
|
52
51
|
"eslint-plugin-import-access": "3.1.0",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
52
|
+
"oxfmt": "0.17.0",
|
|
53
|
+
"pkg-pr-new": "0.0.62",
|
|
54
|
+
"publint": "0.3.16",
|
|
55
|
+
"release-it": "19.1.0",
|
|
56
56
|
"release-it-pnpm": "4.6.6",
|
|
57
|
-
"tsdown": "0.
|
|
58
|
-
"type-fest": "5.
|
|
57
|
+
"tsdown": "0.17.2",
|
|
58
|
+
"type-fest": "5.3.1",
|
|
59
59
|
"typescript": "5.9.3",
|
|
60
|
-
"typescript-eslint": "8.
|
|
60
|
+
"typescript-eslint": "8.49.0",
|
|
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.70",
|
|
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",
|