@s_s/agent-kit 1.0.0 → 1.2.0
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 +154 -79
- package/build/create-kit.js +6 -3
- package/build/create-kit.js.map +1 -1
- package/build/define-hooks.d.ts +31 -0
- package/build/define-hooks.js +192 -0
- package/build/define-hooks.js.map +1 -0
- package/build/hooks.d.ts +8 -5
- package/build/hooks.js +117 -119
- package/build/hooks.js.map +1 -1
- package/build/index.d.ts +4 -6
- package/build/index.js +6 -5
- package/build/index.js.map +1 -1
- package/build/paths.d.ts +31 -0
- package/build/paths.js +119 -0
- package/build/paths.js.map +1 -0
- package/build/prompt.d.ts +4 -0
- package/build/prompt.js +10 -19
- package/build/prompt.js.map +1 -1
- package/build/types.d.ts +66 -13
- package/build/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ MCP Server / Skill 开发基础设施库。提供 agent 检测、prompt 注入
|
|
|
6
6
|
|
|
7
7
|
- **Agent 检测** — 通过文件系统特征或 MCP clientInfo 自动识别 agent 类型(OpenCode、Claude Code、OpenClaw、Codex)
|
|
8
8
|
- **Prompt 注入** — 向 agent 配置文件(`AGENTS.md`、`CLAUDE.md`、manifests 等)注入自定义指令,支持幂等更新
|
|
9
|
-
- **
|
|
9
|
+
- **Hook 安装** — 用户提供完整 hook 内容,agent-kit 负责写入正确路径、管理 settings.json 和生命周期
|
|
10
10
|
- **跨平台数据目录** — 自动适配 macOS / Linux / Windows,支持 global / project 两种作用域
|
|
11
11
|
- **零运行时依赖** — 纯 TypeScript,零 dependencies
|
|
12
12
|
- **工厂模式** — `createKit(name)` 返回绑定了工具名的函数集,支持解构,无全局状态
|
|
@@ -20,42 +20,52 @@ npm install @s_s/agent-kit
|
|
|
20
20
|
## 快速开始
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
|
-
import { createKit,
|
|
23
|
+
import { createKit, defineHooks, detectAgent } from '@s_s/agent-kit';
|
|
24
24
|
|
|
25
|
-
// 1. 创建 kit
|
|
25
|
+
// 1. 创建 kit 实例
|
|
26
26
|
const { injectPrompt, installHooks, getDataDir } = createKit('my-mcp');
|
|
27
27
|
|
|
28
|
-
// 2. 声明 hook
|
|
29
|
-
hooks.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
hooks.beforeToolCall({
|
|
36
|
-
match: /^Bash/,
|
|
37
|
-
handler: (ctx) => {
|
|
38
|
-
if (ctx.args.command?.includes('rm -rf')) {
|
|
39
|
-
return { block: true, reason: 'Dangerous command blocked' };
|
|
40
|
-
}
|
|
28
|
+
// 2. 声明 hook(每个 agent 独立声明,内容由用户完全控制)
|
|
29
|
+
// hooks/claude-code.ts
|
|
30
|
+
const claudeHooks = defineHooks('claude-code', [
|
|
31
|
+
{
|
|
32
|
+
events: ['UserPromptSubmit'],
|
|
33
|
+
content: '#!/bin/bash\necho "<reminder>Use my-mcp tools.</reminder>"',
|
|
41
34
|
},
|
|
35
|
+
{
|
|
36
|
+
events: ['PreToolUse'],
|
|
37
|
+
content:
|
|
38
|
+
'#!/bin/bash\n# 拦截危险操作\nTOOL_NAME="$HOOK_TOOL_NAME"\nif [[ "$TOOL_NAME" == "Bash" ]]; then echo "checked"; fi',
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// hooks/opencode.ts
|
|
43
|
+
const opencodeHooks = defineHooks('opencode', {
|
|
44
|
+
events: ['experimental.chat.messages.transform'],
|
|
45
|
+
content: `export default {
|
|
46
|
+
name: 'my-mcp',
|
|
47
|
+
hooks: {
|
|
48
|
+
'experimental.chat.messages.transform': async (messages) => {
|
|
49
|
+
messages.push({ role: 'user', content: '<reminder>Use my-mcp tools.</reminder>' });
|
|
50
|
+
return messages;
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};`,
|
|
42
54
|
});
|
|
43
55
|
|
|
44
56
|
// 3. 检测当前 agent
|
|
45
57
|
const agent = await detectAgent();
|
|
46
58
|
if (!agent) throw new Error('No supported agent detected');
|
|
47
59
|
|
|
48
|
-
// 4. 注入 prompt
|
|
60
|
+
// 4. 注入 prompt
|
|
49
61
|
await injectPrompt(agent, '## My MCP\nInstructions for the agent...');
|
|
50
62
|
|
|
51
|
-
// 5. 安装 hooks
|
|
52
|
-
const result = await installHooks(agent);
|
|
63
|
+
// 5. 安装 hooks
|
|
64
|
+
const result = await installHooks(agent, [claudeHooks, opencodeHooks]);
|
|
53
65
|
console.log(`Hooks installed: ${result.filesWritten.join(', ')}`);
|
|
54
|
-
console.log(`Warnings: ${result.warnings.join('\n')}`);
|
|
55
66
|
|
|
56
67
|
// 6. 获取数据目录路径
|
|
57
68
|
const dataDir = getDataDir(); // global scope
|
|
58
|
-
const projectDir = getDataDir({ scope: 'project', projectRoot: '/path/to/project' });
|
|
59
69
|
```
|
|
60
70
|
|
|
61
71
|
## API
|
|
@@ -83,23 +93,15 @@ createKit(name: string, options?: KitOptions): Kit
|
|
|
83
93
|
|
|
84
94
|
返回的 Kit 对象包含以下方法:
|
|
85
95
|
|
|
86
|
-
| 方法 | 说明
|
|
87
|
-
| --------------------------------------- |
|
|
88
|
-
| `injectPrompt(agent, prompt, options?)` | 注入 prompt 到 agent 配置文件
|
|
89
|
-
| `hasPromptInjected(agent, options?)` | 检查 prompt 是否已注入
|
|
90
|
-
| `installHooks(agent)`
|
|
91
|
-
| `uninstallHooks(agent)` | 卸载 hooks
|
|
92
|
-
| `hasHooksInstalled(agent)` | 检查 hooks 是否已安装
|
|
93
|
-
| `getDataDir(options?)` | 获取跨平台数据目录路径
|
|
94
|
-
|
|
95
|
-
### Hook 系统
|
|
96
|
-
|
|
97
|
-
Hook 系统分为两个阶段:**声明**和**安装**。
|
|
98
|
-
|
|
99
|
-
1. **声明**:通过全局 `hooks.*` API 声明意图(`inject` / `beforeToolCall` / `afterToolCall` / `onSession` / `onPermission`),以及 `raw` / `extend` 两种高级注册方式
|
|
100
|
-
2. **安装**:调用 `kit.installHooks(agent)` 时,从全局注册中心读取所有声明,翻译为目标 agent 的原生格式并写入磁盘
|
|
101
|
-
|
|
102
|
-
详细的 API 说明、示例、三层优先级机制和降级行为,请参阅 **[Hook 使用指南](docs/hook-usage.md)**。各 agent 原生 hook 能力的完整横向对比,请参阅 **[Hook 横向对比](docs/hooks-comparison.md)**。
|
|
96
|
+
| 方法 | 说明 |
|
|
97
|
+
| --------------------------------------- | --------------------------------------------------------- |
|
|
98
|
+
| `injectPrompt(agent, prompt, options?)` | 注入 prompt 到 agent 配置文件 |
|
|
99
|
+
| `hasPromptInjected(agent, options?)` | 检查 prompt 是否已注入 |
|
|
100
|
+
| `installHooks(agent, hooks)` | 安装 hooks(传入 defineHooks 声明) |
|
|
101
|
+
| `uninstallHooks(agent)` | 卸载 hooks |
|
|
102
|
+
| `hasHooksInstalled(agent)` | 检查 hooks 是否已安装 |
|
|
103
|
+
| `getDataDir(options?)` | 获取跨平台数据目录路径 |
|
|
104
|
+
| `resolvePaths(agent, options?)` | 解析 agent 相关路径(配置文件、hook 目录、settings 文件) |
|
|
103
105
|
|
|
104
106
|
### Kit 方法详情
|
|
105
107
|
|
|
@@ -115,15 +117,20 @@ injectPrompt(agent: AgentType, prompt: string, options?: ScopeOptions): Promise<
|
|
|
115
117
|
- 后续调用:替换已有标记块(幂等更新)
|
|
116
118
|
- 自动创建目录和文件
|
|
117
119
|
|
|
118
|
-
#### `kit.installHooks(agent)`
|
|
120
|
+
#### `kit.installHooks(agent, hooks)`
|
|
119
121
|
|
|
120
|
-
为指定 agent 安装 hooks
|
|
122
|
+
为指定 agent 安装 hooks。接受一个或多个 `HookSet`(由 `defineHooks()` 返回),将内容写入 agent 的 hook 目录。
|
|
121
123
|
|
|
122
124
|
```typescript
|
|
123
|
-
installHooks(agent: AgentType): Promise<HookInstallResult>
|
|
125
|
+
installHooks(agent: AgentType, hooks: HookSet | HookSet[]): Promise<HookInstallResult>
|
|
124
126
|
```
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
- 自动过滤:只处理与 `agent` 匹配的 `HookSet`,其余忽略
|
|
129
|
+
- Claude Code / Codex:写入 shell 脚本 + 自动注册到 `settings.json`
|
|
130
|
+
- OpenCode:写入 TypeScript 插件文件到 plugins 目录
|
|
131
|
+
- OpenClaw:生成 `HOOK.md`(YAML frontmatter)+ `handler.ts`
|
|
132
|
+
|
|
133
|
+
返回值:
|
|
127
134
|
|
|
128
135
|
```typescript
|
|
129
136
|
interface HookInstallResult {
|
|
@@ -132,20 +139,11 @@ interface HookInstallResult {
|
|
|
132
139
|
filesWritten: string[];
|
|
133
140
|
settingsUpdated: boolean;
|
|
134
141
|
notes: string[];
|
|
135
|
-
warnings: string[];
|
|
136
|
-
skipped: SkippedIntent[]; // 被跳过的意图
|
|
142
|
+
warnings: string[];
|
|
137
143
|
error?: string;
|
|
138
144
|
}
|
|
139
145
|
```
|
|
140
146
|
|
|
141
|
-
各 agent 的翻译方式:
|
|
142
|
-
|
|
143
|
-
| Agent | 生成内容 |
|
|
144
|
-
| ------------------- | ------------------------------------------------------- |
|
|
145
|
-
| claude-code / codex | Shell 脚本(多个原生 hook),自动注册到 `settings.json` |
|
|
146
|
-
| openclaw | `HOOK.md` + `handler.ts`(内部 hook + 插件 hook) |
|
|
147
|
-
| opencode | 单个 TypeScript 插件文件(多个 hook 合并) |
|
|
148
|
-
|
|
149
147
|
#### `kit.uninstallHooks(agent)`
|
|
150
148
|
|
|
151
149
|
清理已安装的 hook 文件和 settings.json 条目。
|
|
@@ -169,6 +167,88 @@ getDataDir(options?: ScopeOptions): string
|
|
|
169
167
|
|
|
170
168
|
全局路径可通过环境变量覆盖(默认变量名:`{NAME}_DATA_DIR`)。
|
|
171
169
|
|
|
170
|
+
#### `kit.resolvePaths(agent, options?)`
|
|
171
|
+
|
|
172
|
+
解析指定 agent 的所有相关路径。自动使用 kit 的 `name` 作为 toolName,使用侧无需关心。
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
resolvePaths(agent: AgentType, options?: ScopeOptions): AgentPaths
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
返回值:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
interface AgentPaths {
|
|
182
|
+
configFile: string; // agent 配置文件路径(始终返回)
|
|
183
|
+
hookDir?: string; // hook 目录路径(始终返回,因为 kit 已绑定 toolName)
|
|
184
|
+
settingsFile?: string; // settings.json 路径(仅 claude-code / codex)
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
示例:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const kit = createKit('mnemo');
|
|
192
|
+
|
|
193
|
+
// Global scope
|
|
194
|
+
const paths = kit.resolvePaths('claude-code');
|
|
195
|
+
// → {
|
|
196
|
+
// configFile: '~/.claude/CLAUDE.md',
|
|
197
|
+
// hookDir: '~/.claude/hooks/mnemo',
|
|
198
|
+
// settingsFile: '~/.claude/settings.json',
|
|
199
|
+
// }
|
|
200
|
+
|
|
201
|
+
// Project scope
|
|
202
|
+
const paths = kit.resolvePaths('opencode', { scope: 'project', projectRoot: '/my/project' });
|
|
203
|
+
// → {
|
|
204
|
+
// configFile: '/my/project/AGENTS.md',
|
|
205
|
+
// hookDir: '~/.config/opencode/plugins',
|
|
206
|
+
// }
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Hook 系统
|
|
210
|
+
|
|
211
|
+
Hook 系统分为两个阶段:**声明**和**安装**。
|
|
212
|
+
|
|
213
|
+
1. **声明**:通过 `defineHooks(agent, definitions)` 纯函数声明 hook 内容。每个 agent 独立声明,内容由用户完全控制。支持分文件组织。
|
|
214
|
+
2. **安装**:调用 `kit.installHooks(agent, hooks)` 时,将声明内容写入目标 agent 的 hook 目录。
|
|
215
|
+
|
|
216
|
+
#### `defineHooks(agent, definitions)`
|
|
217
|
+
|
|
218
|
+
纯函数,不依赖任何实例或全局状态。做运行时校验(事件名合法性、content 非空),返回类型安全的 `HookSet` 数据对象。
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { defineHooks } from '@s_s/agent-kit';
|
|
222
|
+
|
|
223
|
+
// 单条声明
|
|
224
|
+
const hooks = defineHooks('claude-code', {
|
|
225
|
+
events: ['PreToolUse', 'PostToolUse'], // 多事件共享同一内容
|
|
226
|
+
content: '#!/bin/bash\necho "hook fired"',
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// 多条声明
|
|
230
|
+
const hooks = defineHooks('claude-code', [
|
|
231
|
+
{ events: ['PreToolUse'], content: '#!/bin/bash\necho "pre"' },
|
|
232
|
+
{ events: ['PostToolUse'], content: '#!/bin/bash\necho "post"' },
|
|
233
|
+
]);
|
|
234
|
+
|
|
235
|
+
// OpenClaw — 支持 description 参数
|
|
236
|
+
const hooks = defineHooks('openclaw', {
|
|
237
|
+
events: ['session_start', 'before_tool_call'],
|
|
238
|
+
content: 'export default async function(event) { return event; }',
|
|
239
|
+
description: '注入项目规范到每轮对话',
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**设计原则**:agent-kit 是"安装器"而非"翻译器"。用户提供各 agent 的原生 hook 内容,agent-kit 只负责:
|
|
244
|
+
|
|
245
|
+
- 知道正确的文件路径
|
|
246
|
+
- 写入文件
|
|
247
|
+
- 管理 settings.json 合并(Claude Code / Codex)
|
|
248
|
+
- 安装 / 卸载生命周期
|
|
249
|
+
|
|
250
|
+
详细的使用示例和各 agent 事件名列表,请参阅 **[Hook 使用指南](docs/hook-usage.md)**。各 agent 原生 hook 能力的完整横向对比,请参阅 **[Hook 横向对比](docs/hooks-comparison.md)**。
|
|
251
|
+
|
|
172
252
|
### 独立函数
|
|
173
253
|
|
|
174
254
|
以下函数不依赖 kit 实例,可直接使用。
|
|
@@ -208,23 +288,6 @@ detectProjectRoot(cwd?: string): Promise<string>
|
|
|
208
288
|
|
|
209
289
|
优先使用 `git rev-parse --show-toplevel`,失败则向上遍历查找标记文件(`.git`、`package.json`、`pyproject.toml`、`Cargo.toml`、`go.mod`),都未找到则回退到 `cwd`。
|
|
210
290
|
|
|
211
|
-
### 能力矩阵 API
|
|
212
|
-
|
|
213
|
-
编程式查询各 agent 对各 intent 的支持程度。
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
import { CAPABILITY_MATRIX, checkDegradation, isIntentFullyUnsupported } from '@s_s/agent-kit';
|
|
217
|
-
|
|
218
|
-
// 查询特定能力
|
|
219
|
-
CAPABILITY_MATRIX.opencode.beforeToolCall.block.level; // 'partial'
|
|
220
|
-
|
|
221
|
-
// 检查某 intent 在某 agent 上的降级情况
|
|
222
|
-
const warnings = checkDegradation('opencode', 'beforeToolCall');
|
|
223
|
-
|
|
224
|
-
// 检查是否完全不支持
|
|
225
|
-
isIntentFullyUnsupported('openclaw', 'onPermission'); // true
|
|
226
|
-
```
|
|
227
|
-
|
|
228
291
|
## 类型
|
|
229
292
|
|
|
230
293
|
```typescript
|
|
@@ -246,10 +309,31 @@ interface Kit {
|
|
|
246
309
|
readonly name: string;
|
|
247
310
|
injectPrompt(agent: AgentType, prompt: string, options?: ScopeOptions): Promise<void>;
|
|
248
311
|
hasPromptInjected(agent: AgentType, options?: ScopeOptions): Promise<boolean>;
|
|
249
|
-
installHooks(agent: AgentType): Promise<HookInstallResult>;
|
|
312
|
+
installHooks(agent: AgentType, hooks: HookSet | HookSet[]): Promise<HookInstallResult>;
|
|
250
313
|
uninstallHooks(agent: AgentType): Promise<{ success: boolean; removed: string[]; error?: string }>;
|
|
251
314
|
hasHooksInstalled(agent: AgentType): Promise<boolean>;
|
|
252
315
|
getDataDir(options?: ScopeOptions): string;
|
|
316
|
+
resolvePaths(agent: AgentType, options?: ScopeOptions): AgentPaths;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Agent 路径
|
|
320
|
+
interface AgentPaths {
|
|
321
|
+
configFile: string;
|
|
322
|
+
hookDir?: string;
|
|
323
|
+
settingsFile?: string;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Hook 定义
|
|
327
|
+
interface HookDefinition<A extends AgentType = AgentType> {
|
|
328
|
+
events: string[]; // agent 原生事件名
|
|
329
|
+
content: string; // hook 内容(shell 脚本、TypeScript 代码等)
|
|
330
|
+
description?: string; // OpenClaw 专属 — HOOK.md 描述
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// defineHooks() 返回的数据对象
|
|
334
|
+
interface HookSet<A extends AgentType = AgentType> {
|
|
335
|
+
readonly agent: A;
|
|
336
|
+
readonly definitions: readonly HookDefinition<A>[];
|
|
253
337
|
}
|
|
254
338
|
|
|
255
339
|
interface HookInstallResult {
|
|
@@ -259,17 +343,8 @@ interface HookInstallResult {
|
|
|
259
343
|
settingsUpdated: boolean;
|
|
260
344
|
notes: string[];
|
|
261
345
|
warnings: string[];
|
|
262
|
-
skipped: SkippedIntent[];
|
|
263
346
|
error?: string;
|
|
264
347
|
}
|
|
265
|
-
|
|
266
|
-
interface SkippedIntent {
|
|
267
|
-
intent: string;
|
|
268
|
-
agent: string;
|
|
269
|
-
reason: string;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
type SupportLevel = 'supported' | 'partial' | 'unsupported';
|
|
273
348
|
```
|
|
274
349
|
|
|
275
350
|
## 支持的 Agent
|
package/build/create-kit.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { injectPrompt, hasPromptInjected } from './prompt.js';
|
|
2
2
|
import { installHooks, uninstallHooks, hasHooksInstalled } from './hooks.js';
|
|
3
|
-
import { getDataDir } from './
|
|
3
|
+
import { getDataDir, resolveAgentPaths } from './paths.js';
|
|
4
4
|
/**
|
|
5
5
|
* Create a kit instance bound to the given tool name.
|
|
6
6
|
*
|
|
@@ -37,8 +37,8 @@ export function createKit(name, options) {
|
|
|
37
37
|
hasPromptInjected(agent, scopeOptions) {
|
|
38
38
|
return hasPromptInjected(name, agent, scopeOptions);
|
|
39
39
|
},
|
|
40
|
-
installHooks(agent) {
|
|
41
|
-
return installHooks(name, agent);
|
|
40
|
+
installHooks(agent, hooks) {
|
|
41
|
+
return installHooks(name, agent, hooks);
|
|
42
42
|
},
|
|
43
43
|
uninstallHooks(agent) {
|
|
44
44
|
return uninstallHooks(name, agent);
|
|
@@ -49,6 +49,9 @@ export function createKit(name, options) {
|
|
|
49
49
|
getDataDir(scopeOptions) {
|
|
50
50
|
return getDataDir(config, scopeOptions);
|
|
51
51
|
},
|
|
52
|
+
resolvePaths(agent, scopeOptions) {
|
|
53
|
+
return resolveAgentPaths(agent, name, scopeOptions);
|
|
54
|
+
},
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
57
|
//# sourceMappingURL=create-kit.js.map
|
package/build/create-kit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-kit.js","sourceRoot":"","sources":["../src/create-kit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"create-kit.js","sourceRoot":"","sources":["../src/create-kit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,OAAoB;IACxD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAsB;QAC9B,IAAI;QACJ,IAAI,EAAE,OAAO,EAAE,IAAI;QACnB,WAAW,EAAE,OAAO,EAAE,WAAW;KACpC,CAAC;IAEF,OAAO;QACH,IAAI,IAAI;YACJ,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,YAAY,CAAC,KAAgB,EAAE,MAAc,EAAE,YAA2B;YACtE,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC7E,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;QAC3D,CAAC;QAED,iBAAiB,CAAC,KAAgB,EAAE,YAA2B;YAC3D,OAAO,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;QACxD,CAAC;QAED,YAAY,CAAC,KAAgB,EAAE,KAA0B;YACrD,OAAO,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;QAED,cAAc,CAAC,KAAgB;YAC3B,OAAO,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;QAED,iBAAiB,CAAC,KAAgB;YAC9B,OAAO,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC1C,CAAC;QAED,UAAU,CAAC,YAA2B;YAClC,OAAO,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC5C,CAAC;QAED,YAAY,CAAC,KAAgB,EAAE,YAA2B;YACtD,OAAO,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;QACxD,CAAC;KACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AgentType, HookDefinition, HookSet } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Define hooks for a specific agent. Returns a validated HookSet that can be
|
|
4
|
+
* passed to `kit.installHooks()`.
|
|
5
|
+
*
|
|
6
|
+
* Pure function — no side effects, no global state, no instance dependency.
|
|
7
|
+
*
|
|
8
|
+
* @param agent - Target agent type.
|
|
9
|
+
* @param definitions - A single HookDefinition or an array of HookDefinitions.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Single definition
|
|
14
|
+
* const hooks = defineHooks('claude-code', {
|
|
15
|
+
* events: ['PreToolUse', 'PostToolUse'],
|
|
16
|
+
* content: '#!/bin/bash\necho "hook fired"',
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Multiple definitions
|
|
20
|
+
* const hooks = defineHooks('claude-code', [
|
|
21
|
+
* { events: ['PreToolUse'], content: '#!/bin/bash\necho "pre"' },
|
|
22
|
+
* { events: ['PostToolUse'], content: '#!/bin/bash\necho "post"' },
|
|
23
|
+
* ]);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function defineHooks<A extends AgentType>(agent: A, definitions: HookDefinition<A> | HookDefinition<A>[]): HookSet<A>;
|
|
27
|
+
/**
|
|
28
|
+
* Get the set of valid event names for a given agent.
|
|
29
|
+
* Useful for tooling and documentation generation.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getValidEvents(agent: AgentType): ReadonlySet<string>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { AGENT_TYPES } from './types.js';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Valid event names per agent (for runtime validation)
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
const VALID_EVENTS = {
|
|
6
|
+
'claude-code': new Set([
|
|
7
|
+
'SessionStart',
|
|
8
|
+
'InstructionsLoaded',
|
|
9
|
+
'UserPromptSubmit',
|
|
10
|
+
'PreToolUse',
|
|
11
|
+
'PermissionRequest',
|
|
12
|
+
'PostToolUse',
|
|
13
|
+
'PostToolUseFailure',
|
|
14
|
+
'Notification',
|
|
15
|
+
'SubagentStart',
|
|
16
|
+
'SubagentStop',
|
|
17
|
+
'Stop',
|
|
18
|
+
'TeammateIdle',
|
|
19
|
+
'TaskCompleted',
|
|
20
|
+
'ConfigChange',
|
|
21
|
+
'WorktreeCreate',
|
|
22
|
+
'WorktreeRemove',
|
|
23
|
+
'PreCompact',
|
|
24
|
+
'PostCompact',
|
|
25
|
+
'Elicitation',
|
|
26
|
+
'ElicitationResult',
|
|
27
|
+
'SessionEnd',
|
|
28
|
+
]),
|
|
29
|
+
codex: new Set([
|
|
30
|
+
'SessionStart',
|
|
31
|
+
'InstructionsLoaded',
|
|
32
|
+
'UserPromptSubmit',
|
|
33
|
+
'PreToolUse',
|
|
34
|
+
'PermissionRequest',
|
|
35
|
+
'PostToolUse',
|
|
36
|
+
'PostToolUseFailure',
|
|
37
|
+
'Notification',
|
|
38
|
+
'SubagentStart',
|
|
39
|
+
'SubagentStop',
|
|
40
|
+
'Stop',
|
|
41
|
+
'TeammateIdle',
|
|
42
|
+
'TaskCompleted',
|
|
43
|
+
'ConfigChange',
|
|
44
|
+
'WorktreeCreate',
|
|
45
|
+
'WorktreeRemove',
|
|
46
|
+
'PreCompact',
|
|
47
|
+
'PostCompact',
|
|
48
|
+
'Elicitation',
|
|
49
|
+
'ElicitationResult',
|
|
50
|
+
'SessionEnd',
|
|
51
|
+
]),
|
|
52
|
+
opencode: new Set([
|
|
53
|
+
'event',
|
|
54
|
+
'config',
|
|
55
|
+
'tool',
|
|
56
|
+
'auth',
|
|
57
|
+
'chat.message',
|
|
58
|
+
'chat.params',
|
|
59
|
+
'chat.headers',
|
|
60
|
+
'permission.ask',
|
|
61
|
+
'command.execute.before',
|
|
62
|
+
'tool.execute.before',
|
|
63
|
+
'shell.env',
|
|
64
|
+
'tool.execute.after',
|
|
65
|
+
'experimental.chat.messages.transform',
|
|
66
|
+
'experimental.chat.system.transform',
|
|
67
|
+
'experimental.session.compacting',
|
|
68
|
+
'experimental.text.complete',
|
|
69
|
+
'tool.definition',
|
|
70
|
+
]),
|
|
71
|
+
openclaw: new Set([
|
|
72
|
+
// Plugin hooks
|
|
73
|
+
'before_model_resolve',
|
|
74
|
+
'before_prompt_build',
|
|
75
|
+
'before_agent_start',
|
|
76
|
+
'llm_input',
|
|
77
|
+
'llm_output',
|
|
78
|
+
'agent_end',
|
|
79
|
+
'before_compaction',
|
|
80
|
+
'after_compaction',
|
|
81
|
+
'before_reset',
|
|
82
|
+
'message_received',
|
|
83
|
+
'message_sending',
|
|
84
|
+
'message_sent',
|
|
85
|
+
'before_tool_call',
|
|
86
|
+
'after_tool_call',
|
|
87
|
+
'tool_result_persist',
|
|
88
|
+
'before_message_write',
|
|
89
|
+
'session_start',
|
|
90
|
+
'session_end',
|
|
91
|
+
'subagent_spawning',
|
|
92
|
+
'subagent_delivery_target',
|
|
93
|
+
'subagent_spawned',
|
|
94
|
+
'subagent_ended',
|
|
95
|
+
'gateway_start',
|
|
96
|
+
'gateway_stop',
|
|
97
|
+
// Internal hooks
|
|
98
|
+
'command:new',
|
|
99
|
+
'command:reset',
|
|
100
|
+
'command:stop',
|
|
101
|
+
'session:compact:before',
|
|
102
|
+
'session:compact:after',
|
|
103
|
+
'agent:bootstrap',
|
|
104
|
+
'gateway:startup',
|
|
105
|
+
'message:received',
|
|
106
|
+
'message:sent',
|
|
107
|
+
'message:transcribed',
|
|
108
|
+
'message:preprocessed',
|
|
109
|
+
]),
|
|
110
|
+
};
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// defineHooks — pure validation + packaging function
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* Define hooks for a specific agent. Returns a validated HookSet that can be
|
|
116
|
+
* passed to `kit.installHooks()`.
|
|
117
|
+
*
|
|
118
|
+
* Pure function — no side effects, no global state, no instance dependency.
|
|
119
|
+
*
|
|
120
|
+
* @param agent - Target agent type.
|
|
121
|
+
* @param definitions - A single HookDefinition or an array of HookDefinitions.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* // Single definition
|
|
126
|
+
* const hooks = defineHooks('claude-code', {
|
|
127
|
+
* events: ['PreToolUse', 'PostToolUse'],
|
|
128
|
+
* content: '#!/bin/bash\necho "hook fired"',
|
|
129
|
+
* });
|
|
130
|
+
*
|
|
131
|
+
* // Multiple definitions
|
|
132
|
+
* const hooks = defineHooks('claude-code', [
|
|
133
|
+
* { events: ['PreToolUse'], content: '#!/bin/bash\necho "pre"' },
|
|
134
|
+
* { events: ['PostToolUse'], content: '#!/bin/bash\necho "post"' },
|
|
135
|
+
* ]);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export function defineHooks(agent, definitions) {
|
|
139
|
+
// Validate agent
|
|
140
|
+
if (!AGENT_TYPES.includes(agent)) {
|
|
141
|
+
throw new Error(`defineHooks: unknown agent type "${agent}". Valid types: ${AGENT_TYPES.join(', ')}`);
|
|
142
|
+
}
|
|
143
|
+
// Normalize to array
|
|
144
|
+
const defs = Array.isArray(definitions) ? definitions : [definitions];
|
|
145
|
+
if (defs.length === 0) {
|
|
146
|
+
throw new Error('defineHooks: definitions array cannot be empty.');
|
|
147
|
+
}
|
|
148
|
+
// OpenClaw: warn if multiple definitions provided (only first is used)
|
|
149
|
+
const warnings = [];
|
|
150
|
+
if (agent === 'openclaw' && defs.length > 1) {
|
|
151
|
+
warnings.push(`defineHooks: OpenClaw only supports a single hook definition. ` +
|
|
152
|
+
`Got ${defs.length} definitions — only the first will be used.`);
|
|
153
|
+
// Log warning (user can also see it in installHooks result)
|
|
154
|
+
console.warn(warnings[0]);
|
|
155
|
+
}
|
|
156
|
+
const validEvents = VALID_EVENTS[agent];
|
|
157
|
+
for (let i = 0; i < defs.length; i++) {
|
|
158
|
+
const def = defs[i];
|
|
159
|
+
// Validate events
|
|
160
|
+
if (!Array.isArray(def.events) || def.events.length === 0) {
|
|
161
|
+
throw new Error(`defineHooks: definitions[${i}].events must be a non-empty array.`);
|
|
162
|
+
}
|
|
163
|
+
for (const event of def.events) {
|
|
164
|
+
if (!validEvents.has(event)) {
|
|
165
|
+
throw new Error(`defineHooks: unknown event "${event}" for agent "${agent}". ` +
|
|
166
|
+
`Valid events: ${[...validEvents].join(', ')}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Validate content
|
|
170
|
+
if (typeof def.content !== 'string' || !def.content.trim()) {
|
|
171
|
+
throw new Error(`defineHooks: definitions[${i}].content must be a non-empty string.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// For OpenClaw, only keep first definition
|
|
175
|
+
const effectiveDefs = agent === 'openclaw' && defs.length > 1 ? [defs[0]] : defs;
|
|
176
|
+
return {
|
|
177
|
+
__brand: 'HookSet',
|
|
178
|
+
agent,
|
|
179
|
+
definitions: Object.freeze([...effectiveDefs]),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get the set of valid event names for a given agent.
|
|
184
|
+
* Useful for tooling and documentation generation.
|
|
185
|
+
*/
|
|
186
|
+
export function getValidEvents(agent) {
|
|
187
|
+
if (!AGENT_TYPES.includes(agent)) {
|
|
188
|
+
throw new Error(`getValidEvents: unknown agent type "${agent}".`);
|
|
189
|
+
}
|
|
190
|
+
return VALID_EVENTS[agent];
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=define-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-hooks.js","sourceRoot":"","sources":["../src/define-hooks.ts"],"names":[],"mappings":"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"}
|
package/build/hooks.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import type { AgentType, HookInstallResult } from './types.js';
|
|
1
|
+
import type { AgentType, HookInstallResult, HookSet } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Install hooks for the given agent type.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Accepts one or more HookSet (from defineHooks()). Filters to only those
|
|
6
|
+
* matching the target agent. Writes content to the agent's hook directory.
|
|
7
|
+
*
|
|
8
|
+
* For Claude Code / Codex: writes shell scripts, merges into settings.json.
|
|
9
|
+
* For OpenCode: writes TypeScript plugin files directly to plugins dir.
|
|
10
|
+
* For OpenClaw: writes HOOK.md + handler.ts, attempts CLI activation.
|
|
8
11
|
*
|
|
9
12
|
* @internal — called by the Kit object returned from createKit().
|
|
10
13
|
*/
|
|
11
|
-
export declare function installHooks(name: string, agent: AgentType): Promise<HookInstallResult>;
|
|
14
|
+
export declare function installHooks(name: string, agent: AgentType, hookSets: HookSet | HookSet[]): Promise<HookInstallResult>;
|
|
12
15
|
/**
|
|
13
16
|
* Uninstall hooks for the given agent type.
|
|
14
17
|
*
|