@mauribadnights/clooks 0.4.0 → 0.4.1
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 +276 -201
- package/dist/cli.js +1 -1
- package/dist/server.js +1 -0
- package/dist/tui.js +541 -371
- package/dist/types.d.ts +1 -0
- package/docs/DASHBOARD-VISION.md +66 -0
- package/docs/DEFERRED-FIXES.md +35 -0
- package/docs/architecture.png +0 -0
- package/docs/generate-diagram.py +177 -0
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# clooks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Persistent hook runtime for Claude Code. Eliminate cold starts. Get observability.
|
|
4
4
|
|
|
5
|
-
[](
|
|
5
|
+
[](https://www.npmjs.com/package/@mauribadnights/clooks)
|
|
6
|
+
[](LICENSE)
|
|
7
7
|
|
|
8
8
|
## Performance
|
|
9
9
|
|
|
@@ -15,172 +15,206 @@
|
|
|
15
15
|
|
|
16
16
|
> Benchmarked on Apple Silicon (M-series), Node v24.4.1. Run `npm run bench` to reproduce.
|
|
17
17
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
Claude Code spawns a fresh process for every hook invocation. Each Node.js cold start costs 30-40ms. Power users with multiple hooks accumulate 100+ process spawns per session -- that is 4-6 seconds of pure overhead, with zero visibility into what your hooks are doing or how they fail.
|
|
21
|
-
|
|
22
|
-
## Quick Start
|
|
18
|
+
## Installation
|
|
23
19
|
|
|
24
20
|
```bash
|
|
25
21
|
npm install -g @mauribadnights/clooks
|
|
26
|
-
|
|
27
|
-
# Option A: Migrate existing hooks automatically
|
|
28
|
-
clooks migrate # converts command hooks to HTTP hooks + manifest
|
|
29
|
-
clooks start # starts the daemon
|
|
30
|
-
|
|
31
|
-
# Option B: Start fresh
|
|
32
|
-
clooks init # creates ~/.clooks/manifest.yaml
|
|
22
|
+
clooks migrate # migrates existing hooks, installs system service, installs clooks agent
|
|
33
23
|
clooks start
|
|
34
24
|
```
|
|
35
25
|
|
|
36
|
-
|
|
26
|
+
`clooks migrate` converts your `settings.json` command hooks into HTTP hooks backed by the daemon, auto-installs a system service (launchd/systemd) for auto-start and crash recovery, and installs the `clooks` expert agent (`claude --agent clooks`). Starting fresh instead? Use `clooks init` to create a blank manifest.
|
|
37
27
|
|
|
38
28
|
## How It Works
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
Claude Code clooks daemon (localhost:7890)
|
|
42
|
-
| |
|
|
43
|
-
|-- SessionStart ------> POST /hooks/SessionStart ------> [handler1, handler2]
|
|
44
|
-
|-- UserPromptSubmit --> POST /hooks/UserPromptSubmit --> [handler3]
|
|
45
|
-
|-- PreToolUse (x50) --> POST /hooks/PreToolUse --------> [handler4, handler5]
|
|
46
|
-
|-- Stop --------------> POST /hooks/Stop ---------------> [handler6]
|
|
47
|
-
| |
|
|
48
|
-
|<-------------- JSON responses ---------|
|
|
49
|
-
```
|
|
30
|
+

|
|
50
31
|
|
|
51
|
-
One persistent process.
|
|
32
|
+
One persistent HTTP server replaces per-invocation process spawning. Claude Code POSTs hook events to the daemon, which dispatches to handlers defined in `~/.clooks/manifest.yaml`. Handlers that fail 3 times consecutively are auto-disabled.
|
|
52
33
|
|
|
53
|
-
|
|
34
|
+
## Quick Reference -- Commands
|
|
54
35
|
|
|
55
|
-
|
|
36
|
+
**Daemon lifecycle:**
|
|
56
37
|
|
|
57
38
|
| Command | Description |
|
|
58
39
|
|---------|-------------|
|
|
59
|
-
| `clooks start` | Start the daemon (
|
|
40
|
+
| `clooks start` | Start the daemon (`-f` foreground, `--no-watch` disable manifest watching) |
|
|
60
41
|
| `clooks stop` | Stop the daemon |
|
|
61
|
-
| `clooks status` | Show daemon status, uptime,
|
|
62
|
-
| `clooks
|
|
63
|
-
|
|
64
|
-
|
|
42
|
+
| `clooks status` | Show daemon status, uptime, handler count, service state |
|
|
43
|
+
| `clooks ensure-running` | Start daemon if not running (used internally by SessionStart hook) |
|
|
44
|
+
|
|
45
|
+
**Observability:**
|
|
46
|
+
|
|
47
|
+
| Command | Description |
|
|
48
|
+
|---------|-------------|
|
|
49
|
+
| `clooks stats` | Interactive TUI for execution metrics (`-t` for plain text) |
|
|
50
|
+
| `clooks costs` | LLM token usage and cost breakdown |
|
|
65
51
|
| `clooks doctor` | Run diagnostic health checks |
|
|
52
|
+
|
|
53
|
+
**Configuration:**
|
|
54
|
+
|
|
55
|
+
| Command | Description |
|
|
56
|
+
|---------|-------------|
|
|
57
|
+
| `clooks migrate` | Convert `settings.json` command hooks to HTTP hooks, install service + agent |
|
|
58
|
+
| `clooks restore` | Restore original `settings.json` from backup |
|
|
59
|
+
| `clooks sync` | Sync `settings.json` with manifest (add missing HTTP hook entries) |
|
|
66
60
|
| `clooks init` | Create default config directory and example manifest |
|
|
67
|
-
| `clooks
|
|
61
|
+
| `clooks update` | Update clooks to latest version and refresh agent |
|
|
62
|
+
| `clooks rotate-token` | Generate new auth token, update manifest + settings.json, hot-reload daemon |
|
|
63
|
+
|
|
64
|
+
**Plugins:**
|
|
65
|
+
|
|
66
|
+
| Command | Description |
|
|
67
|
+
|---------|-------------|
|
|
68
68
|
| `clooks add <path>` | Install a plugin from a local directory |
|
|
69
69
|
| `clooks remove <name>` | Uninstall a plugin and its contributed handlers |
|
|
70
70
|
| `clooks plugins` | List installed plugins and their handlers |
|
|
71
|
-
| `clooks rotate-token` | Generate a new auth token, update manifest + settings.json, hot-reload daemon |
|
|
72
|
-
| `clooks costs` | Show LLM token usage and cost breakdown |
|
|
73
71
|
|
|
74
|
-
|
|
72
|
+
**System service:**
|
|
73
|
+
|
|
74
|
+
| Command | Description |
|
|
75
|
+
|---------|-------------|
|
|
76
|
+
| `clooks service install` | Install as system service (auto-start on login, auto-restart on crash) |
|
|
77
|
+
| `clooks service uninstall` | Remove system service |
|
|
78
|
+
| `clooks service status` | Show service status |
|
|
79
|
+
|
|
80
|
+
## Configuration -- Manifest
|
|
75
81
|
|
|
76
82
|
Handlers are defined in `~/.clooks/manifest.yaml`:
|
|
77
83
|
|
|
78
84
|
```yaml
|
|
85
|
+
# Pre-fetch shared context once per event, available as $VARIABLES in LLM prompts
|
|
86
|
+
prefetch:
|
|
87
|
+
- transcript # last 50KB of session transcript
|
|
88
|
+
- git_status # git status --porcelain
|
|
89
|
+
- git_diff # git diff --stat (max 20KB)
|
|
90
|
+
|
|
79
91
|
handlers:
|
|
80
92
|
PreToolUse:
|
|
93
|
+
# Script handler -- spawns a shell command
|
|
81
94
|
- id: safety-guard
|
|
82
|
-
type: script
|
|
95
|
+
type: script
|
|
83
96
|
command: node ~/hooks/guard.js
|
|
97
|
+
filter: "Bash|Execute|!Read" # OR logic, ! negates
|
|
98
|
+
project: "*/my-project/*" # only fire in matching cwd
|
|
84
99
|
timeout: 3000
|
|
85
100
|
enabled: true
|
|
86
101
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
# LLM handler -- calls Anthropic Messages API
|
|
103
|
+
- id: code-review
|
|
104
|
+
type: llm
|
|
105
|
+
model: claude-haiku-4-5
|
|
106
|
+
prompt: "Review this $TOOL_NAME call: $ARGUMENTS"
|
|
107
|
+
batchGroup: analysis # batched with other handlers in same group
|
|
108
|
+
maxTokens: 512
|
|
109
|
+
temperature: 0.5
|
|
110
|
+
depends: [safety-guard] # waits for safety-guard to complete first
|
|
111
|
+
|
|
112
|
+
# Another LLM handler in the same batch group -- one API call for both
|
|
113
|
+
- id: security-check
|
|
114
|
+
type: llm
|
|
115
|
+
model: claude-haiku-4-5
|
|
116
|
+
prompt: "Check for security issues in $TOOL_NAME: $ARGUMENTS"
|
|
117
|
+
batchGroup: analysis
|
|
118
|
+
agent: "builder" # only fire in builder agent sessions
|
|
119
|
+
|
|
120
|
+
UserPromptSubmit:
|
|
121
|
+
# Inline handler -- imports a JS module in-process (no subprocess)
|
|
122
|
+
- id: prompt-logger
|
|
123
|
+
type: inline
|
|
124
|
+
module: ~/.clooks/handlers/logger.js
|
|
125
|
+
async: true # fire-and-forget, doesn't block response
|
|
126
|
+
sessionIsolation: true # reset state on SessionStart
|
|
91
127
|
|
|
92
128
|
Stop:
|
|
93
|
-
- id: session-
|
|
94
|
-
type:
|
|
95
|
-
|
|
129
|
+
- id: session-summary
|
|
130
|
+
type: llm
|
|
131
|
+
model: claude-haiku-4-5
|
|
132
|
+
prompt: "Summarize this session:\n$TRANSCRIPT\n\nGit changes:\n$GIT_DIFF"
|
|
96
133
|
|
|
97
134
|
settings:
|
|
98
135
|
port: 7890
|
|
99
|
-
logLevel: info
|
|
136
|
+
logLevel: info # debug | info | warn | error
|
|
137
|
+
authToken: your-token-here # auto-generated by migrate/init
|
|
138
|
+
# anthropicApiKey: sk-... # or set ANTHROPIC_API_KEY env var
|
|
100
139
|
```
|
|
101
140
|
|
|
102
|
-
|
|
103
|
-
- `script` -- runs a shell command, pipes hook JSON to stdin, reads JSON from stdout.
|
|
104
|
-
- `inline` -- imports a JS module and calls its default export. Faster; no subprocess overhead.
|
|
105
|
-
- `llm` -- calls Anthropic Messages API. Supports prompt templates, batching, and cost tracking. *(v0.2+)*
|
|
141
|
+
## Handler Types
|
|
106
142
|
|
|
107
|
-
|
|
143
|
+
| Type | Overhead | Language | Use case |
|
|
144
|
+
|------|----------|----------|----------|
|
|
145
|
+
| `script` | ~5-35ms (subprocess) | Any (shell command) | Existing scripts, non-JS tools |
|
|
146
|
+
| `inline` | <1ms (in-process) | JavaScript/TypeScript | Performance-critical handlers |
|
|
147
|
+
| `llm` | Network-bound | Prompt template | AI-powered analysis, review, summarization |
|
|
108
148
|
|
|
109
|
-
|
|
149
|
+
**script** -- runs `sh -c "command"`, pipes hook JSON to stdin, reads JSON from stdout.
|
|
110
150
|
|
|
111
|
-
|
|
112
|
-
$ clooks stats
|
|
151
|
+
**inline** -- imports a JS module and calls its default export. No subprocess overhead.
|
|
113
152
|
|
|
114
|
-
|
|
115
|
-
------------------------------------------------------------------------
|
|
116
|
-
PreToolUse 47 0 1.2 0.8 3.1
|
|
117
|
-
Stop 12 0 2.4 1.1 5.6
|
|
118
|
-
UserPromptSubmit 12 1 1.8 0.9 4.2
|
|
153
|
+
**llm** -- calls Anthropic Messages API with `$VARIABLE` interpolation. Supports batching and cost tracking.
|
|
119
154
|
|
|
120
|
-
|
|
121
|
-
```
|
|
155
|
+
## Handler Fields Reference
|
|
122
156
|
|
|
123
|
-
|
|
157
|
+
| Field | Type | Default | Applies to | Description |
|
|
158
|
+
|-------|------|---------|------------|-------------|
|
|
159
|
+
| `id` | string | required | all | Unique handler identifier |
|
|
160
|
+
| `type` | string | required | all | `script`, `inline`, or `llm` |
|
|
161
|
+
| `command` | string | required | script | Shell command to execute |
|
|
162
|
+
| `module` | string | required | inline | Path to JS module with default export |
|
|
163
|
+
| `model` | string | required | llm | `claude-haiku-4-5`, `claude-sonnet-4-6`, or `claude-opus-4-6` |
|
|
164
|
+
| `prompt` | string | required | llm | Prompt template with `$VARIABLE` interpolation |
|
|
165
|
+
| `filter` | string | -- | all | Keyword filter (see Filtering) |
|
|
166
|
+
| `project` | string | -- | all | Glob pattern matched against cwd |
|
|
167
|
+
| `agent` | string | -- | all | Only fire when session agent matches |
|
|
168
|
+
| `async` | boolean | `false` | all | Fire-and-forget, don't block response |
|
|
169
|
+
| `depends` | string[] | -- | all | Handler IDs to wait for before executing |
|
|
170
|
+
| `sessionIsolation` | boolean | `false` | all | Reset handler state on SessionStart |
|
|
171
|
+
| `batchGroup` | string | -- | llm | Group ID for batching into one API call |
|
|
172
|
+
| `maxTokens` | number | `1024` | llm | Maximum output tokens |
|
|
173
|
+
| `temperature` | number | `1.0` | llm | Sampling temperature |
|
|
174
|
+
| `timeout` | number | `5000`/`30000` | all | Timeout in ms (5s default, 30s for llm) |
|
|
175
|
+
| `enabled` | boolean | `true` | all | Disable without removing |
|
|
124
176
|
|
|
125
|
-
|
|
126
|
-
$ clooks doctor
|
|
177
|
+
## Scoped Execution
|
|
127
178
|
|
|
128
|
-
|
|
129
|
-
[pass] Port 7890 is responding
|
|
130
|
-
[pass] Manifest loaded: 4 handlers across 3 events
|
|
131
|
-
[pass] settings.json has HTTP hooks pointing to clooks
|
|
132
|
-
[pass] No handlers in circuit-breaker state
|
|
133
|
-
[warn] 1 handler error in last 24h (session-logger on Stop)
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Comparison
|
|
179
|
+
Handlers can be scoped to specific projects or agents:
|
|
137
180
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
181
|
+
```yaml
|
|
182
|
+
- id: driffusion-lint
|
|
183
|
+
type: script
|
|
184
|
+
command: node ~/hooks/lint.js
|
|
185
|
+
project: "*/Driffusion/*" # only fires when cwd matches this glob
|
|
186
|
+
|
|
187
|
+
- id: builder-guard
|
|
188
|
+
type: inline
|
|
189
|
+
module: ~/hooks/guard.js
|
|
190
|
+
agent: "builder" # only fires in builder agent sessions
|
|
191
|
+
```
|
|
147
192
|
|
|
148
|
-
|
|
149
|
-
|--------|---------|-------------|
|
|
150
|
-
| Port | `7890` | HTTP server port |
|
|
151
|
-
| Config directory | `~/.clooks/` | Root configuration directory |
|
|
152
|
-
| Manifest | `~/.clooks/manifest.yaml` | Handler definitions |
|
|
153
|
-
| Metrics | `~/.clooks/metrics.jsonl` | Execution metrics log |
|
|
154
|
-
| Daemon log | `~/.clooks/daemon.log` | Server output log |
|
|
155
|
-
| PID file | `~/.clooks/daemon.pid` | Process ID file |
|
|
193
|
+
Both fields are optional. When omitted, the handler fires for all projects/agents.
|
|
156
194
|
|
|
157
|
-
##
|
|
195
|
+
## Filtering
|
|
158
196
|
|
|
159
|
-
|
|
197
|
+
The `filter` field skips handlers based on keywords matched against the full JSON-serialized hook input (case-insensitive):
|
|
160
198
|
|
|
161
|
-
|
|
199
|
+
```
|
|
200
|
+
filter: "word1|word2" # run if input contains word1 OR word2
|
|
201
|
+
filter: "!word" # run unless input contains word
|
|
202
|
+
filter: "word1|!word2" # run if word1 present AND word2 absent
|
|
203
|
+
```
|
|
162
204
|
|
|
163
205
|
```yaml
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
model: claude-haiku-4-5
|
|
169
|
-
prompt: "Review this tool call for $TOOL_NAME with args: $ARGUMENTS"
|
|
170
|
-
batchGroup: analysis
|
|
171
|
-
timeout: 15000
|
|
172
|
-
|
|
173
|
-
- id: security-check
|
|
174
|
-
type: llm
|
|
175
|
-
model: claude-haiku-4-5
|
|
176
|
-
prompt: "Check for security issues in $TOOL_NAME call: $ARGUMENTS"
|
|
177
|
-
batchGroup: analysis # batched with code-review into one API call
|
|
206
|
+
- id: bash-guard
|
|
207
|
+
type: script
|
|
208
|
+
command: node ~/hooks/guard.js
|
|
209
|
+
filter: "Bash|Execute|!Read" # runs for Bash/Execute tools, never for Read
|
|
178
210
|
```
|
|
179
211
|
|
|
212
|
+
## LLM Handlers
|
|
213
|
+
|
|
180
214
|
**Setup:**
|
|
181
215
|
|
|
182
216
|
```bash
|
|
183
|
-
npm install @anthropic-ai/sdk # peer dependency, only
|
|
217
|
+
npm install @anthropic-ai/sdk # peer dependency, required only for llm handlers
|
|
184
218
|
export ANTHROPIC_API_KEY=sk-... # or set in manifest: settings.anthropicApiKey
|
|
185
219
|
```
|
|
186
220
|
|
|
@@ -196,64 +230,40 @@ export ANTHROPIC_API_KEY=sk-... # or set in manifest: settings.anthropicApiKey
|
|
|
196
230
|
| `$PROMPT` | `hook_input.prompt` | User's prompt (UserPromptSubmit only) |
|
|
197
231
|
| `$CWD` | `hook_input.cwd` | Current working directory |
|
|
198
232
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
| Field | Type | Default | Description |
|
|
202
|
-
|-------|------|---------|-------------|
|
|
203
|
-
| `model` | string | required | `claude-haiku-4-5`, `claude-sonnet-4-6`, or `claude-opus-4-6` |
|
|
204
|
-
| `prompt` | string | required | Prompt template with `$VARIABLE` interpolation |
|
|
205
|
-
| `batchGroup` | string | optional | Group ID -- handlers with same group make one API call |
|
|
206
|
-
| `maxTokens` | number | `1024` | Maximum output tokens |
|
|
207
|
-
| `temperature` | number | `1.0` | Sampling temperature |
|
|
208
|
-
| `filter` | string | optional | Keyword filter (see Filtering) |
|
|
209
|
-
| `timeout` | number | `30000` | Timeout in milliseconds |
|
|
210
|
-
|
|
211
|
-
**How batching works:**
|
|
212
|
-
|
|
213
|
-
When multiple LLM handlers share a `batchGroup` on the same event, clooks combines their prompts into a single multi-task API call and splits the structured response back to each handler. This means 3 Haiku calls become 1, saving ~2/3 of the input token cost and eliminating 2 round-trips.
|
|
233
|
+
`$TRANSCRIPT`, `$GIT_STATUS`, and `$GIT_DIFF` require the corresponding key in `prefetch`. The others are always available from the hook input.
|
|
214
234
|
|
|
215
|
-
|
|
235
|
+
**Batching:** Handlers sharing a `batchGroup` on the same event are combined into a single API call. Three Haiku calls become one, saving ~2/3 of input token cost and eliminating two round-trips. Batch groups are scoped per session to prevent cross-session contamination.
|
|
216
236
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
**Filter syntax:**
|
|
220
|
-
|
|
221
|
-
```
|
|
222
|
-
filter: "word1|word2" # run if input contains word1 OR word2
|
|
223
|
-
filter: "!word" # run unless input contains word
|
|
224
|
-
filter: "word1|!word2" # run if word1 present AND word2 absent
|
|
225
|
-
```
|
|
237
|
+
## Async Handlers
|
|
226
238
|
|
|
227
|
-
|
|
239
|
+
Handlers with `async: true` execute fire-and-forget -- they run in the background and do not block Claude's response. Use this for logging, analytics, or any work that does not need to inject context back into the session.
|
|
228
240
|
|
|
229
241
|
```yaml
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
command: node ~/hooks/guard.js
|
|
235
|
-
filter: "Bash|Execute|!Read" # runs for Bash/Execute, never for Read
|
|
242
|
+
- id: session-tracker
|
|
243
|
+
type: inline
|
|
244
|
+
module: ~/hooks/tracker.js
|
|
245
|
+
async: true
|
|
236
246
|
```
|
|
237
247
|
|
|
238
|
-
|
|
248
|
+
## Dependency Resolution
|
|
239
249
|
|
|
240
|
-
|
|
250
|
+
Handlers can declare dependencies with `depends`. clooks resolves them into topological execution waves -- handlers in the same wave run in parallel, waves execute sequentially.
|
|
241
251
|
|
|
242
252
|
```yaml
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
model: claude-haiku-4-5
|
|
253
|
-
prompt: "Summarize this session:\n$TRANSCRIPT\n\nGit changes:\n$GIT_DIFF"
|
|
253
|
+
- id: context-loader
|
|
254
|
+
type: inline
|
|
255
|
+
module: ~/hooks/context.js
|
|
256
|
+
|
|
257
|
+
- id: security-check
|
|
258
|
+
type: llm
|
|
259
|
+
model: claude-haiku-4-5
|
|
260
|
+
prompt: "Check $TOOL_NAME given context: $CONTEXT"
|
|
261
|
+
depends: [context-loader] # runs in wave 2, after context-loader completes in wave 1
|
|
254
262
|
```
|
|
255
263
|
|
|
256
|
-
|
|
264
|
+
## Pre-fetch
|
|
265
|
+
|
|
266
|
+
Fetch shared context once per hook event and make it available to all handlers via `$VARIABLE` interpolation in LLM prompts.
|
|
257
267
|
|
|
258
268
|
| Key | Source | Max size | Description |
|
|
259
269
|
|-----|--------|----------|-------------|
|
|
@@ -261,11 +271,38 @@ handlers:
|
|
|
261
271
|
| `git_status` | `git status --porcelain` | unbounded | Working tree status |
|
|
262
272
|
| `git_diff` | `git diff --stat` | 20KB | Changed files summary |
|
|
263
273
|
|
|
264
|
-
Pre-fetched data is cached for the duration of a single event dispatch. Errors on individual keys are silently caught -- a failed `git_status`
|
|
274
|
+
Pre-fetched data is cached for the duration of a single event dispatch. Errors on individual keys are silently caught -- a failed `git_status` does not prevent `transcript` from loading.
|
|
265
275
|
|
|
266
|
-
|
|
276
|
+
## Observability
|
|
267
277
|
|
|
268
|
-
|
|
278
|
+
**Execution metrics** -- `clooks stats` launches an interactive TUI by default. Use `-t` for plain text (also auto-selected when piped):
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
$ clooks stats -t
|
|
282
|
+
|
|
283
|
+
Event Fires Errors Avg (ms) Min (ms) Max (ms)
|
|
284
|
+
------------------------------------------------------------------------
|
|
285
|
+
PreToolUse 47 0 1.2 0.8 3.1
|
|
286
|
+
Stop 12 0 2.4 1.1 5.6
|
|
287
|
+
UserPromptSubmit 12 1 1.8 0.9 4.2
|
|
288
|
+
|
|
289
|
+
Total fires: 71 | Total errors: 1 | Spawns saved: ~71
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Diagnostics** -- `clooks doctor` runs health checks on daemon, port, manifest, settings, and handler state:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
$ clooks doctor
|
|
296
|
+
|
|
297
|
+
[pass] Daemon is running (PID 44721, uptime 2h 13m)
|
|
298
|
+
[pass] Port 7890 is responding
|
|
299
|
+
[pass] Manifest loaded: 4 handlers across 3 events
|
|
300
|
+
[pass] settings.json has HTTP hooks pointing to clooks
|
|
301
|
+
[pass] No handlers in circuit-breaker state
|
|
302
|
+
[warn] 1 handler error in last 24h (session-logger on Stop)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Cost tracking** -- `clooks costs` shows LLM token usage and spend per handler and model:
|
|
269
306
|
|
|
270
307
|
```
|
|
271
308
|
$ clooks costs
|
|
@@ -281,35 +318,44 @@ LLM Cost Summary
|
|
|
281
318
|
security-check $0.0053 (12 calls, avg 178 tokens)
|
|
282
319
|
```
|
|
283
320
|
|
|
284
|
-
- Costs
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
- Cost data also appears in `clooks stats` when LLM handlers have been used
|
|
321
|
+
Built-in pricing (per million tokens): Haiku ($0.80 / $4.00), Sonnet ($3.00 / $15.00), Opus ($15.00 / $75.00). Costs persist to `~/.clooks/costs.jsonl`.
|
|
322
|
+
|
|
323
|
+
## System Service
|
|
288
324
|
|
|
289
|
-
|
|
325
|
+
`clooks service install` creates a platform-native service (launchd on macOS, systemd on Linux) that starts the daemon on login and restarts it on crash. `clooks migrate` and `clooks init` install the service automatically. Use `clooks service status` to check and `clooks service uninstall` to remove.
|
|
290
326
|
|
|
291
|
-
|
|
327
|
+
## Plugin Development
|
|
292
328
|
|
|
293
|
-
Plugins
|
|
329
|
+
Plugins package reusable sets of handlers. A plugin is any directory with a `clooks-plugin.yaml`:
|
|
294
330
|
|
|
295
331
|
```yaml
|
|
296
332
|
# clooks-plugin.yaml
|
|
297
333
|
name: my-security-suite
|
|
298
334
|
version: 1.0.0
|
|
299
335
|
description: Security guards for tool calls
|
|
336
|
+
author: your-name
|
|
337
|
+
|
|
300
338
|
handlers:
|
|
301
339
|
PreToolUse:
|
|
302
340
|
- id: bash-guard
|
|
303
341
|
type: inline
|
|
304
|
-
module:
|
|
342
|
+
module: $PLUGIN_DIR/handlers/bash-guard.js # $PLUGIN_DIR resolves to plugin install path
|
|
305
343
|
timeout: 3000
|
|
306
344
|
- id: file-guard
|
|
307
345
|
type: inline
|
|
308
|
-
module:
|
|
346
|
+
module: $PLUGIN_DIR/handlers/file-guard.js
|
|
309
347
|
timeout: 2000
|
|
348
|
+
|
|
349
|
+
prefetch:
|
|
350
|
+
- git_status
|
|
351
|
+
|
|
352
|
+
extras:
|
|
353
|
+
skills: [security-audit] # skill names this plugin provides
|
|
354
|
+
agents: [security-reviewer] # agent names this plugin provides
|
|
355
|
+
readme: README.md # path to plugin README (relative to plugin dir)
|
|
310
356
|
```
|
|
311
357
|
|
|
312
|
-
|
|
358
|
+
**Installing and managing plugins:**
|
|
313
359
|
|
|
314
360
|
```bash
|
|
315
361
|
clooks add ./my-security-suite # install from local path
|
|
@@ -317,55 +363,84 @@ clooks remove my-security-suite # uninstall
|
|
|
317
363
|
clooks plugins # list installed plugins + handlers
|
|
318
364
|
```
|
|
319
365
|
|
|
320
|
-
Handler IDs are namespaced to the plugin (`my-security-suite
|
|
321
|
-
|
|
322
|
-
### Dependency Resolution
|
|
323
|
-
|
|
324
|
-
Handlers can declare dependencies on other handlers using the `depends` field. clooks resolves dependencies into topological execution waves -- handlers in the same wave run in parallel, waves execute sequentially.
|
|
366
|
+
Handler IDs are automatically namespaced to the plugin (`my-security-suite/bash-guard`) to avoid collisions with user-defined handlers or other plugins.
|
|
325
367
|
|
|
326
|
-
|
|
327
|
-
handlers:
|
|
328
|
-
PreToolUse:
|
|
329
|
-
- id: context-loader
|
|
330
|
-
type: inline
|
|
331
|
-
module: ~/hooks/context.js
|
|
368
|
+
## Expert Agent
|
|
332
369
|
|
|
333
|
-
|
|
334
|
-
type: llm
|
|
335
|
-
model: claude-haiku-4-5
|
|
336
|
-
prompt: "Check $TOOL_NAME for issues given context: $CONTEXT"
|
|
337
|
-
depends: [context-loader] # waits for context-loader to finish first
|
|
338
|
-
```
|
|
370
|
+
clooks ships with an expert agent that understands the full architecture, configuration, and troubleshooting workflow. It is auto-installed and auto-updated by `clooks migrate`, `clooks init`, and `clooks update`. Invoke it with `claude --agent clooks`.
|
|
339
371
|
|
|
340
|
-
|
|
372
|
+
## Short-Circuit Chains
|
|
341
373
|
|
|
342
|
-
|
|
374
|
+
When a `PreToolUse` handler returns a deny decision, clooks automatically skips the corresponding `PostToolUse` handlers for that tool call. Deny results are cached with a 30-second TTL, so repeated calls to the same tool with the same arguments short-circuit without re-evaluating handlers.
|
|
343
375
|
|
|
344
|
-
|
|
376
|
+
## Configuration Reference
|
|
345
377
|
|
|
346
|
-
|
|
378
|
+
| Item | Path / Value |
|
|
379
|
+
|------|-------------|
|
|
380
|
+
| Port | `7890` (default) |
|
|
381
|
+
| Config directory | `~/.clooks/` |
|
|
382
|
+
| Manifest | `~/.clooks/manifest.yaml` |
|
|
383
|
+
| Metrics | `~/.clooks/metrics.jsonl` |
|
|
384
|
+
| Costs | `~/.clooks/costs.jsonl` |
|
|
385
|
+
| Daemon log | `~/.clooks/daemon.log` |
|
|
386
|
+
| PID file | `~/.clooks/daemon.pid` |
|
|
387
|
+
| Plugins directory | `~/.clooks/plugins/` |
|
|
347
388
|
|
|
348
|
-
|
|
389
|
+
## Contributing
|
|
349
390
|
|
|
350
|
-
|
|
351
|
-
- **Health endpoint split:** `/health` is now public (returns `{ status: "ok" }` only). `/health/detail` requires auth and returns uptime, handler count, and plugin list.
|
|
352
|
-
- **Rate limiting on auth failures:** In-memory rate limiter rejects with 429 after repeated failed auth attempts within a time window. Resets on successful auth.
|
|
353
|
-
- **Session-scoped LLM batch groups:** Batch groups are now scoped to `{batchGroup}:{session_id}`, preventing cross-session batching violations.
|
|
354
|
-
- **Manifest reload resets handler state:** Reloading the manifest now diffs old vs new handlers and resets session-isolated state for changed or new handlers.
|
|
391
|
+
### Setup
|
|
355
392
|
|
|
356
|
-
|
|
393
|
+
```bash
|
|
394
|
+
git clone https://github.com/mauribadnights/clooks.git
|
|
395
|
+
cd clooks
|
|
396
|
+
npm install
|
|
397
|
+
```
|
|
357
398
|
|
|
358
|
-
|
|
399
|
+
### Codebase layout
|
|
359
400
|
|
|
360
|
-
|
|
401
|
+
```
|
|
402
|
+
src/
|
|
403
|
+
cli.ts Command definitions (commander)
|
|
404
|
+
server.ts HTTP daemon — hook routing, auth, session management
|
|
405
|
+
handlers.ts Handler execution engine (script, inline, LLM)
|
|
406
|
+
manifest.ts Manifest loading and validation
|
|
407
|
+
metrics.ts Metrics collection and aggregation
|
|
408
|
+
tui.ts Interactive terminal dashboard (ANSI-based)
|
|
409
|
+
llm.ts Anthropic API integration and batching
|
|
410
|
+
filter.ts Keyword filter engine
|
|
411
|
+
prefetch.ts Pre-fetch context (transcript, git status/diff)
|
|
412
|
+
plugin.ts Plugin install/remove/list
|
|
413
|
+
...
|
|
414
|
+
|
|
415
|
+
tests/ Mirrors src/ — one test file per module
|
|
416
|
+
benchmarks/ Performance benchmarks
|
|
417
|
+
docs/ Architecture diagram and assets
|
|
418
|
+
hooks/ Built-in hook scripts
|
|
419
|
+
agents/ Built-in agent definitions
|
|
420
|
+
```
|
|
361
421
|
|
|
362
|
-
|
|
422
|
+
### Development workflow
|
|
363
423
|
|
|
364
424
|
```bash
|
|
365
|
-
npm
|
|
366
|
-
npm
|
|
425
|
+
npm run build # Compile TypeScript to dist/
|
|
426
|
+
npm test # Run all tests (vitest)
|
|
427
|
+
npm run test:watch # Watch mode
|
|
428
|
+
npm run bench # Run performance benchmarks
|
|
429
|
+
npx tsc --noEmit # Type-check without emitting
|
|
367
430
|
```
|
|
368
431
|
|
|
432
|
+
### Pull request guidelines
|
|
433
|
+
|
|
434
|
+
1. Fork the repo and create a feature branch from `main`
|
|
435
|
+
2. Write tests for new functionality — tests are required for all PRs
|
|
436
|
+
3. Ensure `npm test` passes and `npx tsc --noEmit` reports zero errors
|
|
437
|
+
4. Write a clear PR description explaining **what** changed and **why**
|
|
438
|
+
5. Keep PRs focused — one feature or fix per PR
|
|
439
|
+
|
|
440
|
+
### Bug reports and feature requests
|
|
441
|
+
|
|
442
|
+
Open an issue at [github.com/mauribadnights/clooks/issues](https://github.com/mauribadnights/clooks/issues) with reproduction steps for bugs or a use-case description for features.
|
|
443
|
+
|
|
369
444
|
## License
|
|
370
445
|
|
|
371
446
|
MIT
|
package/dist/cli.js
CHANGED
package/dist/server.js
CHANGED
|
@@ -243,6 +243,7 @@ function createServer(manifest, metrics) {
|
|
|
243
243
|
usage: result.usage,
|
|
244
244
|
cost_usd: result.cost_usd,
|
|
245
245
|
session_id: input.session_id,
|
|
246
|
+
agent_type: currentAgent,
|
|
246
247
|
});
|
|
247
248
|
if (result.usage && result.cost_usd !== undefined && result.cost_usd > 0) {
|
|
248
249
|
const handlerConfig = allHandlerConfigs.find(h => h.id === result.id);
|