@polderlabs/bizar 6.2.0 → 6.2.2
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/cli/bin.mjs +19 -0
- package/cli/commands/setup-provider.mjs +292 -0
- package/cli/commands/setup-provider.test.mjs +211 -0
- package/cli/commands/validate.mjs +62 -16
- package/cli/commands/validate.test.mjs +41 -13
- package/cli/provision.mjs +92 -82
- package/config/agents/baldr.md +1 -1
- package/config/agents/forseti.md +1 -1
- package/config/agents/frigg.md +1 -1
- package/config/agents/heimdall.md +1 -1
- package/config/agents/hermod.md +1 -1
- package/config/agents/mimir.md +1 -1
- package/config/agents/odin.md +1 -1
- package/config/agents/quick.md +1 -1
- package/config/agents/semble-search.md +1 -1
- package/config/agents/thor.md +1 -1
- package/config/agents/tyr.md +1 -1
- package/config/agents/vidarr.md +1 -1
- package/config/agents/vor.md +1 -1
- package/config/cline.json.template +20 -93
- package/config/commands/setup-provider.md +95 -0
- package/config/hooks/PostToolUse +66 -0
- package/config/hooks/PreToolUse +82 -0
- package/config/hooks/README.md +60 -21
- package/config/hooks/TaskResume +48 -0
- package/config/hooks/TaskStart +47 -0
- package/config/hooks/UserPromptSubmit +55 -0
- package/package.json +2 -2
- package/plugins/bizar/package.json +1 -1
- package/scripts/bh-full-e2e.mjs +40 -5
- package/config/hooks/post-tool-use.md +0 -16
- package/config/hooks/pre-tool-use.md +0 -16
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://docs.cline.bot/config.json",
|
|
3
3
|
"instructions": [".cline/instructions/bizar-tools.md"],
|
|
4
|
-
"model": "
|
|
5
|
-
"small_model": "
|
|
4
|
+
"model": "minimaxcustom/MiniMax-M3",
|
|
5
|
+
"small_model": "minimaxcustom/MiniMax-M2.7",
|
|
6
6
|
"default_agent": "odin",
|
|
7
7
|
"permission": "allow",
|
|
8
8
|
"snapshot": false,
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"threshold": 0.5,
|
|
36
36
|
"preserve_recent": 10,
|
|
37
37
|
"strategy": "summarize",
|
|
38
|
-
"model": "
|
|
38
|
+
"model": "minimaxcustom/MiniMax-M3"
|
|
39
39
|
}
|
|
40
40
|
}]
|
|
41
41
|
],
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"odin": {
|
|
53
53
|
"description": "Odin — Pure router that delegates all work to subagents.",
|
|
54
54
|
"mode": "primary",
|
|
55
|
-
"model": "
|
|
55
|
+
"model": "minimaxcustom/MiniMax-M3",
|
|
56
56
|
"color": "#6366f1",
|
|
57
57
|
"permission": {
|
|
58
58
|
"task": "allow",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"vor": {
|
|
67
67
|
"description": "Vör — The Questioning One.",
|
|
68
68
|
"mode": "subagent",
|
|
69
|
-
"model": "
|
|
69
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
70
70
|
"color": "#8b5cf6",
|
|
71
71
|
"permission": {
|
|
72
72
|
"read": "allow",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"frigg": {
|
|
78
78
|
"description": "Frigg — All-knowing Q&A agent.",
|
|
79
79
|
"mode": "primary",
|
|
80
|
-
"model": "
|
|
80
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
81
81
|
"color": "#06b6d4",
|
|
82
82
|
"permission": {
|
|
83
83
|
"read": "allow",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"quick": {
|
|
94
94
|
"description": "Quick — Fast single-shot tasks.",
|
|
95
95
|
"mode": "primary",
|
|
96
|
-
"model": "
|
|
96
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
97
97
|
"color": "#22d3ee",
|
|
98
98
|
"permission": {
|
|
99
99
|
"read": "allow",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"mimir": {
|
|
113
113
|
"description": "Mimir — Research and codebase exploration agent.",
|
|
114
114
|
"mode": "subagent",
|
|
115
|
-
"model": "
|
|
115
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
116
116
|
"color": "#0ea5e9",
|
|
117
117
|
"permission": {
|
|
118
118
|
"read": "allow",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"heimdall": {
|
|
131
131
|
"description": "Heimdall — Simple, routine, deterministic tasks.",
|
|
132
132
|
"mode": "subagent",
|
|
133
|
-
"model": "
|
|
133
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
134
134
|
"color": "#10b981",
|
|
135
135
|
"permission": {
|
|
136
136
|
"read": "allow",
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"hermod": {
|
|
148
148
|
"description": "Hermod — Git and GitHub operations specialist.",
|
|
149
149
|
"mode": "subagent",
|
|
150
|
-
"model": "
|
|
150
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
151
151
|
"color": "#06b6d4",
|
|
152
152
|
"permission": {
|
|
153
153
|
"read": "allow",
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
"thor": {
|
|
164
164
|
"description": "Thor — Medium-complexity tasks, strong and reliable.",
|
|
165
165
|
"mode": "subagent",
|
|
166
|
-
"model": "
|
|
166
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
167
167
|
"color": "#a855f7",
|
|
168
168
|
"permission": {
|
|
169
169
|
"read": "allow",
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
"baldr": {
|
|
181
181
|
"description": "Baldr — UI/UX design system specialist.",
|
|
182
182
|
"mode": "subagent",
|
|
183
|
-
"model": "
|
|
183
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
184
184
|
"color": "#ec4899",
|
|
185
185
|
"permission": {
|
|
186
186
|
"read": "allow",
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
"tyr": {
|
|
198
198
|
"description": "Tyr — Complex implementation, debugging, architecture.",
|
|
199
199
|
"mode": "subagent",
|
|
200
|
-
"model": "
|
|
200
|
+
"model": "minimaxcustom/MiniMax-M3",
|
|
201
201
|
"color": "#f59e0b",
|
|
202
202
|
"permission": {
|
|
203
203
|
"read": "allow",
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
"vidarr": {
|
|
215
215
|
"description": "Vidarr — Ultimate fallback via MiniMax M3.",
|
|
216
216
|
"mode": "subagent",
|
|
217
|
-
"model": "
|
|
217
|
+
"model": "minimaxcustom/MiniMax-M3",
|
|
218
218
|
"color": "#dc2626",
|
|
219
219
|
"permission": {
|
|
220
220
|
"read": "allow",
|
|
@@ -231,7 +231,7 @@
|
|
|
231
231
|
"forseti": {
|
|
232
232
|
"description": "Forseti — Audits and corrects plans before execution.",
|
|
233
233
|
"mode": "subagent",
|
|
234
|
-
"model": "
|
|
234
|
+
"model": "minimaxcustom/MiniMax-M3",
|
|
235
235
|
"color": "#ef4444",
|
|
236
236
|
"permission": {
|
|
237
237
|
"read": "allow",
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
"browser-harness": {
|
|
250
250
|
"description": "browser-harness — Primary agent for browser-driven E2E verification. No-edit permissions. Drives Chrome via CDP for end-to-end testing of web apps.",
|
|
251
251
|
"mode": "primary",
|
|
252
|
-
"model": "
|
|
252
|
+
"model": "minimaxcustom/MiniMax-M2.7",
|
|
253
253
|
"color": "#84cc16",
|
|
254
254
|
"permission": {
|
|
255
255
|
"read": "allow",
|
|
@@ -332,84 +332,11 @@
|
|
|
332
332
|
"description": "Validate the Bizar install: cline.json, plugin entry, agents, skills, hooks, rules, commands, and provider config.",
|
|
333
333
|
"agent": "heimdall",
|
|
334
334
|
"template": "commands-bizar/validate.md\n\n$ARGUMENTS"
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
"provider": {
|
|
338
|
-
"9router": {
|
|
339
|
-
"baseUrl": "http://localhost:20128/v1",
|
|
340
|
-
"apiKey": "${env:NINEROUTER_KEY}",
|
|
341
|
-
"models": {
|
|
342
|
-
"minimax/MiniMax-M3": {
|
|
343
|
-
"interleaved": { "field": "reasoning_details" },
|
|
344
|
-
"reasoning": true,
|
|
345
|
-
"options": {
|
|
346
|
-
"thinking": { "type": "enabled" }
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
"minimax/MiniMax-M2.7": {
|
|
350
|
-
"interleaved": { "field": "reasoning_details" },
|
|
351
|
-
"reasoning": true,
|
|
352
|
-
"options": {
|
|
353
|
-
"thinking": { "type": "enabled" }
|
|
354
|
-
}
|
|
355
|
-
},
|
|
356
|
-
"minimax/MiniMax-M2.5": {
|
|
357
|
-
"interleaved": { "field": "reasoning_details" },
|
|
358
|
-
"reasoning": true,
|
|
359
|
-
"options": {
|
|
360
|
-
"thinking": { "type": "enabled" }
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
"minimax/MiniMax-M2.1": {
|
|
364
|
-
"interleaved": { "field": "reasoning_details" },
|
|
365
|
-
"reasoning": true,
|
|
366
|
-
"options": {
|
|
367
|
-
"thinking": { "type": "enabled" }
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
"kr/auto": {
|
|
371
|
-
"reasoning": false
|
|
372
|
-
},
|
|
373
|
-
"kr/auto-thinking": {
|
|
374
|
-
"reasoning": true
|
|
375
|
-
},
|
|
376
|
-
"kr/claude-sonnet-4.5": {
|
|
377
|
-
"reasoning": false
|
|
378
|
-
},
|
|
379
|
-
"kr/claude-haiku-4.5": {
|
|
380
|
-
"reasoning": false
|
|
381
|
-
},
|
|
382
|
-
"kr/deepseek-3.2": {
|
|
383
|
-
"reasoning": false
|
|
384
|
-
},
|
|
385
|
-
"openrouter/cohere/north-mini-code:free": {
|
|
386
|
-
"reasoning": false
|
|
387
|
-
},
|
|
388
|
-
"openrouter/poolside/laguna-m.1:free": {
|
|
389
|
-
"reasoning": false
|
|
390
|
-
},
|
|
391
|
-
"openrouter/nvidia/nemotron-3-super-120b-a12b:free": {
|
|
392
|
-
"reasoning": true
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
335
|
},
|
|
396
|
-
"
|
|
397
|
-
"
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
"reasoning": true,
|
|
401
|
-
"options": {
|
|
402
|
-
"thinking": { "type": "enabled" }
|
|
403
|
-
}
|
|
404
|
-
},
|
|
405
|
-
"MiniMax-M2.7": {
|
|
406
|
-
"interleaved": { "field": "reasoning_details" },
|
|
407
|
-
"reasoning": true,
|
|
408
|
-
"options": {
|
|
409
|
-
"thinking": { "type": "enabled" }
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
336
|
+
"setup-provider": {
|
|
337
|
+
"description": "Configure a provider in cline.json. Since v6.2.2 the installer no longer touches provider config — use this to add your own.",
|
|
338
|
+
"agent": "heimdall",
|
|
339
|
+
"template": "commands-bizar/setup-provider.md\n\n$ARGUMENTS"
|
|
413
340
|
}
|
|
414
341
|
}
|
|
415
342
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Configure a provider in cline.json (apiKey, baseUrl, model catalog). The installer no longer touches provider config — use this command to set one up.
|
|
3
|
+
agent: heimdall
|
|
4
|
+
---
|
|
5
|
+
# /setup-provider — Configure a provider in cline.json
|
|
6
|
+
|
|
7
|
+
The Bizar installer no longer configures a provider for you (since
|
|
8
|
+
v6.2.2 — the user owns provider config). Use this command to add
|
|
9
|
+
or update a provider in `~/.cline/cline.json`.
|
|
10
|
+
|
|
11
|
+
## What It Does
|
|
12
|
+
|
|
13
|
+
Interactively (or non-interactively with flags), the setup-provider
|
|
14
|
+
command writes a `provider` block to `~/.cline/cline.json` with:
|
|
15
|
+
|
|
16
|
+
- `baseUrl` — the API endpoint (e.g. `http://localhost:20128/v1` for
|
|
17
|
+
the local 9Router gateway)
|
|
18
|
+
- `apiKey` — the provider's API key (read from `env:` reference or
|
|
19
|
+
direct value)
|
|
20
|
+
- `models` — the model catalog (the IDs returned by
|
|
21
|
+
`GET /v1/models`)
|
|
22
|
+
|
|
23
|
+
## Default: 9Router Gateway
|
|
24
|
+
|
|
25
|
+
If the user invokes `/setup-provider` with no arguments, default to
|
|
26
|
+
the local 9Router gateway at `http://localhost:20128/v1`. The
|
|
27
|
+
catalog comes from `GET http://localhost:20128/v1/models`. As of
|
|
28
|
+
v6.2.2, that catalog includes:
|
|
29
|
+
|
|
30
|
+
- `minimax/MiniMax-M3` (reasoning)
|
|
31
|
+
- `minimax/MiniMax-M2.7` (reasoning)
|
|
32
|
+
- `nvidia/minimaxai/minimax-m3` (reasoning)
|
|
33
|
+
- `nvidia/minimaxai/minimax-m2.7` (reasoning)
|
|
34
|
+
- `nvidia/z-ai/glm-5.2`
|
|
35
|
+
- `nvidia/deepseek-ai/deepseek-v4-pro`
|
|
36
|
+
- `nvidia/deepseek-ai/deepseek-v4-flash`
|
|
37
|
+
- `nvidia/moonshotai/kimi-k2.6`
|
|
38
|
+
- `nvidia/nemotron-3-ultra-550b-a55b`
|
|
39
|
+
- `openrouter/cohere/north-mini-code:free`
|
|
40
|
+
- `openrouter/poolside/laguna-m.1:free`
|
|
41
|
+
- `openrouter/nvidia/nemotron-3-super-120b-a12b:free`
|
|
42
|
+
- (and several more `minimax/MiniMax-M2.x` variants)
|
|
43
|
+
|
|
44
|
+
## Flags
|
|
45
|
+
|
|
46
|
+
- `--gateway <url>` — override the 9Router URL (default
|
|
47
|
+
`http://localhost:20128/v1`)
|
|
48
|
+
- `--key <key>` — provider API key (writes `${env:KEY_NAME}` if it
|
|
49
|
+
looks like an env var name, otherwise writes the literal)
|
|
50
|
+
- `--provider <name>` — provider name in cline.json (default `9router`)
|
|
51
|
+
- `--model <id>` — add a single model instead of fetching the catalog
|
|
52
|
+
- `--list` — print the model catalog from `/v1/models` and exit
|
|
53
|
+
- `--remove <name>` — remove a provider by name
|
|
54
|
+
|
|
55
|
+
## Examples
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
# Use the default 9Router gateway with the live model catalog
|
|
59
|
+
/setup-provider
|
|
60
|
+
|
|
61
|
+
# Custom provider
|
|
62
|
+
/setup-provider --provider my-anthropic --gateway https://api.anthropic.com/v1 --key sk-ant-...
|
|
63
|
+
|
|
64
|
+
# Just list the available models
|
|
65
|
+
/setup-provider --list
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Implementation
|
|
69
|
+
|
|
70
|
+
1. If `--list`, GET `${gateway}/v1/models` and pretty-print the IDs.
|
|
71
|
+
2. If `--remove`, edit `~/.cline/cline.json` and remove the
|
|
72
|
+
`provider.<name>` block.
|
|
73
|
+
3. Otherwise, read `~/.cline/cline.json`. If the file doesn't
|
|
74
|
+
exist, error — the user should run `bizar install` first.
|
|
75
|
+
4. Build the new `provider.<name>` block from the flags + fetched
|
|
76
|
+
catalog.
|
|
77
|
+
5. Write the file back atomically (write to `cline.json.tmp`,
|
|
78
|
+
then `rename`).
|
|
79
|
+
6. Run `bizar validate` to confirm everything is wired up.
|
|
80
|
+
|
|
81
|
+
## Security
|
|
82
|
+
|
|
83
|
+
Never echo or log the full API key. If the user supplies a key on
|
|
84
|
+
the command line, write it as a literal value (or `${env:KEY}`
|
|
85
|
+
reference if it matches a `KEY_NAME` pattern). Never commit
|
|
86
|
+
`~/.cline/cline.json` to source control — it may contain real
|
|
87
|
+
secrets.
|
|
88
|
+
|
|
89
|
+
## Related
|
|
90
|
+
|
|
91
|
+
- `bizar connect` — interactive TUI for provider setup (alternative)
|
|
92
|
+
- `bizar install` — installs the Bizar scaffolding (NOT provider config
|
|
93
|
+
since v6.2.2)
|
|
94
|
+
- `bizar validate` — confirms provider config is wired correctly
|
|
95
|
+
- Cline provider config: https://docs.cline.bot/customization/providers
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PostToolUse — Bizar harness hook.
|
|
3
|
+
//
|
|
4
|
+
// Runs AFTER a tool completes. Reads JSON from stdin, outputs JSON to
|
|
5
|
+
// stdout. This hook does NOT block (post-tool actions can't undo the
|
|
6
|
+
// call); it just adds context for the AI's next decision.
|
|
7
|
+
//
|
|
8
|
+
// Responsibilities:
|
|
9
|
+
// 1. Log tool latency for performance observability (append-only JSONL).
|
|
10
|
+
// 2. Surface any pre-tool warnings (e.g. console.log detected) AFTER
|
|
11
|
+
// the tool ran, so the model gets a second chance to fix it.
|
|
12
|
+
// 3. Remind the AI to run /test before claiming "done".
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const os = require('os');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
let raw = '';
|
|
21
|
+
process.stdin.setEncoding('utf8');
|
|
22
|
+
process.stdin.on('data', (chunk) => { raw += chunk; });
|
|
23
|
+
process.stdin.on('end', () => {
|
|
24
|
+
let input = {};
|
|
25
|
+
try { input = JSON.parse(raw); } catch { input = {}; }
|
|
26
|
+
const post = input.postToolUse || {};
|
|
27
|
+
const toolName = String(post.toolName || '');
|
|
28
|
+
const success = post.success !== false;
|
|
29
|
+
const executionTimeMs = Number(post.executionTimeMs) || 0;
|
|
30
|
+
const params = (post.parameters && typeof post.parameters === 'object') ? post.parameters : {};
|
|
31
|
+
|
|
32
|
+
// 1. Append-only JSONL log under ~/.config/bizar/hook-logs/
|
|
33
|
+
try {
|
|
34
|
+
const logDir = path.join(os.homedir(), '.config', 'bizar', 'hook-logs');
|
|
35
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
36
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
37
|
+
const logFile = path.join(logDir, `post-tool-use-${today}.jsonl`);
|
|
38
|
+
const line = JSON.stringify({
|
|
39
|
+
ts: new Date().toISOString(),
|
|
40
|
+
tool: toolName,
|
|
41
|
+
success,
|
|
42
|
+
executionTimeMs,
|
|
43
|
+
sessionId: input.taskId || null,
|
|
44
|
+
});
|
|
45
|
+
fs.appendFileSync(logFile, line + '\n');
|
|
46
|
+
} catch { /* best-effort */ }
|
|
47
|
+
|
|
48
|
+
const out = { cancel: false, contextModification: '' };
|
|
49
|
+
const notes = [];
|
|
50
|
+
|
|
51
|
+
if (!success) {
|
|
52
|
+
notes.push(`Bizar PostToolUse: tool '${toolName}' failed. Investigate the error before proceeding.`);
|
|
53
|
+
} else if (executionTimeMs > 5000) {
|
|
54
|
+
notes.push(`Bizar PostToolUse: tool '${toolName}' took ${executionTimeMs}ms — consider whether this is worth the cost.`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Surface a reminder for write_to_file / apply_patch to src/.
|
|
58
|
+
const path = params.path || params.file_path || params.filePath || params.target_file || '';
|
|
59
|
+
if (/^write_to_file$|^apply_patch$|^replace_in_file$|^edit$/i.test(toolName) &&
|
|
60
|
+
/\/src\/|\/plugins\/|\/packages\//.test(String(path))) {
|
|
61
|
+
notes.push("Bizar PostToolUse: if you're done editing, run `/test` to gate the change with tests before claiming complete.");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
out.contextModification = notes.join(' ');
|
|
65
|
+
process.stdout.write(JSON.stringify(out) + '\n');
|
|
66
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse — Bizar harness hook.
|
|
3
|
+
//
|
|
4
|
+
// Cline hook script (https://docs.cline.bot/customization/hooks).
|
|
5
|
+
// Runs BEFORE any tool call. Reads JSON from stdin, outputs JSON to
|
|
6
|
+
// stdout. Returns `{ cancel: true, errorMessage: "..." }` to block
|
|
7
|
+
// the tool call, or `{ cancel: false, contextModification: "..." }`
|
|
8
|
+
// to add context for the next AI decision.
|
|
9
|
+
//
|
|
10
|
+
// This hook enforces three Bizar harness invariants:
|
|
11
|
+
// 1. Block writes to .env, secrets, .lock files, node_modules.
|
|
12
|
+
// 2. Warn (don't block) on console.log / debugger / .only() in src/.
|
|
13
|
+
// 3. Always return a small context line for the next AI decision.
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
let raw = '';
|
|
18
|
+
process.stdin.setEncoding('utf8');
|
|
19
|
+
process.stdin.on('data', (chunk) => { raw += chunk; });
|
|
20
|
+
process.stdin.on('end', () => {
|
|
21
|
+
let input = {};
|
|
22
|
+
try { input = JSON.parse(raw); } catch { input = {}; }
|
|
23
|
+
const pre = input.preToolUse || {};
|
|
24
|
+
const toolName = String(pre.toolName || '');
|
|
25
|
+
const params = (pre.parameters && typeof pre.parameters === 'object') ? pre.parameters : {};
|
|
26
|
+
|
|
27
|
+
const out = { cancel: false, contextModification: '' };
|
|
28
|
+
const notes = [];
|
|
29
|
+
|
|
30
|
+
// Extract the most common "path" parameter (varies by tool name).
|
|
31
|
+
const path =
|
|
32
|
+
params.path ||
|
|
33
|
+
params.file_path ||
|
|
34
|
+
params.filePath ||
|
|
35
|
+
params.target_file ||
|
|
36
|
+
params.targetFile ||
|
|
37
|
+
params.notebook_path ||
|
|
38
|
+
'';
|
|
39
|
+
|
|
40
|
+
const lower = String(path).toLowerCase();
|
|
41
|
+
|
|
42
|
+
// 1. Hard block — secrets + protected paths.
|
|
43
|
+
const blocked = [
|
|
44
|
+
/\/\.env(\.|$|\/)/,
|
|
45
|
+
/\/\.envrc$/,
|
|
46
|
+
/\/secrets?\//,
|
|
47
|
+
/\/credentials?/,
|
|
48
|
+
/\/node_modules\//,
|
|
49
|
+
/\.(lock|lockb)$/,
|
|
50
|
+
/\/package-lock\.json$/,
|
|
51
|
+
/\/bun\.lockb?$/,
|
|
52
|
+
/\/yarn\.lock$/,
|
|
53
|
+
];
|
|
54
|
+
if (path && blocked.some((re) => re.test(lower))) {
|
|
55
|
+
out.cancel = true;
|
|
56
|
+
out.errorMessage = `PreToolUse: refusing to write to protected path '${path}' (Bizar harness policy).`;
|
|
57
|
+
process.stdout.write(JSON.stringify(out) + '\n');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. Warn — debug artifacts in src/.
|
|
62
|
+
if (path && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(lower)) {
|
|
63
|
+
if (/\/src\//.test(lower) || /\/plugins\//.test(lower) || /\/packages\//.test(lower)) {
|
|
64
|
+
const text = JSON.stringify(params);
|
|
65
|
+
if (/\bconsole\.(log|debug|warn)\b/.test(text) && !/\bconsole\.(error|info)\b/.test(text)) {
|
|
66
|
+
notes.push('Heads up: console.log detected in src — `make clean-check` will fail.');
|
|
67
|
+
}
|
|
68
|
+
if (/\bdebugger\b/.test(text)) {
|
|
69
|
+
notes.push('Heads up: debugger statement detected — remove before committing.');
|
|
70
|
+
}
|
|
71
|
+
if (/\.only\s*\(/.test(text) && !/^[\s\S]*\.skip\s*\(/m.test(text)) {
|
|
72
|
+
notes.push('Heads up: .only() detected — `make clean-check` will fail.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 3. Always add a small context line for the AI's next decision.
|
|
78
|
+
notes.push(`Bizar PreToolUse: tool=${toolName || 'unknown'} path=${path || '(no path)'}`);
|
|
79
|
+
|
|
80
|
+
out.contextModification = notes.join(' ');
|
|
81
|
+
process.stdout.write(JSON.stringify(out) + '\n');
|
|
82
|
+
});
|
package/config/hooks/README.md
CHANGED
|
@@ -1,29 +1,68 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Bizar Harness — Cline Hooks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This directory contains Cline-native hook scripts. They are NOT markdown
|
|
4
|
+
behavioral patterns — they are real Cline hook executables that run at
|
|
5
|
+
specific lifecycle events.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Format
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|------|------|---------|----------|
|
|
9
|
-
| `pre-tool-use` | Behavioral | Before any edit/write | Check for secrets, verify permissions |
|
|
10
|
-
| `post-tool-use` | Behavioral | After any edit/write | Auto-lint, auto-format, auto-test |
|
|
11
|
-
| `session-start` | Behavioral | On session start | Read .bizar/, Hindsight recall, stack detection |
|
|
12
|
-
| `session-end` | Behavioral | On task completion | Append to AGENTS_SELF_IMPROVEMENT.md |
|
|
13
|
-
| `pre-commit` | Behavioral | Before git commit | Check for console.log, .env leaks, lint |
|
|
14
|
-
| `post-implement` | Behavioral | After parallel implementation | Run test gate, verify both agents' outputs |
|
|
9
|
+
Cline hooks are executable scripts (no extension) with a shebang line:
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
```bash
|
|
12
|
+
#!/usr/bin/env node
|
|
13
|
+
```
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
They live in two locations:
|
|
16
|
+
- **Global**: `~/Documents/Cline/Hooks/` (applies to all workspaces)
|
|
17
|
+
- **Workspace**: `.clinerules/hooks/` (applies to one project)
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- **Session-start**: Odin reads `.bizar/PROJECT.md` and Hindsight; Heimdall runs `bizar init` if missing
|
|
23
|
-
- **Session-end**: Odin dispatches Heimdall to auto-extract self-improvement entries
|
|
24
|
-
- **Pre-commit**: Hermod checks for console.log and .env before allowing git commits
|
|
25
|
-
- **Post-implement**: After Thor+Tyr complete, Odin routes to Thor for test gate
|
|
19
|
+
The `bizar install` / `bizar update` commands install these scripts to
|
|
20
|
+
**both** locations with `chmod +x` so Cline picks them up.
|
|
26
21
|
|
|
27
|
-
##
|
|
22
|
+
## What each hook does
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
| Hook | Stage | Job |
|
|
25
|
+
|------|-------|-----|
|
|
26
|
+
| `TaskStart` | New task | Prime the AI with `.bizar/PROJECT.md` + memory-vault search |
|
|
27
|
+
| `TaskResume` | Existing task resumed | Re-read project state, check `git log` since last run |
|
|
28
|
+
| `UserPromptSubmit` | User submits prompt | Tag the prompt for routing (`/team`, `/plow-through`, etc.) |
|
|
29
|
+
| `PreToolUse` | Before every tool | Block writes to `.env`, `secrets/`, `node_modules/`, lockfiles; warn on `console.log`/`debugger`/`.only()` in `src/` |
|
|
30
|
+
| `PostToolUse` | After every tool | Log latency to `~/.config/bizar/hook-logs/`, remind to run `/test` after edits |
|
|
31
|
+
|
|
32
|
+
## I/O contract
|
|
33
|
+
|
|
34
|
+
Cline invokes each hook with **JSON on stdin** and reads **JSON on stdout**.
|
|
35
|
+
|
|
36
|
+
Input shape (Cline → hook):
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"clineVersion": "3.0.39",
|
|
41
|
+
"hookName": "PreToolUse",
|
|
42
|
+
"taskId": "...",
|
|
43
|
+
"preToolUse": { "toolName": "write_to_file", "parameters": { "path": "src/foo.ts", ... } }
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Output shape (hook → Cline):
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"cancel": false,
|
|
52
|
+
"contextModification": "small note for the next AI decision"
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Set `cancel: true` to block the tool call. The optional `errorMessage`
|
|
57
|
+
is shown to the user.
|
|
58
|
+
|
|
59
|
+
## Disabling a hook
|
|
60
|
+
|
|
61
|
+
Rename the file to add a `.disabled` suffix (e.g. `PreToolUse.disabled`)
|
|
62
|
+
or remove the executable bit (`chmod -x PreToolUse`). Cline silently
|
|
63
|
+
skips hooks it can't execute.
|
|
64
|
+
|
|
65
|
+
## See also
|
|
66
|
+
|
|
67
|
+
- [Cline hooks documentation](https://docs.cline.bot/customization/hooks)
|
|
68
|
+
- `cli/commands/validate.mjs` — `bizar validate` includes a `hooks-installed` check
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// TaskResume — Bizar harness hook.
|
|
3
|
+
//
|
|
4
|
+
// Runs when an existing task is resumed. Reads JSON from stdin, outputs
|
|
5
|
+
// JSON to stdout. Used to remind the model of previous progress and to
|
|
6
|
+
// detect scope drift since the last run.
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
let raw = '';
|
|
15
|
+
process.stdin.setEncoding('utf8');
|
|
16
|
+
process.stdin.on('data', (chunk) => { raw += chunk; });
|
|
17
|
+
process.stdin.on('end', () => {
|
|
18
|
+
let input = {};
|
|
19
|
+
try { input = JSON.parse(raw); } catch { input = {}; }
|
|
20
|
+
const task = input.taskResume || {};
|
|
21
|
+
const previous = task.previousState || {};
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const logDir = path.join(os.homedir(), '.config', 'bizar', 'hook-logs');
|
|
25
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
26
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
27
|
+
const logFile = path.join(logDir, `task-resume-${today}.jsonl`);
|
|
28
|
+
fs.appendFileSync(logFile, JSON.stringify({
|
|
29
|
+
ts: new Date().toISOString(),
|
|
30
|
+
taskId: input.taskId || null,
|
|
31
|
+
lastMessageTs: previous.lastMessageTs || null,
|
|
32
|
+
messageCount: previous.messageCount || null,
|
|
33
|
+
}) + '\n');
|
|
34
|
+
} catch { /* best-effort */ }
|
|
35
|
+
|
|
36
|
+
const notes = [
|
|
37
|
+
'Bizar TaskResume: re-read `.bizar/PROJECT.md` and `PROGRESS.md` before continuing.',
|
|
38
|
+
'Bizar TaskResume: check `git log --oneline -10` to see what changed since the last run.',
|
|
39
|
+
];
|
|
40
|
+
if (previous.conversationHistoryDeleted) {
|
|
41
|
+
notes.push('Bizar TaskResume: WARNING — conversation history was deleted. Context from previous run is gone; verify scope before continuing.');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
process.stdout.write(JSON.stringify({
|
|
45
|
+
cancel: false,
|
|
46
|
+
contextModification: notes.join(' '),
|
|
47
|
+
}) + '\n');
|
|
48
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// TaskStart — Bizar harness hook.
|
|
3
|
+
//
|
|
4
|
+
// Runs when a NEW task is started (not when resuming). Reads JSON from
|
|
5
|
+
// stdin, outputs JSON to stdout. Used to prime the AI with project
|
|
6
|
+
// context and to record the start of a session.
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
let raw = '';
|
|
15
|
+
process.stdin.setEncoding('utf8');
|
|
16
|
+
process.stdin.on('data', (chunk) => { raw += chunk; });
|
|
17
|
+
process.stdin.on('end', () => {
|
|
18
|
+
let input = {};
|
|
19
|
+
try { input = JSON.parse(raw); } catch { input = {}; }
|
|
20
|
+
const task = input.taskStart || {};
|
|
21
|
+
const initial = String(task.taskMetadata?.initialTask || '').slice(0, 500);
|
|
22
|
+
|
|
23
|
+
// Log task start to ~/.config/bizar/hook-logs/
|
|
24
|
+
try {
|
|
25
|
+
const logDir = path.join(os.homedir(), '.config', 'bizar', 'hook-logs');
|
|
26
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
27
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
28
|
+
const logFile = path.join(logDir, `task-start-${today}.jsonl`);
|
|
29
|
+
fs.appendFileSync(logFile, JSON.stringify({
|
|
30
|
+
ts: new Date().toISOString(),
|
|
31
|
+
taskId: input.taskId || null,
|
|
32
|
+
clineVersion: input.clineVersion || null,
|
|
33
|
+
initial: initial,
|
|
34
|
+
}) + '\n');
|
|
35
|
+
} catch { /* best-effort */ }
|
|
36
|
+
|
|
37
|
+
const notes = [
|
|
38
|
+
'Bizar TaskStart: read `.bizar/PROJECT.md` (if present) before any routing decision.',
|
|
39
|
+
'Bizar TaskStart: if memory vault exists, run `bizar memory search "<topic>"` first.',
|
|
40
|
+
'Bizar TaskStart: Odin dispatches to subagents via `task` (sync) or `bizar_spawn_background` (async).',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
process.stdout.write(JSON.stringify({
|
|
44
|
+
cancel: false,
|
|
45
|
+
contextModification: notes.join(' '),
|
|
46
|
+
}) + '\n');
|
|
47
|
+
});
|