@polderlabs/bizar 6.1.0 → 6.2.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/bizar-dash/DESIGN.md +18 -23
- package/bizar-dash/critique.json +6 -6
- package/cli/bin.mjs +19 -1
- package/cli/commands/validate.mjs +480 -0
- package/cli/commands/validate.test.mjs +281 -0
- package/cli/post-install-smoke.mjs +96 -3
- package/cli/post-install-smoke.test.mjs +3 -5
- package/cli/provision.mjs +137 -16
- package/config/cline.json.template +15 -0
- package/config/commands/team.md +124 -0
- package/config/commands/test.md +60 -0
- package/config/commands/validate.md +66 -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 +5 -4
- package/plugins/bizar/package.json +1 -1
- package/plugins/bizar/src/clineruntime.ts +1 -1
- package/plugins/bizar/tests/clineruntime-config.test.ts +44 -0
- package/scripts/bh-full-e2e.mjs +422 -0
- package/scripts/check-arch.sh +80 -0
- package/scripts/check-deps.mjs +510 -0
- package/scripts/clean-state-check.sh +81 -0
- package/scripts/git-hooks/pre-commit +59 -0
- package/scripts/git-hooks/pre-push +54 -0
- package/scripts/install-hooks.sh +29 -0
- package/scripts/install-service.mjs +190 -0
- package/scripts/session-trace.sh +52 -0
- package/scripts/verify-feature.sh +102 -0
- package/bizar-dash/image-1.png +0 -0
- package/bizar-dash/image-2.png +0 -0
- package/bizar-dash/image-3.png +0 -0
- package/bizar-dash/image-4.png +0 -0
- package/bizar-dash/image-5.png +0 -0
- package/bizar-dash/image-6.png +0 -0
- package/bizar-dash/image-7.png +0 -0
- package/bizar-dash/image.png +0 -0
- package/config/hooks/post-tool-use.md +0 -16
- package/config/hooks/pre-tool-use.md +0 -16
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Spawn a Cline agent team — coordinate Thor (M2.7), Tyr (M3), Mimir (research), and Hermod (git) to complete a complex mission end-to-end.
|
|
3
|
+
agent: odin
|
|
4
|
+
---
|
|
5
|
+
# Team — Spawn a Cline Agent Team
|
|
6
|
+
|
|
7
|
+
The `/team` command spawns a **coordinated agent team** for a complex,
|
|
8
|
+
multi-step mission. The lead agent coordinates teammates; each teammate
|
|
9
|
+
owns a disjoint scope; results are synthesized and committed.
|
|
10
|
+
|
|
11
|
+
## When to Use `/team`
|
|
12
|
+
|
|
13
|
+
Use `/team` for any non-trivial mission that benefits from **parallel
|
|
14
|
+
work + explicit coordination**. Examples:
|
|
15
|
+
|
|
16
|
+
- **Refactor** — split a module across Thor (M2.7) and Tyr (M3) in parallel.
|
|
17
|
+
- **Multi-feature build** — new feature A (Thor) + new feature B (Tyr) + tests (Heimdall).
|
|
18
|
+
- **Research + implementation** — Mimir researches, Thor implements, Forseti audits.
|
|
19
|
+
- **Migration / upgrade** — Hermod handles the git/PR lifecycle while Thor/Tyr write code.
|
|
20
|
+
- **Bug hunt** — Mimir traces the data flow while Tyr writes the fix and tests.
|
|
21
|
+
|
|
22
|
+
Do NOT use `/team` for trivial single-file edits or single-question
|
|
23
|
+
clarifications. Use `@heimdall` directly for those.
|
|
24
|
+
|
|
25
|
+
## Default Team Composition
|
|
26
|
+
|
|
27
|
+
If the user does not specify a team composition, spawn this default:
|
|
28
|
+
|
|
29
|
+
| Role | Agent | Model | Job |
|
|
30
|
+
|---------------|---------|-------------------|---------------------------------------|
|
|
31
|
+
| Lead | @odin | minimax/MiniMax-M3 | Coordinate, synthesize, gate quality |
|
|
32
|
+
| Implementer 1 | @thor | minimax/MiniMax-M2.7 | Moderate-complexity implementation |
|
|
33
|
+
| Implementer 2 | @tyr | minimax/MiniMax-M3 | Complex / cross-cutting work |
|
|
34
|
+
| Researcher | @mimir | 9router/kr/auto | Codebase research, pattern discovery |
|
|
35
|
+
| Git ops | @hermod | minimax/MiniMax-M2.7 | Branch, commit, push, PR |
|
|
36
|
+
| Reviewer | @forseti| minimax/MiniMax-M3 | Audit plan + final output |
|
|
37
|
+
|
|
38
|
+
You can swap or remove any of these — but always keep at least one
|
|
39
|
+
implementer and a reviewer.
|
|
40
|
+
|
|
41
|
+
## Protocol (How Odin Runs a `/team` Invocation)
|
|
42
|
+
|
|
43
|
+
1. **Read `.bizar/PROJECT.md`** if it exists. Factor the project
|
|
44
|
+
description into the team mission.
|
|
45
|
+
2. **Read `bizar memory status`**. If the vault is reachable, run
|
|
46
|
+
`bizar memory search "<topic>"` for prior context on the mission.
|
|
47
|
+
3. **Decompose** the mission into disjoint work items. Each item
|
|
48
|
+
names:
|
|
49
|
+
- The agent that owns it
|
|
50
|
+
- The file scope (paths/globs)
|
|
51
|
+
- The deliverable (a function, a test, a commit, a doc)
|
|
52
|
+
- The exit signal ("done when X is committed")
|
|
53
|
+
4. **Launch in parallel** — use `bizar_spawn_team` with the mission
|
|
54
|
+
string. Or, if you prefer synchronous control, launch 2+ `task`
|
|
55
|
+
calls in a **single message** to drive Thor and Tyr directly.
|
|
56
|
+
5. **Monitor** — while the team runs, return control to the user.
|
|
57
|
+
The team runs asynchronously. Use `bizar_status` to check progress
|
|
58
|
+
only when the user asks.
|
|
59
|
+
6. **Synthesize** — when teammates report back, gate through @forseti
|
|
60
|
+
(audit) and @hermod (commit/push). Then report the final outcome
|
|
61
|
+
to the user with file:line references.
|
|
62
|
+
|
|
63
|
+
## Pre-Dispatch Checklist (MANDATORY)
|
|
64
|
+
|
|
65
|
+
- [ ] Each teammate has a **disjoint file scope** — no two agents
|
|
66
|
+
edit the same file or directory
|
|
67
|
+
- [ ] Lockfiles, `package.json`, root configs, `tsconfig.json`,
|
|
68
|
+
`vite.config.*`, `Dockerfile`, CI configs are assigned to ONE
|
|
69
|
+
agent or marked READ-ONLY for everyone else
|
|
70
|
+
- [ ] No subagent has both `bash: allow` AND a write-level git task
|
|
71
|
+
in the same batch — Hermod is the only git writer
|
|
72
|
+
- [ ] Each teammate's scope is named in plain English
|
|
73
|
+
- [ ] You have prepended the **Sibling-Awareness Block** to every
|
|
74
|
+
prompt — see Odin baseline, "Parallel Dispatch Coordination"
|
|
75
|
+
- [ ] You have called `bizar memory status` and confirmed the vault
|
|
76
|
+
is reachable
|
|
77
|
+
|
|
78
|
+
## Example Mission Strings
|
|
79
|
+
|
|
80
|
+
**Refactor a module:**
|
|
81
|
+
```
|
|
82
|
+
Refactor src/api/auth.ts into a clean module: extract JWT validation
|
|
83
|
+
into src/auth/jwt.ts, rate-limiting into src/auth/rate-limit.ts, and
|
|
84
|
+
keep the public surface stable. Add tests for each new module. Update
|
|
85
|
+
docs/architecture.md with the new module map. Do not change the HTTP
|
|
86
|
+
route handlers.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Multi-feature build:**
|
|
90
|
+
```
|
|
91
|
+
Add a kanban board: (1) Thor — backend API at /api/tasks with CRUD
|
|
92
|
+
endpoints and SQLite persistence. (2) Tyr — frontend React component
|
|
93
|
+
in src/components/Kanban.tsx with 5 columns. (3) Heimdall — write
|
|
94
|
+
the OpenAPI spec at docs/api/tasks.yaml. (4) Hermod — open a PR
|
|
95
|
+
named feat/kanban-board.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Bug hunt:**
|
|
99
|
+
```
|
|
100
|
+
Mimir — trace the data flow from POST /api/checkout through to the
|
|
101
|
+
Stripe webhook handler; find any place where the order status is
|
|
102
|
+
written before the payment is confirmed. Tyr — write a regression
|
|
103
|
+
test that reproduces the race. Thor — fix the bug. Forseti —
|
|
104
|
+
audit the fix for completeness. Hermod — commit + push.
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Tool Reference
|
|
108
|
+
|
|
109
|
+
- `bizar_spawn_team` — spawn a full team in one call (lead + mission)
|
|
110
|
+
- `bizar_spawn_background` — spawn a single background agent
|
|
111
|
+
- `bizar_status` — list all running background instances
|
|
112
|
+
- `bizar_collect` — block until an instance completes (use sparingly)
|
|
113
|
+
- `bizar_kill` — terminate an instance
|
|
114
|
+
|
|
115
|
+
## Failure Modes
|
|
116
|
+
|
|
117
|
+
- **Two agents write the same file** — STOP, revert one branch, reassign.
|
|
118
|
+
- **One agent stalls** — call `bizar_kill`, route the remaining scope
|
|
119
|
+
to another agent.
|
|
120
|
+
- **Tests fail after team work** — dispatch @thor to investigate; do
|
|
121
|
+
not paper over the failure.
|
|
122
|
+
- **Git conflict** — route to @hermod for `git rebase` / merge resolution.
|
|
123
|
+
- **A teammate goes silent** — call `bizar_status` for a status report;
|
|
124
|
+
if dead, kill and re-spawn with a fresh prompt.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run the project's test suite via the Bizar test gate. Auto-detects jest/vitest/bun/pytest/cargo/go. Exits non-zero on failure.
|
|
3
|
+
agent: thor
|
|
4
|
+
---
|
|
5
|
+
# /test — Run the Project's Test Suite
|
|
6
|
+
|
|
7
|
+
The `/test` command is a thin wrapper around `bizar test-gate`. It
|
|
8
|
+
auto-detects the project's test runner and runs the full suite.
|
|
9
|
+
|
|
10
|
+
## What It Does
|
|
11
|
+
|
|
12
|
+
1. Detects the project stack (one of: npm, pytest, cargo, go).
|
|
13
|
+
2. Runs the matching test command:
|
|
14
|
+
- `package.json` present → `npm test`
|
|
15
|
+
- `pyproject.toml` present → `pytest`
|
|
16
|
+
- `Cargo.toml` present → `cargo test`
|
|
17
|
+
- `go.mod` present → `go test ./...`
|
|
18
|
+
3. Streams the test output to the user.
|
|
19
|
+
4. Exits non-zero on test failures.
|
|
20
|
+
|
|
21
|
+
## Exit Codes
|
|
22
|
+
|
|
23
|
+
| Code | Meaning |
|
|
24
|
+
|------|----------------------------------|
|
|
25
|
+
| 0 | All tests passed |
|
|
26
|
+
| 1 | Tests failed |
|
|
27
|
+
| 2 | No test runner detected |
|
|
28
|
+
|
|
29
|
+
## How to Use
|
|
30
|
+
|
|
31
|
+
Invoke `/test` (no arguments) from the project root. The test gate
|
|
32
|
+
inspects the current directory and picks the right runner.
|
|
33
|
+
|
|
34
|
+
To run a specific Bizar test scope instead, you can pass arguments
|
|
35
|
+
to `/test`. The arguments are forwarded to the underlying runner:
|
|
36
|
+
|
|
37
|
+
- `/test plugins/bizar` — run only the Bizar plugin tests (bun)
|
|
38
|
+
- `/test --testPathPattern=auth` — pass a flag to the runner
|
|
39
|
+
|
|
40
|
+
## When to Use
|
|
41
|
+
|
|
42
|
+
- After any non-trivial code change
|
|
43
|
+
- Before committing (`@hermod` should always run `/test` first)
|
|
44
|
+
- As the final step of a `/team` mission — gate quality with tests
|
|
45
|
+
|
|
46
|
+
## Common Patterns
|
|
47
|
+
|
|
48
|
+
- **Post-implementation gate** — after Thor and Tyr finish their
|
|
49
|
+
parallel work, dispatch `@thor` to run `/test`. If failures, fix
|
|
50
|
+
and re-run.
|
|
51
|
+
- **PR check** — run `/test` in CI before merging.
|
|
52
|
+
- **Debug a flaky test** — run `/test` twice; if the second run
|
|
53
|
+
passes, the test is order-dependent (fix or quarantine).
|
|
54
|
+
|
|
55
|
+
## Related
|
|
56
|
+
|
|
57
|
+
- `bizar test-gate` — the underlying CLI command
|
|
58
|
+
- `bizar doctor` — checks the Bizar install itself (not project tests)
|
|
59
|
+
- `make check` — typecheck + tests (full pipeline, dev only)
|
|
60
|
+
- `make e2e` — Bizar plugin end-to-end (dev only)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Validate the Bizar install end-to-end — cline.json parses, plugin entry resolves, all 14 agent files are installed, all skills/rules/hooks/commands are in place, provider config is sane, and the 9router gateway is reachable.
|
|
3
|
+
agent: heimdall
|
|
4
|
+
---
|
|
5
|
+
# /validate — Validate the Bizar Install
|
|
6
|
+
|
|
7
|
+
The `/validate` command runs `bizar doctor` with the full check
|
|
8
|
+
battery. It is the fastest way to confirm the install is healthy
|
|
9
|
+
and Cline will see every Bizar component on the next session start.
|
|
10
|
+
|
|
11
|
+
## What It Checks
|
|
12
|
+
|
|
13
|
+
1. **cline-reachable** — `cline --version` exits 0.
|
|
14
|
+
2. **cline-config-valid** — `~/.cline/cline.json` parses as JSON.
|
|
15
|
+
3. **plugin-entry-present** — `cline.json` has a Bizar plugin entry.
|
|
16
|
+
4. **plugin-path-resolves** — the plugin path on disk actually exists.
|
|
17
|
+
5. **agent-files-installed** — all 14 agent files in `~/.cline/agents/`
|
|
18
|
+
(odin, vor, frigg, quick, mimir, heimdall, hermod, thor, baldr,
|
|
19
|
+
tyr, vidarr, forseti, semble-search, agent-browser).
|
|
20
|
+
6. **tools-available** — at least one of `headroom`, `semble`, `skills`
|
|
21
|
+
is on PATH.
|
|
22
|
+
7. **provider-config-sanity** — `provider.9router` (preferred) or
|
|
23
|
+
`provider.minimax` (legacy) has at least one sane model entry.
|
|
24
|
+
8. **9router-reachable** — 9Router gateway responds at
|
|
25
|
+
`http://localhost:20128/api/health` (lenient — warn, don't fail).
|
|
26
|
+
9. **slash-commands-installed** — all Bizar slash commands
|
|
27
|
+
(`/audit`, `/explain`, `/init`, `/learn`, `/plan`, `/plow-through`,
|
|
28
|
+
`/pr-review`, `/tailscale-serve`, `/visual-plan`, `/bizar`,
|
|
29
|
+
`/team`, `/test`, `/validate`) are present in `~/.cline/commands/`.
|
|
30
|
+
10. **skills-installed** — every subdir of `config/skills/` is
|
|
31
|
+
mirrored to `~/.cline/skills/`.
|
|
32
|
+
11. **rules-installed** — every `.md` in `config/rules/` is mirrored
|
|
33
|
+
to `~/.cline/rules/`.
|
|
34
|
+
12. **hooks-installed** — `config/hooks/` is mirrored to
|
|
35
|
+
`~/.cline/hooks/`.
|
|
36
|
+
|
|
37
|
+
## How to Use
|
|
38
|
+
|
|
39
|
+
Invoke `/validate` (no arguments) from anywhere. It is read-only and
|
|
40
|
+
never modifies the system.
|
|
41
|
+
|
|
42
|
+
If any check fails, the user should run `bizar update` to refresh the
|
|
43
|
+
install, or `bizar repair` to fix common issues.
|
|
44
|
+
|
|
45
|
+
## Output
|
|
46
|
+
|
|
47
|
+
Each check prints ✓ or ✗ with a one-line message. The summary line
|
|
48
|
+
shows the total pass/fail count. Exits non-zero if any check fails.
|
|
49
|
+
|
|
50
|
+
For machine-readable output, the user can run `bizar doctor --json`
|
|
51
|
+
directly.
|
|
52
|
+
|
|
53
|
+
## When to Use
|
|
54
|
+
|
|
55
|
+
- After a manual config edit — confirm nothing broke.
|
|
56
|
+
- Before a `/team` mission — confirm the install is healthy so the
|
|
57
|
+
team has all the tools it needs.
|
|
58
|
+
- After `bizar install` or `bizar update` — the install already
|
|
59
|
+
calls doctor, but a manual re-run is cheap.
|
|
60
|
+
- When a user asks "why is cline misbehaving?" — start here.
|
|
61
|
+
|
|
62
|
+
## Related
|
|
63
|
+
|
|
64
|
+
- `bizar doctor` — the underlying CLI command
|
|
65
|
+
- `bizar update` — refreshes the install (re-runs syncConfigExtras)
|
|
66
|
+
- `bizar repair` — fixes common issues (stale symlinks, version drift)
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// UserPromptSubmit — Bizar harness hook.
|
|
3
|
+
//
|
|
4
|
+
// Runs when the user submits a prompt. Reads JSON from stdin, outputs
|
|
5
|
+
// JSON to stdout. Used to lightly tag user prompts for routing and to
|
|
6
|
+
// add a "Plow Through" prompt hint when ambiguity is detected.
|
|
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 submit = input.userPromptSubmit || {};
|
|
21
|
+
const prompt = String(submit.prompt || '').trim();
|
|
22
|
+
|
|
23
|
+
// Always log (no PII redaction needed — the prompt is the user's).
|
|
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, `user-prompt-${today}.jsonl`);
|
|
29
|
+
fs.appendFileSync(logFile, JSON.stringify({
|
|
30
|
+
ts: new Date().toISOString(),
|
|
31
|
+
taskId: input.taskId || null,
|
|
32
|
+
promptLength: prompt.length,
|
|
33
|
+
}) + '\n');
|
|
34
|
+
} catch { /* best-effort */ }
|
|
35
|
+
|
|
36
|
+
const notes = [];
|
|
37
|
+
if (prompt.length === 0) {
|
|
38
|
+
notes.push('Bizar UserPromptSubmit: empty prompt — wait for actual user input.');
|
|
39
|
+
} else if (prompt.startsWith('/team')) {
|
|
40
|
+
notes.push('Bizar UserPromptSubmit: /team — Odin will spawn a coordinated agent team. Confirm disjoint file scopes before dispatching.');
|
|
41
|
+
} else if (prompt.startsWith('/plow-through')) {
|
|
42
|
+
notes.push('Bizar UserPromptSubmit: /plow-through — autonomous mode. Dispatch 2+ parallel agents when possible. Run /test before claiming done.');
|
|
43
|
+
} else if (prompt.startsWith('/test')) {
|
|
44
|
+
notes.push('Bizar UserPromptSubmit: /test — auto-detects runner (jest/vitest/bun/pytest/cargo/go). Streams output to the user.');
|
|
45
|
+
} else if (prompt.startsWith('/validate')) {
|
|
46
|
+
notes.push('Bizar UserPromptSubmit: /validate — runs 21-point health check on the Bizar install.');
|
|
47
|
+
} else {
|
|
48
|
+
notes.push('Bizar UserPromptSubmit: if the request is complex, decompose into 2+ parallel subagent tasks (Odin) or spawn a /team.');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
process.stdout.write(JSON.stringify({
|
|
52
|
+
cancel: false,
|
|
53
|
+
contextModification: notes.join(' '),
|
|
54
|
+
}) + '\n');
|
|
55
|
+
});
|