@mauribadnights/clooks 0.5.1 → 0.5.3

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.
@@ -0,0 +1,122 @@
1
+ # HTTP API
2
+
3
+ clooks runs an HTTP server on localhost (default port 7890). Claude Code sends hook events as HTTP POST requests. The API also exposes health endpoints for monitoring and orchestration.
4
+
5
+ ---
6
+
7
+ ## Authentication
8
+
9
+ If `settings.authToken` is configured in `manifest.yaml`, all requests except `GET /health` must include the token:
10
+
11
+ ```
12
+ Authorization: Bearer <token>
13
+ ```
14
+
15
+ Failed authentication returns `401 Unauthorized`.
16
+
17
+ Rate limiting applies to authentication failures: after 10 failures within 60 seconds from the same source IP, the server returns `429 Too Many Requests` with a `Retry-After` header.
18
+
19
+ ---
20
+
21
+ ## Endpoints
22
+
23
+ ### GET /health
24
+
25
+ Public health check. No authentication required.
26
+
27
+ **Response:**
28
+
29
+ ```json
30
+ {
31
+ "status": "ok",
32
+ "pid": 12345
33
+ }
34
+ ```
35
+
36
+ ### GET /health/detail
37
+
38
+ Authenticated detailed health check.
39
+
40
+ **Response:**
41
+
42
+ ```json
43
+ {
44
+ "status": "ok",
45
+ "pid": 12345,
46
+ "uptime": 3600,
47
+ "handlers": 8,
48
+ "port": 7890
49
+ }
50
+ ```
51
+
52
+ ### POST /hooks/:eventName
53
+
54
+ Main hook dispatch endpoint. Accepts a HookInput JSON body, executes all matching handlers for the event, and returns the merged result.
55
+
56
+ The `:eventName` parameter must be one of the 9 valid hook events (see [Hook Events](hook-events.md)).
57
+
58
+ **Request:**
59
+
60
+ ```bash
61
+ curl -X POST http://localhost:7890/hooks/PreToolUse \
62
+ -H "Authorization: Bearer your-token" \
63
+ -H "Content-Type: application/json" \
64
+ -d '{
65
+ "session_id": "abc123",
66
+ "hook_event_name": "PreToolUse",
67
+ "tool_name": "Bash",
68
+ "tool_input": { "command": "ls -la" },
69
+ "cwd": "/home/user/project",
70
+ "transcript_path": "/tmp/transcript.jsonl",
71
+ "permission_mode": "default"
72
+ }'
73
+ ```
74
+
75
+ **Response:**
76
+
77
+ ```json
78
+ {
79
+ "additionalContext": "Handler 1 output\nHandler 2 output",
80
+ "decision": "allow",
81
+ "reason": "All checks passed"
82
+ }
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Response Merging
88
+
89
+ When multiple handlers return results for the same event, fields are merged as follows:
90
+
91
+ | Field | Strategy |
92
+ |-------|----------|
93
+ | `additionalContext` | Concatenated with newlines |
94
+ | `decision` | Last writer wins (latest handler in execution order) |
95
+ | `reason` | Last writer wins |
96
+ | `hookSpecificOutput` | Last writer wins |
97
+
98
+ > **Note:** Handler execution order is determined by the order in `manifest.yaml`, modified by any `depends` declarations. See [Types](types.md) for the `HandlerConfig.depends` field.
99
+
100
+ ---
101
+
102
+ ## Error Responses
103
+
104
+ | Status | Condition |
105
+ |--------|-----------|
106
+ | `400` | Invalid event name or malformed JSON body |
107
+ | `401` | Missing or invalid auth token |
108
+ | `404` | Unknown route |
109
+ | `429` | Rate limited (too many authentication failures) |
110
+ | `500` | Internal server error |
111
+
112
+ All error responses return a JSON body:
113
+
114
+ ```json
115
+ {
116
+ "error": "Description of the problem"
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ [Home](../index.md) | [Prev: Hook Events](hook-events.md) | [Next: Config Files](config-files.md)
@@ -0,0 +1,410 @@
1
+ # Types
2
+
3
+ TypeScript type reference for programmatic usage. All types are exported from the `@mauribadnights/clooks` package.
4
+
5
+ ```typescript
6
+ import type { Manifest, HandlerConfig, HookInput, HookEvent } from '@mauribadnights/clooks';
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Core Types
12
+
13
+ ### HookEvent
14
+
15
+ ```typescript
16
+ type HookEvent =
17
+ | 'SessionStart'
18
+ | 'UserPromptSubmit'
19
+ | 'PreToolUse'
20
+ | 'PostToolUse'
21
+ | 'Stop'
22
+ | 'SubagentStart'
23
+ | 'SubagentStop'
24
+ | 'Notification'
25
+ | 'ConfigChange';
26
+ ```
27
+
28
+ ### HookInput
29
+
30
+ ```typescript
31
+ interface HookInput {
32
+ session_id: string;
33
+ transcript_path: string;
34
+ cwd: string;
35
+ permission_mode: string;
36
+ hook_event_name: HookEvent;
37
+
38
+ // UserPromptSubmit
39
+ prompt?: string;
40
+
41
+ // Pre/PostToolUse
42
+ tool_name?: string;
43
+ tool_input?: Record<string, unknown>;
44
+
45
+ // SessionStart
46
+ source?: string;
47
+ agent_type?: string;
48
+
49
+ // Stop
50
+ stop_hook_active?: boolean;
51
+
52
+ // Internal: dependency outputs injected by the engine
53
+ _handlerOutputs?: Record<string, unknown>;
54
+
55
+ // Extensible for plugins
56
+ [key: string]: unknown;
57
+ }
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Handler Types
63
+
64
+ ### HandlerConfig
65
+
66
+ A union of three handler types, all sharing a common base.
67
+
68
+ ```typescript
69
+ interface BaseHandler {
70
+ id: string;
71
+ filter?: string;
72
+ timeout?: number;
73
+ enabled?: boolean;
74
+ sessionIsolation?: boolean;
75
+ depends?: string[];
76
+ async?: boolean;
77
+ agent?: string;
78
+ project?: string;
79
+ }
80
+
81
+ interface ScriptHandlerConfig extends BaseHandler {
82
+ type: 'script';
83
+ command: string;
84
+ }
85
+
86
+ interface InlineHandlerConfig extends BaseHandler {
87
+ type: 'inline';
88
+ module: string;
89
+ }
90
+
91
+ interface LLMHandlerConfig extends BaseHandler {
92
+ type: 'llm';
93
+ model: LLMModel;
94
+ prompt: string;
95
+ batchGroup?: string;
96
+ maxTokens?: number; // Default: 1024
97
+ temperature?: number; // Default: 1.0
98
+ }
99
+
100
+ type HandlerConfig = ScriptHandlerConfig | InlineHandlerConfig | LLMHandlerConfig;
101
+ ```
102
+
103
+ ### LLMModel
104
+
105
+ ```typescript
106
+ type LLMModel = 'claude-haiku-4-5' | 'claude-sonnet-4-6' | 'claude-opus-4-6';
107
+ ```
108
+
109
+ ### HandlerResult
110
+
111
+ ```typescript
112
+ interface HandlerResult {
113
+ id: string;
114
+ ok: boolean;
115
+ output?: unknown;
116
+ error?: string;
117
+ duration_ms: number;
118
+ filtered?: boolean;
119
+ usage?: TokenUsage;
120
+ cost_usd?: number;
121
+ }
122
+ ```
123
+
124
+ ### HandlerState
125
+
126
+ ```typescript
127
+ interface HandlerState {
128
+ consecutiveFailures: number;
129
+ disabled: boolean;
130
+ totalFires: number;
131
+ totalErrors: number;
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Manifest Types
138
+
139
+ ### Manifest
140
+
141
+ ```typescript
142
+ interface Manifest {
143
+ handlers: Partial<Record<HookEvent, HandlerConfig[]>>;
144
+ prefetch?: PrefetchKey[];
145
+ settings?: {
146
+ port?: number;
147
+ logLevel?: 'debug' | 'info' | 'warn' | 'error';
148
+ anthropicApiKey?: string;
149
+ authToken?: string;
150
+ };
151
+ }
152
+ ```
153
+
154
+ ### PluginManifest
155
+
156
+ ```typescript
157
+ interface PluginManifest {
158
+ name: string;
159
+ version: string;
160
+ description?: string;
161
+ author?: string;
162
+ handlers: Partial<Record<HookEvent, HandlerConfig[]>>;
163
+ prefetch?: PrefetchKey[];
164
+ extras?: PluginExtras;
165
+ }
166
+
167
+ interface PluginExtras {
168
+ skills?: string[];
169
+ agents?: string[];
170
+ readme?: string;
171
+ [key: string]: unknown;
172
+ }
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Plugin Types
178
+
179
+ ### InstalledPlugin
180
+
181
+ ```typescript
182
+ interface InstalledPlugin {
183
+ name: string;
184
+ version: string;
185
+ path: string;
186
+ installedAt: string;
187
+ }
188
+ ```
189
+
190
+ ### PluginRegistry
191
+
192
+ ```typescript
193
+ interface PluginRegistry {
194
+ plugins: InstalledPlugin[];
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Metrics Types
201
+
202
+ ### MetricEntry
203
+
204
+ ```typescript
205
+ interface MetricEntry {
206
+ ts: string;
207
+ event: HookEvent;
208
+ handler: string;
209
+ duration_ms: number;
210
+ ok: boolean;
211
+ error?: string;
212
+ filtered?: boolean;
213
+ usage?: TokenUsage;
214
+ cost_usd?: number;
215
+ session_id?: string;
216
+ agent_type?: string;
217
+ }
218
+ ```
219
+
220
+ ### CostEntry
221
+
222
+ ```typescript
223
+ interface CostEntry {
224
+ ts: string;
225
+ event: HookEvent;
226
+ handler: string;
227
+ model: LLMModel;
228
+ usage: TokenUsage;
229
+ cost_usd: number;
230
+ batched: boolean;
231
+ }
232
+ ```
233
+
234
+ ### TokenUsage
235
+
236
+ ```typescript
237
+ interface TokenUsage {
238
+ input_tokens: number;
239
+ output_tokens: number;
240
+ }
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Context Types
246
+
247
+ ### PrefetchKey
248
+
249
+ ```typescript
250
+ type PrefetchKey = 'transcript' | 'git_status' | 'git_diff';
251
+ ```
252
+
253
+ ### PrefetchContext
254
+
255
+ ```typescript
256
+ interface PrefetchContext {
257
+ transcript?: string;
258
+ git_status?: string;
259
+ git_diff?: string;
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Diagnostic Types
266
+
267
+ ### DiagnosticResult
268
+
269
+ ```typescript
270
+ interface DiagnosticResult {
271
+ check: string;
272
+ status: 'ok' | 'warn' | 'error';
273
+ message: string;
274
+ }
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Exported Functions
280
+
281
+ ### Server
282
+
283
+ ```typescript
284
+ createServer(manifest: Manifest, metrics: MetricsCollector): ServerContext;
285
+ startDaemon(manifest: Manifest, metrics: MetricsCollector, options?: object): Promise<ServerContext>;
286
+ stopDaemon(): boolean;
287
+ isDaemonRunning(): boolean;
288
+ ```
289
+
290
+ ### Manifest
291
+
292
+ ```typescript
293
+ loadManifest(): Promise<Manifest>;
294
+ loadCompositeManifest(): Promise<Manifest>;
295
+ validateManifest(manifest: Manifest): void;
296
+ createDefaultManifest(authToken?: string): Promise<string>;
297
+ ```
298
+
299
+ ### Plugins
300
+
301
+ ```typescript
302
+ loadPlugins(pluginsDir?: string, registryPath?: string): Promise<Array>;
303
+ mergeManifests(userManifest: Manifest, plugins: Array): Manifest;
304
+ validatePluginManifest(manifest: PluginManifest): void;
305
+ installPlugin(sourcePath: string, pluginsDir?: string, registryPath?: string): Promise<InstalledPlugin>;
306
+ uninstallPlugin(name: string, pluginsDir?: string, registryPath?: string): Promise<void>;
307
+ listPlugins(registryPath?: string): Promise<InstalledPlugin[]>;
308
+ ```
309
+
310
+ ### Handlers
311
+
312
+ ```typescript
313
+ executeHandlers(
314
+ event: HookEvent,
315
+ input: HookInput,
316
+ handlers: HandlerConfig[],
317
+ context?: PrefetchContext,
318
+ onAsyncResult?: (result: HandlerResult) => void,
319
+ currentAgent?: string
320
+ ): Promise<HandlerResult[]>;
321
+
322
+ resolveExecutionOrder(handlers: HandlerConfig[]): HandlerConfig[][];
323
+ resetSessionIsolatedHandlers(handlers: Partial<Record<HookEvent, HandlerConfig[]>>): void;
324
+ cleanupHandlerState(id: string): void;
325
+ ```
326
+
327
+ ### Filtering
328
+
329
+ ```typescript
330
+ evaluateFilter(filter: string, input: HookInput): boolean;
331
+ ```
332
+
333
+ ### LLM
334
+
335
+ ```typescript
336
+ executeLLMHandler(handler: LLMHandlerConfig, input: HookInput, context: PrefetchContext): Promise<HandlerResult>;
337
+ executeLLMHandlersBatched(
338
+ handlers: LLMHandlerConfig[],
339
+ input: HookInput,
340
+ context: PrefetchContext,
341
+ sessionId?: string
342
+ ): Promise<HandlerResult[]>;
343
+ calculateCost(model: LLMModel, usage: TokenUsage): number;
344
+ ```
345
+
346
+ ### Context
347
+
348
+ ```typescript
349
+ prefetchContext(keys: PrefetchKey[], input: HookInput): Promise<PrefetchContext>;
350
+ renderPromptTemplate(template: string, input: HookInput, context: PrefetchContext): string;
351
+ ```
352
+
353
+ ### Auth
354
+
355
+ ```typescript
356
+ generateAuthToken(): string;
357
+ validateAuth(authHeader: string, expectedToken: string): boolean;
358
+ rotateToken(options?: object): Promise<string>;
359
+ ```
360
+
361
+ ### Metrics
362
+
363
+ ```typescript
364
+ class MetricsCollector {
365
+ record(entry: MetricEntry): void;
366
+ // ...
367
+ }
368
+ ```
369
+
370
+ ### Rate Limiting
371
+
372
+ ```typescript
373
+ class RateLimiter { /* ... */ }
374
+ class DenyCache { /* ... */ }
375
+ ```
376
+
377
+ ### Service
378
+
379
+ ```typescript
380
+ installService(): Promise<void>;
381
+ uninstallService(): Promise<void>;
382
+ isServiceInstalled(): Promise<boolean>;
383
+ getServiceStatus(): Promise<string>;
384
+ ```
385
+
386
+ ### Migration and Diagnostics
387
+
388
+ ```typescript
389
+ migrate(): Promise<void>;
390
+ restore(): Promise<void>;
391
+ syncSettings(): Promise<void>;
392
+ runDoctor(): Promise<DiagnosticResult[]>;
393
+ ```
394
+
395
+ ### File Watching
396
+
397
+ ```typescript
398
+ startWatcher(path: string, onReload: () => void, onError?: (err: Error) => void): FSWatcher | null;
399
+ stopWatcher(watcher: FSWatcher): void;
400
+ ```
401
+
402
+ ### Agent
403
+
404
+ ```typescript
405
+ installAgent(): Promise<void>;
406
+ ```
407
+
408
+ ---
409
+
410
+ [Home](../index.md) | [Prev: Config Files](config-files.md) | [Next: Monitoring](../operations/monitoring.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mauribadnights/clooks",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Persistent hook runtime for Claude Code — eliminates process spawning overhead and gives you observability",
5
5
  "bin": {
6
6
  "clooks": "./dist/cli.js"
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "license": "MIT",
18
18
  "author": "mauribadnights",
19
+ "homepage": "https://mauribadnights.github.io/clooks/",
19
20
  "repository": {
20
21
  "type": "git",
21
22
  "url": "https://github.com/mauribadnights/clooks.git"