brindle 0.0.6__tar.gz → 0.2.0__tar.gz
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.
- {brindle-0.0.6 → brindle-0.2.0}/PKG-INFO +343 -15
- {brindle-0.0.6 → brindle-0.2.0}/README.md +342 -14
- {brindle-0.0.6 → brindle-0.2.0}/pyproject.toml +7 -2
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/agents.py +199 -27
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/autopilot.py +63 -9
- brindle-0.2.0/src/brindle/budget.py +374 -0
- brindle-0.2.0/src/brindle/builtin_agents/backend-auditor.md +12 -0
- brindle-0.2.0/src/brindle/builtin_agents/qa.md +9 -0
- brindle-0.2.0/src/brindle/builtin_rules/__init__.py +1 -0
- brindle-0.2.0/src/brindle/builtin_rules/security/backend.md +24 -0
- brindle-0.2.0/src/brindle/builtin_rules/style/minimal-diff.md +10 -0
- brindle-0.2.0/src/brindle/builtin_rules/tests/only.md +12 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/ci_adapters.py +3 -2
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/ci_client.py +152 -28
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/ci_federation.py +5 -0
- brindle-0.2.0/src/brindle/ci_hosts.py +313 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/cli.py +601 -23
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/config.py +32 -6
- brindle-0.2.0/src/brindle/conflicts.py +211 -0
- brindle-0.2.0/src/brindle/cost.py +422 -0
- brindle-0.2.0/src/brindle/cost_estimate.py +432 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/cull.py +24 -2
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/db.py +164 -4
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/doctor.py +17 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/events.py +62 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/gates.py +28 -6
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/git.py +18 -2
- brindle-0.2.0/src/brindle/guardrails.py +244 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/history.py +3 -2
- brindle-0.2.0/src/brindle/learned_rules.py +614 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/mcp_server.py +135 -10
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/native/runner.py +3 -1
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/native/tools.py +37 -5
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pipeline.py +289 -45
- brindle-0.2.0/src/brindle/pricing.py +230 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/account.py +102 -8
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/audit_chain.py +100 -15
- brindle-0.2.0/src/brindle/pro/audit_export.py +822 -0
- brindle-0.2.0/src/brindle/pro/cost_centers.py +225 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/learning.py +21 -4
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/license.py +9 -3
- brindle-0.2.0/src/brindle/pro/managed_models.py +218 -0
- brindle-0.2.0/src/brindle/pro/org_profiles.py +350 -0
- brindle-0.2.0/src/brindle/pro/rollout.py +227 -0
- brindle-0.2.0/src/brindle/pro/status.py +591 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/team_events.py +73 -8
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/team_policy.py +245 -10
- brindle-0.2.0/src/brindle/profiles.py +625 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/providers.py +19 -1
- brindle-0.2.0/src/brindle/rewind.py +447 -0
- brindle-0.2.0/src/brindle/rule_checks.py +502 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/savings.py +139 -20
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/scratch.py +34 -2
- brindle-0.2.0/src/brindle/secrets.py +191 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/tasks.py +161 -20
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/tmux.py +92 -13
- brindle-0.2.0/src/brindle/usage.py +394 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/watch.py +83 -9
- brindle-0.2.0/src/brindle/watch_costs.py +298 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/workspaces.py +61 -0
- brindle-0.0.6/src/brindle/profiles.py +0 -248
- brindle-0.0.6/src/brindle/usage.py +0 -205
- {brindle-0.0.6 → brindle-0.2.0}/.gitignore +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/LICENSE +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/SCHEDULE-A +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/__init__.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/__main__.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/account.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/airgap.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/antigravity.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/__init__.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/developer-codex.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/developer-heavy.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/developer-local.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/developer.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/reviewer-local.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/reviewer.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/subagent.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/builtin_agents/supervisor.md +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/codemap.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/codex_hook.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/demo.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/detect.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/inbox.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/learning.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/legacy.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/native/__init__.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/native/client.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/native/loop.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/native/permissions.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/native/serve.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/permissions.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/plugins.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/policy.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pool.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/__init__.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/_files.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/auth.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/credentials.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/features.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/keys.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/loopback.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/orgkey.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/pro/settings_sync.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/procs.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/quota.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/repo_cmds.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/repos.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/services.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/sessions.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/sidebar_follow.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/status_cache.py +0 -0
- {brindle-0.0.6 → brindle-0.2.0}/src/brindle/view.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brindle
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Run coding agents in parallel, each on its own git branch, with a supervisor that delegates, reviews, and merges
|
|
5
5
|
Project-URL: Homepage, https://pawdelta.com/brindle/
|
|
6
6
|
Project-URL: Documentation, https://pawdelta.com/brindle/docs/recommended-use
|
|
@@ -89,7 +89,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
|
|
|
89
89
|
brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
|
|
90
90
|
|
|
91
91
|
`brindle --version` prints the installed version; the tmux status bar of every brindle
|
|
92
|
-
session shows it too (`brindle 0.0
|
|
92
|
+
session shows it too (`brindle 0.2.0`). A session started before an upgrade keeps
|
|
93
93
|
running the old code, and shows the old number, until you restart it.
|
|
94
94
|
|
|
95
95
|
## Quick start
|
|
@@ -257,8 +257,8 @@ or put the goal in `.brindle/goals.md`, and it works like a project manager:
|
|
|
257
257
|
Claude workers run the task as a Claude Code `/goal` with a finish line, so
|
|
258
258
|
they keep going until it's met. `assign`/`handoff` take `files` (the
|
|
259
259
|
paths/globs a task expects to touch, checked for overlap against other
|
|
260
|
-
active workers' declared and actually-changed files
|
|
261
|
-
|
|
260
|
+
active workers' declared and actually-changed files; refused by default,
|
|
261
|
+
a warning with `"overlap": "warn"`) and `depends_on` (earlier tasks, by agent id or branch, that must
|
|
262
262
|
merge first): a task with unmet dependencies is queued instead of started,
|
|
263
263
|
and starts automatically, cut from the updated base, once
|
|
264
264
|
`merge_workspace` resolves them. `list_tasks` shows what's queued.
|
|
@@ -351,12 +351,17 @@ your own status line prints, so what you see doesn't change.
|
|
|
351
351
|
| `brindle ls [--all]` | workspaces and agents |
|
|
352
352
|
| `brindle repo add PATH [--name ALIAS] / rm ALIAS / ls` | attach other local repos to the current session so workers can go there (brindle Pro; see "Several repos in one session") |
|
|
353
353
|
| `brindle history [--limit N] [--kind K] [--all]` | durable log of worker results, reviews, merges and milestone checks |
|
|
354
|
+
| `brindle cost [--days N] [--all]` | dollar spend at list prices over the last 30 days, by model |
|
|
355
|
+
| `brindle cost report [--days N] [--all]` | Pro: spend by day, profile and goal, cost per merged branch, review pass rate per worker profile |
|
|
354
356
|
| `brindle history --share [--session ID]` | a few lines about this session to paste into Slack or a post: goal, milestones verified, workers, merges, reviews (and how many by a different model), parallel speedup, tokens |
|
|
355
357
|
| `brindle permissions list / check / suggestions / accept / allow / deny / forget / reset` | the rules brindle answers workers' permission requests with, and what it suggests from your approvals (see "Permission policy") |
|
|
356
358
|
| `brindle permissions install-codex-hook [--yes]` / `brindle permissions sync-agy` | trust brindle's Codex permission hook now (brindle does it itself when needed); copy your rules into Antigravity's settings (see "Permission policy") |
|
|
359
|
+
| `brindle cost request --usd N --reason "..." [--goal TEXT]` | Enterprise (cost centers): ask an admin to approve spend over your budget. A budget refusal points here. Once approved, this month's limit (or the named goal's) goes up by the approved amount, for the month it was approved in |
|
|
360
|
+
| `brindle cost requests` | Enterprise: your approval requests and their status; checks pending ones with the server (the cull pass also does, every few minutes). With cost centers, spend events also carry the repo (`owner/name`) so the org can total spend per center |
|
|
361
|
+
| `brindle cost estimate [--tasks N] [--weight W] [--profile P] [--reviewer R]` | what the current goal (or N tasks) is likely to cost, as a range from this repo's history (median to p80 of past worker and review runs), plus a cheaper alternative with a different reviewer; says "low confidence" when there's too little history. Also added to `set_goal` and `assign` replies (brindle Pro) |
|
|
357
362
|
| `brindle learning` | whether brindle Pro's hosted learning is on for this repo, and if not, why (nothing is learned on your machine) |
|
|
358
363
|
| `brindle account [login\|logout\|status\|upgrade\|portal\|org]` | paid features: bare `brindle account` shows what your plan has and how to get the rest (see "brindle Pro and Team" below) |
|
|
359
|
-
| `brindle audit verify\|export\|pubkey` | the local tamper-evident audit log (brindle Enterprise; see "Audit log" below) |
|
|
364
|
+
| `brindle audit verify\|export\|pubkey\|ship\|prune` | the local tamper-evident audit log, and shipping it to your SIEM (brindle Enterprise; see "Audit log" below) |
|
|
360
365
|
| `brindle watch [--all] [--once]` | the dashboard on its own (the same view as the sidebar): enter attaches, `p` peeks, `x` closes |
|
|
361
366
|
| `brindle sidebar` | bring this session's sidebar into the tmux session you're in (also `Ctrl-b S` in a window without one); restarts it if it was closed |
|
|
362
367
|
| `brindle attach / cd / open [WS]` | tmux session (at the agent waiting on you, else the busiest or newest) / path / editor |
|
|
@@ -369,6 +374,9 @@ your own status line prints, so what you see doesn't change.
|
|
|
369
374
|
| `brindle close AGENT` / `brindle close --exited` | hide an agent (or every stopped one) from the dashboard, stopping it if it's running; its worktree and branch stay |
|
|
370
375
|
| `brindle send AGENT MSG` | message an agent; waits in its inbox until it's idle |
|
|
371
376
|
| `brindle agent spawn/kill/peek/profiles` | manage agents |
|
|
377
|
+
| `brindle profile new/lint/show` | write a profile file, check every profile (extends chains, rule packs, patterns), or print one as brindle resolves it |
|
|
378
|
+
| `brindle rules suggest/accept/reject` | (brindle Pro) rules suggested from review findings that keep recurring; accept one into `.brindle/rules/learned.md`, or reject it for good |
|
|
379
|
+
| `brindle agent turns AGENT` / `brindle agent rewind AGENT --to N [--profile P] [--note "..."]` | a worker's worktree is snapshotted after every turn that changed files; rewind puts it back as it was after turn N and starts a fresh session there, briefed on the task, turns 1..N and your note (any provider, Codex included). Supervisors have the `agent_turns` and `rewind_agent` tools |
|
|
372
380
|
| `brindle setup [WS]` | re-run the repo's setup commands in a workspace |
|
|
373
381
|
| `brindle mcp` | the MCP server agents talk to (launched for them; you don't run it) |
|
|
374
382
|
|
|
@@ -381,12 +389,13 @@ knowing them helps when you tell the supervisor how to work.
|
|
|
381
389
|
|
|
382
390
|
| Tool | Used by | |
|
|
383
391
|
|---|---|---|
|
|
384
|
-
| `assign` / `handoff` / `wait_for_worker` | supervisor | start a worker (return now / wait for its result / keep waiting) |
|
|
392
|
+
| `assign` / `handoff` / `wait_for_worker` | supervisor | start a worker (return now / wait for its result / keep waiting); `dry_run=true` on `assign`/`handoff` starts nothing and reports the profile, what each `files` glob matches, overlaps and whether it would queue |
|
|
385
393
|
| `send_message` | any agent | message another agent; delivered when it's idle |
|
|
386
394
|
| `read_messages` | supervisor | read the messages agents and brindle sent you and mark them read; with `message_delivery` `"pull"` (the default) you get a one-line "N new messages" notice instead of each message |
|
|
387
|
-
| `list_agents` / `list_tasks` / `list_agent_profiles` | supervisor | who's running, what's queued, which profiles exist |
|
|
395
|
+
| `list_agents` / `list_tasks` / `list_agent_profiles` | supervisor | who's running, what's queued, which profiles exist; `list_tasks(full=true)` also shows each task's brief and `done_when` |
|
|
388
396
|
| `list_repos` | supervisor | the repos attached to the session (brindle Pro); `repo=<alias>` on `assign`/`handoff` puts a worker there |
|
|
389
397
|
| `cancel_task` | supervisor | cancel a queued task (and its dependents) to re-plan |
|
|
398
|
+
| `requeue` | supervisor | queue a cancelled task again with the same brief, files and profile; `depends_on` replaces its dependencies |
|
|
390
399
|
| `workspace_diff` | supervisor | a worker branch's changes against its base |
|
|
391
400
|
| `request_review` / `submit_review` | supervisor / reviewer | start a reviewer on a branch / record its verdict |
|
|
392
401
|
| `merge_workspace` / `remove_workspace` | supervisor | merge through the gates / delete the worktree |
|
|
@@ -420,6 +429,51 @@ capped at 5000 rows per repo, oldest dropped first. Recording usage or
|
|
|
420
429
|
history never blocks a report, merge or check: a failure there is logged and
|
|
421
430
|
skipped.
|
|
422
431
|
|
|
432
|
+
Codex agents' usage comes from Codex's own session rollouts under
|
|
433
|
+
`~/.codex/sessions/`; agents on other providers (Antigravity) have none, and
|
|
434
|
+
count as unknown.
|
|
435
|
+
|
|
436
|
+
### Spend in dollars
|
|
437
|
+
|
|
438
|
+
`brindle cost` prices the last 30 days of history at each model's list price
|
|
439
|
+
(per million tokens: input, output, cache write, cache read), copied from
|
|
440
|
+
Anthropic's and OpenAI's pricing pages and dated; brindle warns when they're
|
|
441
|
+
more than 90 days old. Local models cost $0, and usage on a model known to be
|
|
442
|
+
free reads "Free" (its tokens still count; a mix reads "$1.20 (+40k tokens
|
|
443
|
+
free)"). A model brindle has no price for is shown as unpriced tokens, never
|
|
444
|
+
guessed. Add or correct prices under
|
|
445
|
+
`pricing` in `~/.brindle/config.json` or `.brindle/config.json`:
|
|
446
|
+
|
|
447
|
+
```json
|
|
448
|
+
"pricing": {"my-model": {"input": 1, "output": 5, "cache_write": 1.25, "cache_read": 0.1}}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
These are list prices: a subscription or a discount makes the real bill
|
|
452
|
+
lower. `brindle cost report` (Pro) breaks spend down by day, profile and goal,
|
|
453
|
+
with the cost per merged branch and each worker profile's review pass rate,
|
|
454
|
+
and a savings section: the cache-hit rate, what routing to cheaper profiles
|
|
455
|
+
saved against running the same tokens on the most expensive profile in your
|
|
456
|
+
routing, and the spend budgets avoided.
|
|
457
|
+
Forwarded results add the dollar figure to their token line, e.g.
|
|
458
|
+
`tokens: 182k in (160k cached, 20k written) · 9k out · sonnet · ~$0.18`.
|
|
459
|
+
|
|
460
|
+
### Budgets (Pro)
|
|
461
|
+
|
|
462
|
+
```json
|
|
463
|
+
"budget": {"task_usd": 5, "goal_usd": 40, "month_usd": 200, "stop": false}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Set in `~/.brindle/config.json`, `.brindle/config.json` or `config.local.json`
|
|
467
|
+
(merged per key, the local file last). When a task routed by `weight` would
|
|
468
|
+
take the task, its goal or the month over a limit (a task's cost is estimated
|
|
469
|
+
from this repo's finished tasks on that profile, else a typical token mix at
|
|
470
|
+
list prices), brindle skips that candidate and uses the next cheaper one. If
|
|
471
|
+
none fits it refuses the assign, says why and, in autopilot, asks you. A
|
|
472
|
+
profile you name yourself is never second-guessed. Running workers are
|
|
473
|
+
reported to the supervisor at 80% of `task_usd` and again at 100%; set
|
|
474
|
+
`"stop": true` to close a worker at 100% instead of only warning. Without the
|
|
475
|
+
Pro `cost` feature budgets do nothing.
|
|
476
|
+
|
|
423
477
|
## Provider quota
|
|
424
478
|
|
|
425
479
|
brindle keeps one place (`~/.brindle/quota.json`) that knows how close each
|
|
@@ -484,9 +538,10 @@ Autopilot, merge gates and cleanup:
|
|
|
484
538
|
| `rules` | `[]` | standing rules for the supervisor, e.g. `["Fix review findings without asking", "Validate options before offering them"]`; added to every supervisor's brief. Rules in `~/.brindle/config.json`, the repo's config and `config.local.json` are all kept, so a team's rules and your own add up |
|
|
485
539
|
| `plan_first` | `false` | workers propose a plan (`submit_plan`) and wait for `approve_plan` before editing |
|
|
486
540
|
| `overlap` | `"block"` | a task whose `files` overlap a running task's is refused (`"warn"` starts it with a warning) |
|
|
541
|
+
| `protected_paths` | `[]` | paths or globs (`"migrations/"`, `"*.lock"`) whose merge conflicts brindle never hands to a worker to resolve: the branch waits for you instead (see "Conflict-aware merging") |
|
|
487
542
|
| `pool_size` | `1` if `setup` is set, else `0` | pre-built worktrees (checked out, files copied, setup run) kept ready so a new worker doesn't wait on `setup`; `0` disables it |
|
|
488
543
|
| `add_dirs` | `[]` | directories outside the worktree that Claude Code agents may use (`--add-dir`; full tool access, see "Directories outside the workspace") |
|
|
489
|
-
| `local_models` | `
|
|
544
|
+
| `local_models` | `true` | When a native profile points at Ollama on this machine and it isn't running, `brindle` starts `ollama serve` in the background (with the context length the profiles need) and loads their models, then stops it when the last session closes. `false`: brindle uses a server that's already running and never starts or preloads one |
|
|
490
545
|
| `sidebar` | `"left"` | where the dashboard sits in each window: `"left"` of the chat, or `"bottom"` (full-width rows under it) |
|
|
491
546
|
| `delegation` | `"balanced"` | how readily the supervisor hands work to workers. `"conservative"` does most work in its own chat (fewest tokens), `"fast"` splits any multi-part request across parallel workers straight away (quickest, most tokens). `brindle delegation fast` saves it for every repo and session (in `~/.brindle/config.json`; `--repo` for this repo only) and tells a running supervisor |
|
|
492
547
|
| `delete_merged_branches` | `true` | removing a worktree (after a merge, `brindle rm`, `brindle prune`, session cleanup) also deletes its branch once every commit is in its base, so finished branches don't pile up. An unmerged branch is always kept; `false` keeps them all. If GitHub keeps merged PR branches, the first `brindle pr` in a repo offers to turn on its automatic deletion with your `gh` login (repo admins only) |
|
|
@@ -494,6 +549,8 @@ Autopilot, merge gates and cleanup:
|
|
|
494
549
|
| `message_delivery` | `"pull"` | how agent and brindle messages reach an interactive supervisor: `"pull"` keeps them unread and delivers one notice ("brindle (16:25:03): 2 new messages (from 9f742c5c, pipeline). Call read_messages."; the time keeps Claude Code from dropping a repeat; the sidebar shows an unread count), `"push"` delivers each message's text. Messages you send (`brindle send`, typing) and messages to workers are always pushed |
|
|
495
550
|
| `learning` | `"auto"` | hosted learning (brindle Pro, via the API; see below): `"auto"` uses it when your plan includes it and nothing otherwise; `"cloud"`; `"off"`. Any other value means off |
|
|
496
551
|
| `learning_candidates` | `[]` | the profile names the hosted learner may pick from |
|
|
552
|
+
| `learned_rules_repeats` | `3` | learned rules (brindle Pro): how many tasks of one profile's work must get the same review finding before a rule is suggested |
|
|
553
|
+
| `learned_rules_profile` | none | the profile that groups review findings for learned rules (a `native` or `claude` one); unset, a local model that answers, else Claude's cheapest model |
|
|
497
554
|
| `plugins` | `{}` | which installed plugin to use per group, e.g. `{"events": "<name>", "policy": "off"}`; unset, a group uses the only plugin installed in it, except `events`, which uses every installed one (several names: `"pro, audit"`; see "Plugins" below) |
|
|
498
555
|
| `routing` | see below | for each task weight (`light`, `medium`, `heavy`), the profiles `assign`/`handoff` try in order |
|
|
499
556
|
| `services` | `[]` | per-worktree Docker services (brindle Pro; see "Per-worktree services" below) |
|
|
@@ -679,9 +736,24 @@ how to use them, and the next step to get the rest:
|
|
|
679
736
|
| hosted learning | Pro | on by itself; `brindle learning` |
|
|
680
737
|
| per-worktree services | Pro | `"services"` in `.brindle/config.json` |
|
|
681
738
|
| several repos in one session | Pro | `brindle repo add <path>` |
|
|
739
|
+
| settings sync across machines | Pro | `brindle account sync` |
|
|
740
|
+
| Brindle-CI build fixing (the fix workflow) | Pro | `brindle ci init` |
|
|
741
|
+
| learned rules from repeated review findings | Pro | `brindle rules suggest` |
|
|
742
|
+
| cost (estimates, budgets, reports) | Pro | `brindle cost report` |
|
|
743
|
+
| guardrails (write scope, read scope, env allowlist per profile) | Pro | `write_scope`, `read_scope`, `env_allow` in a profile |
|
|
682
744
|
| org policies + audit feed | Team | `brindle account org policy` |
|
|
683
|
-
| Brindle-CI | Team | `brindle ci init` |
|
|
745
|
+
| Brindle-CI (fixes builds, issues into verified pull requests, validation) | Team | `brindle ci init` |
|
|
746
|
+
| org profile and rule-pack library, pinned so a repo can't loosen it | Team | `brindle account org profiles` |
|
|
747
|
+
| org budgets and protected paths | Team | `budget`, `protected_paths` in your org policy (`brindle account org policy`) |
|
|
748
|
+
| per-member budgets, pause, and admin messages (sidebar "Messages", the bottom bar, `brindle org messages`) | Team | `brindle org messages [--keep]` lists and marks read the notices your admins sent you; a pause refuses new workers and stops running ones |
|
|
684
749
|
| audit log, air-gap | Enterprise | `brindle audit verify`, `"airgap": true` |
|
|
750
|
+
| audit export | Enterprise | `"audit_export"` in `~/.brindle/config.json`, `brindle audit ship` |
|
|
751
|
+
| managed models | Enterprise | `provider_config`, `deny_personal_keys` in your org policy (see "Managed models" below) |
|
|
752
|
+
| wider CI, cost centers, managed rollout | Enterprise | `brindle ci init --host gitlab`, `brindle cost request`, and `min_version`, required packs and the kill switch in your org policy; admins can also shut down or throttle a member, role or org remotely (it arrives within about a minute) |
|
|
753
|
+
|
|
754
|
+
`brindle ci init` checks your plan before it changes anything: it refuses
|
|
755
|
+
without Brindle-CI on the org you're using, and on Pro it sets up only the fix
|
|
756
|
+
workflow.
|
|
685
757
|
|
|
686
758
|
```sh
|
|
687
759
|
brindle account # paid features: what you have, how to use them, how to get the rest
|
|
@@ -692,6 +764,9 @@ brindle account upgrade # opens the checkout for brindle Pro (and prints its
|
|
|
692
764
|
brindle account portal # opens the billing portal (invoices, seats, cancellation); --org ORG for a team org
|
|
693
765
|
brindle account org list # the orgs you belong to; `org use <id>` switches, `org policy` shows the current one
|
|
694
766
|
brindle account org policy # the org's policy, its per-role overrides, and the policy that applies to you
|
|
767
|
+
brindle account org profiles # the org's shared profiles and rule packs, and which are pinned
|
|
768
|
+
brindle account org profiles push <file> [--pack] [--pinned] [--name NAME] # publish a profile (--pack: a rule pack) to the org (admin)
|
|
769
|
+
brindle account org profiles rm <name> [--pack] # take one out of the org's library (admin)
|
|
695
770
|
brindle account org member policy-role <member> <role|none> # give a member a policy role (admin; Enterprise)
|
|
696
771
|
brindle account org company [link <org_id> | unlink] # link orgs you own into one company; learning is pooled only within it
|
|
697
772
|
```
|
|
@@ -733,6 +808,35 @@ key) that the server can't reverse. Never the task text, prompts, diffs,
|
|
|
733
808
|
file names, paths or branch names. See `src/brindle/pro/learning.py` and
|
|
734
809
|
`src/brindle/pro/team_events.py` for the exact payloads.
|
|
735
810
|
|
|
811
|
+
### Managed models (brindle Enterprise)
|
|
812
|
+
|
|
813
|
+
An org policy can pick the provider its workers run on and keep people's own
|
|
814
|
+
model keys out of their panes. Both fields are ignored without the Enterprise
|
|
815
|
+
entitlement.
|
|
816
|
+
|
|
817
|
+
* `provider_config`: `provider` (`bedrock`, `vertex`, `azure` or
|
|
818
|
+
`openai-compatible`), plus `region`, `model_ids` and `endpoint` as the
|
|
819
|
+
provider needs them. For `vertex` set `project` to the GCP project id
|
|
820
|
+
(brindle passes it to Claude Code as `ANTHROPIC_VERTEX_PROJECT_ID`; an older
|
|
821
|
+
policy that put the project id in `endpoint` still works). Every agent is
|
|
822
|
+
launched on that provider, and a profile that points somewhere else is
|
|
823
|
+
refused with a message saying why.
|
|
824
|
+
* `deny_personal_keys`: the personal model keys (`ANTHROPIC_API_KEY`,
|
|
825
|
+
`OPENAI_API_KEY`, ...) are taken out of every agent pane, and a profile that
|
|
826
|
+
sets one is refused.
|
|
827
|
+
|
|
828
|
+
Two things to know when you turn on `deny_personal_keys`:
|
|
829
|
+
|
|
830
|
+
* With an `openai-compatible` or `azure` provider, `OPENAI_API_KEY` is
|
|
831
|
+
stripped like the rest, so give the profile a non-personal key to use: set
|
|
832
|
+
`api_key_env` to the name of a variable that holds the org's key (not
|
|
833
|
+
`OPENAI_API_KEY`).
|
|
834
|
+
* It can't block a Claude subscription or keychain login by itself: Claude
|
|
835
|
+
Code signs in on its own, outside the variables brindle removes. Only a
|
|
836
|
+
`provider_config` that routes Claude Code to Bedrock, Vertex or Foundry
|
|
837
|
+
(`azure`) keeps it off that login. Always pair `deny_personal_keys` with a
|
|
838
|
+
`provider_config`.
|
|
839
|
+
|
|
736
840
|
### Air-gapped mode (brindle Enterprise)
|
|
737
841
|
|
|
738
842
|
For machines that must not talk to the internet at all, air-gap mode turns
|
|
@@ -815,9 +919,56 @@ one altered in place (hash or signature), one removed, inserted or reordered
|
|
|
815
919
|
(seq and prev_hash), or a truncated tail. Verifying and exporting never need
|
|
816
920
|
the entitlement, so a log keeps its value after a plan lapses.
|
|
817
921
|
|
|
922
|
+
#### Audit export (brindle Enterprise)
|
|
923
|
+
|
|
924
|
+
With a plan that includes `audit_export`, brindle ships the audit records to your
|
|
925
|
+
own logging stack and can prune old ones locally. Configure it under
|
|
926
|
+
`audit_export` in `~/.brindle/config.json`; secrets are never in the config, each
|
|
927
|
+
`*_env` names an environment variable that is read when sending:
|
|
928
|
+
|
|
929
|
+
```json
|
|
930
|
+
{"audit_export": {
|
|
931
|
+
"retention_days": 90,
|
|
932
|
+
"sinks": [
|
|
933
|
+
{"type": "webhook", "url": "https://siem.example.com/brindle", "secret_env": "BRINDLE_WEBHOOK_SECRET"},
|
|
934
|
+
{"type": "splunk", "url": "https://splunk.example.com:8088", "token_env": "SPLUNK_HEC_TOKEN", "index": "audit"},
|
|
935
|
+
{"type": "datadog", "site": "datadoghq.com", "api_key_env": "DD_API_KEY", "tags": ["env:prod"]},
|
|
936
|
+
{"type": "s3", "bucket": "acme-audit", "prefix": "brindle/", "region": "eu-west-1"}]}}
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
- `webhook`: an HTTPS `POST` of `{"version", "source", "sent_at", "records"}` with
|
|
940
|
+
`X-Brindle-Signature: sha256=<HMAC-SHA256 of the raw body under the secret>`.
|
|
941
|
+
- `splunk`: the HTTP Event Collector (`Authorization: Splunk <token>`); the path
|
|
942
|
+
defaults to `/services/collector/event`; `index`, `source`, `sourcetype` are optional.
|
|
943
|
+
- `datadog`: the Logs intake for your `site`, one log per record.
|
|
944
|
+
- `s3`: one JSONL object per batch under `<prefix>YYYY/MM/DD/`. Needs `boto3`
|
|
945
|
+
(not installed with brindle; without it the sink reports it isn't configured).
|
|
946
|
+
Credentials are boto3's usual chain, or `access_key_env` / `secret_key_env`.
|
|
947
|
+
|
|
948
|
+
Two sources are shipped: every record of the local audit chain, as written
|
|
949
|
+
(`seq`, `ts`, the event, `prev_hash`, `hash`, `sig`, plus the log's name, so the
|
|
950
|
+
receiver can re-check the chain against `brindle audit pubkey`), and the Team
|
|
951
|
+
feed's events (the same HMAC refs the Team backend gets, never raw names, each
|
|
952
|
+
with its content `hash` and the `chain_hash` of the matching chain record). Each
|
|
953
|
+
sink has its own cursor, so a slow or failing sink never holds the others back.
|
|
954
|
+
Records go out in batches, in order, at least once; a failed send is retried with
|
|
955
|
+
exponential backoff from what is on disk, so it survives offline periods and
|
|
956
|
+
restarts. A sink that can't be set up (an unset variable, a non-HTTPS URL) is
|
|
957
|
+
reported and skipped. Nothing is sent without the entitlement or in air-gap mode.
|
|
958
|
+
|
|
959
|
+
`retention_days` prunes local audit records older than that, but only once every
|
|
960
|
+
sink has received them. Pruning replaces the dropped records with one signed
|
|
961
|
+
checkpoint (the last dropped seq and its hash), so `brindle audit verify` still
|
|
962
|
+
passes; the newest record is always kept.
|
|
963
|
+
|
|
964
|
+
```sh
|
|
965
|
+
brindle audit ship [--force] # send now; exit 1 if a sink failed
|
|
966
|
+
brindle audit prune [--repo PATH] [--days N] # prune now (default: retention_days)
|
|
967
|
+
```
|
|
968
|
+
|
|
818
969
|
### Brindle-CI
|
|
819
970
|
|
|
820
|
-
Brindle-CI (brindle Team) brings brindle into your CI: it fixes broken builds,
|
|
971
|
+
Brindle-CI (brindle Team; build fixing alone is on Pro) brings brindle into your CI: it fixes broken builds,
|
|
821
972
|
turns issues into pull requests whose checks it has verified, and reviews pull
|
|
822
973
|
requests with evidence. It runs on your own CI with your own model keys; the
|
|
823
974
|
hosted control plane decides what each run does and what it reports. The
|
|
@@ -931,6 +1082,20 @@ conflict, a failing check, no reviewer) arrives as "needs you" with the details.
|
|
|
931
1082
|
`"pipeline": false` restores the manual flow. Supervisors are also told to keep task
|
|
932
1083
|
briefs short: writing a long brief holds up every worker waiting on it.
|
|
933
1084
|
|
|
1085
|
+
**Conflict-aware merging.** Declared `files` are a guess; what a branch actually
|
|
1086
|
+
changed is the truth. So at the end of each worker turn brindle compares the files
|
|
1087
|
+
each running branch has changed (committed or not) and tells the supervisor as soon
|
|
1088
|
+
as two branches touch the same file, once per pair, while there's still time to have
|
|
1089
|
+
one wait for the other. When several approved branches are ready at once, brindle
|
|
1090
|
+
merges the one whose hunks overlap the others' least first, then syncs each of the
|
|
1091
|
+
rest onto the new tip (merging the base into it, so its approval carries over) and
|
|
1092
|
+
merges it in turn; a conflict then belongs to one branch, not to the pile. That
|
|
1093
|
+
branch goes back to its own worker with the conflicting files to resolve, or to a
|
|
1094
|
+
light worker started in its worktree if the original has finished; its next report
|
|
1095
|
+
starts a fresh review, and the checks run again on the resolved commit before it
|
|
1096
|
+
merges. Conflicts in `protected_paths` (migrations, lockfiles, generated code) are
|
|
1097
|
+
never handed to a worker: the supervisor gets them as "needs you".
|
|
1098
|
+
|
|
934
1099
|
**The completion audit.** A milestone's check can pass without the milestone being
|
|
935
1100
|
done: the test asserts too little, or checks the wrong thing. So when every milestone
|
|
936
1101
|
passes, autopilot doesn't call the goal reached yet. A read-only reviewer audits everything since the goal was set
|
|
@@ -993,7 +1158,8 @@ servers don't collide. Agents also get `BRINDLE_AGENT_ID`.
|
|
|
993
1158
|
## Agent profiles
|
|
994
1159
|
|
|
995
1160
|
Markdown files with frontmatter. brindle looks in `.brindle/agents/`, then
|
|
996
|
-
`~/.brindle/agents/`, then
|
|
1161
|
+
`~/.brindle/agents/`, then your org's library (brindle Team, below), then its
|
|
1162
|
+
built-ins (`supervisor`, `developer`, `reviewer`,
|
|
997
1163
|
`reviewer-codex`, `developer-local`, `reviewer-local`, `subagent`):
|
|
998
1164
|
|
|
999
1165
|
```markdown
|
|
@@ -1120,7 +1286,169 @@ reports the error. Differences from an interactive worker:
|
|
|
1120
1286
|
|
|
1121
1287
|
Blank values and anything after ` #` are ignored, so frontmatter can carry comments.
|
|
1122
1288
|
`env.NAME: value` lines set environment variables for the agent's process (see
|
|
1123
|
-
"Open-weight models" for what that's for).
|
|
1289
|
+
"Open-weight models" for what that's for). A list field takes `a, b`, `[a, b]`,
|
|
1290
|
+
or a block list (`- item` lines under the key), which is the form for items that
|
|
1291
|
+
contain commas.
|
|
1292
|
+
|
|
1293
|
+
### Building on a profile: `extends` and rule packs
|
|
1294
|
+
|
|
1295
|
+
A profile can start from another one instead of copying it:
|
|
1296
|
+
|
|
1297
|
+
```markdown
|
|
1298
|
+
---
|
|
1299
|
+
name: backend-auditor
|
|
1300
|
+
description: Reviews a branch as a backend security audit
|
|
1301
|
+
extends: reviewer
|
|
1302
|
+
rules: security/backend
|
|
1303
|
+
---
|
|
1304
|
+
Audit the change the way a security reviewer would...
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
`extends` names one parent, found by the same lookup order (the repo's
|
|
1308
|
+
`.brindle/agents/`, then `~/.brindle/agents/`, then the built-ins). The child's
|
|
1309
|
+
fields override the parent's, field by field; a field the child leaves out is
|
|
1310
|
+
the parent's. The child's prompt is appended to the parent's, so the child adds
|
|
1311
|
+
to the role rather than restating it. Chains work (`a` extends `b` extends
|
|
1312
|
+
`developer`); a cycle is an error that names the loop. `brindle profile show
|
|
1313
|
+
<name>` prints the result.
|
|
1314
|
+
|
|
1315
|
+
`rules` names **rule packs**: markdown files that give an agent a standing set
|
|
1316
|
+
of rules, and brindle a few it can check itself. A pack lives in
|
|
1317
|
+
`.brindle/rules/<name>.md` (the repo's), `~/.brindle/rules/<name>.md` (yours),
|
|
1318
|
+
or ships with brindle, in that order of precedence; `security/backend` is
|
|
1319
|
+
`security/backend.md`. A child's packs are added after its parent's.
|
|
1320
|
+
|
|
1321
|
+
```markdown
|
|
1322
|
+
---
|
|
1323
|
+
name: security/backend
|
|
1324
|
+
description: Backend code handles untrusted input, secrets and the shell carefully
|
|
1325
|
+
deny_deps: [pickle] # names that may not be imported or added to a manifest
|
|
1326
|
+
require_tests_for: [src/**/*.py] # a change here must come with a test file in the diff
|
|
1327
|
+
deny_patterns: # regexes no added line may match
|
|
1328
|
+
- shell=True
|
|
1329
|
+
- \beval\(
|
|
1330
|
+
---
|
|
1331
|
+
Treat every input that crosses a trust boundary as hostile until validated...
|
|
1332
|
+
```
|
|
1333
|
+
|
|
1334
|
+
The pack's text joins the agent's prompt, for every provider, with the
|
|
1335
|
+
mechanical rules spelled out. Those three are checked against the branch's
|
|
1336
|
+
diff (what it commits on top of its base) in the same step that runs the repo's
|
|
1337
|
+
`checks`: the reviewer's check summary lists each pack as `PASS`/`FAIL` with
|
|
1338
|
+
the offending `file:line`, and `merge_workspace` refuses the branch while a
|
|
1339
|
+
pack fails, with the violations to send back to the worker, like a failed
|
|
1340
|
+
check. `deny_deps` looks at added `import`/`from`/`require` lines and at
|
|
1341
|
+
dependency manifests (`pyproject.toml`, `requirements*.txt`, `package.json`,
|
|
1342
|
+
`go.mod`, `Cargo.toml` and the like); `require_tests_for` is satisfied by any
|
|
1343
|
+
added or changed test file (`tests/`, `test_*`, `*_test.*`, `*.test.*`,
|
|
1344
|
+
`*.spec.*`, `__tests__/`). A pack with only prose is a prompt and nothing to
|
|
1345
|
+
check. A pack a profile names but that doesn't exist stops the launch, so a
|
|
1346
|
+
rule never silently stops applying. These are pattern checks on the diff's
|
|
1347
|
+
text: they catch the honest mistake and the obvious shortcut and name the
|
|
1348
|
+
line, and a dependency loaded through a name built at run time, or a test
|
|
1349
|
+
that tests nothing, is the reviewer's to catch. What they read can't be
|
|
1350
|
+
hidden: the diff is taken with git's attributes, drivers and prefixes
|
|
1351
|
+
overridden, and a worker whose profile no longer loads fails the gate rather
|
|
1352
|
+
than passing with no rules.
|
|
1353
|
+
|
|
1354
|
+
Built-in packs: `security/backend` (no shell-outs from input, no `eval`, no
|
|
1355
|
+
unsafe deserializing, no secrets in code), `tests/only` (change tests, not the
|
|
1356
|
+
code under test: every diff must touch a test file), `style/minimal-diff` (the
|
|
1357
|
+
smallest diff that does the job). Built-in profiles that use them:
|
|
1358
|
+
`backend-auditor` (`reviewer` plus `security/backend`) and `qa` (`developer`
|
|
1359
|
+
plus `tests/only`).
|
|
1360
|
+
|
|
1361
|
+
`brindle profile new <name> --extends developer --rules security/backend` writes
|
|
1362
|
+
the file; `brindle profile lint` checks every profile it can see (a missing
|
|
1363
|
+
parent, a cycle, an unknown pack, a pattern that doesn't compile, an unknown
|
|
1364
|
+
provider) and exits 1 if any fails.
|
|
1365
|
+
|
|
1366
|
+
### Guardrails: write scope, read scope, env allowlist (brindle Pro)
|
|
1367
|
+
|
|
1368
|
+
```markdown
|
|
1369
|
+
---
|
|
1370
|
+
name: api-dev
|
|
1371
|
+
extends: developer
|
|
1372
|
+
write_scope: [src/api/**, tests/api/**]
|
|
1373
|
+
read_scope: [src/**, tests/**, docs/**]
|
|
1374
|
+
env_allow: [NPM_TOKEN, AWS_*]
|
|
1375
|
+
---
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
Globs are relative to the worktree; a trailing `/` or a bare directory name
|
|
1379
|
+
means everything under it, `*` crosses `/`, and case counts.
|
|
1380
|
+
|
|
1381
|
+
- **`write_scope`** is enforced on the branch: the same step that checks rule
|
|
1382
|
+
packs lists every file the branch changes (deletions, renames and mode
|
|
1383
|
+
changes included) and fails the review and merge gates on any outside the
|
|
1384
|
+
scope. On top of that, best effort: Claude Code's PreToolUse hook denies an
|
|
1385
|
+
Edit or Write outside it, the native runner's file tools refuse one, and the
|
|
1386
|
+
worker is told its scope in its prompt. A shell command can still write
|
|
1387
|
+
anywhere, and Codex and agy get only the prompt, so the diff check is what
|
|
1388
|
+
holds. An empty `write_scope: []` means no limit, not "deny everything".
|
|
1389
|
+
- **`read_scope`** is best effort only. The native runner's Read refuses a
|
|
1390
|
+
file outside it and Glob and Grep leave such files out; Claude Code's hook
|
|
1391
|
+
denies a Read outside it, and a Glob or Grep unless the directory searched
|
|
1392
|
+
is wholly in scope (a scope of `src/api/**` can search `src/api/`, not
|
|
1393
|
+
`src/`). A shell command can read anything, other providers only get the
|
|
1394
|
+
prompt, and a read leaves no diff to check afterwards.
|
|
1395
|
+
- **`env_allow`** (names or globs): the worker's process starts with only
|
|
1396
|
+
these variables, brindle's own (`BRINDLE_*`), the basics a process needs
|
|
1397
|
+
(`PATH`, `HOME`, `LANG`, `LC_*`, `TERM`, ...), the profile's own `env.NAME`
|
|
1398
|
+
lines and the provider's sign-in (Claude Code's keys and cloud backends,
|
|
1399
|
+
Codex's keys, a native profile's `api_key_env`). brindle's own secrets (the
|
|
1400
|
+
Pro token, GitHub's tokens) never reach a worker, listed or not. Anything
|
|
1401
|
+
else is dropped, so a tool that needs `GIT_*`, the proxy variables
|
|
1402
|
+
(`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`) or `NO_COLOR` must have them listed.
|
|
1403
|
+
|
|
1404
|
+
Without the `guardrails` feature these keys are ignored, with one warning per
|
|
1405
|
+
profile, and the profile runs as if they weren't there.
|
|
1406
|
+
|
|
1407
|
+
### Learned rules (brindle Pro)
|
|
1408
|
+
|
|
1409
|
+
When reviewers keep asking for the same change, brindle suggests a rule for
|
|
1410
|
+
it. Each review that requests changes is a finding, filed under the repo and
|
|
1411
|
+
the profile of the worker whose branch it was. After a review like that,
|
|
1412
|
+
brindle groups the new findings with one small model call (the repo's
|
|
1413
|
+
`learned_rules_profile`, else a local model that answers, else Claude's
|
|
1414
|
+
cheapest model; its tokens show up in `brindle history` like any other).
|
|
1415
|
+
Once the same finding has come up in `learned_rules_repeats` tasks (3 by
|
|
1416
|
+
default), the supervisor's `get_progress` mentions the suggestion and
|
|
1417
|
+
`brindle rules suggest` lists it. Nothing changes until you decide:
|
|
1418
|
+
|
|
1419
|
+
```sh
|
|
1420
|
+
brindle rules suggest # group new findings, then list the suggestions (--no-refresh: just list)
|
|
1421
|
+
brindle rules accept 3f9c2a1b # add it to .brindle/rules/learned.md
|
|
1422
|
+
brindle rules reject 3f9c2a1b # never suggest it again
|
|
1423
|
+
```
|
|
1424
|
+
|
|
1425
|
+
`.brindle/rules/learned.md` is an ordinary rule pack that every profile in the
|
|
1426
|
+
repo picks up: each accepted rule is a line of its prose, and when the finding
|
|
1427
|
+
is a mechanical one (a pattern no added line may match, a dependency not to
|
|
1428
|
+
add, paths whose changes need a test), it lands in the pack's `deny_patterns`,
|
|
1429
|
+
`deny_deps` or `require_tests_for` and is checked against every branch's diff.
|
|
1430
|
+
Commit it so the rule is reviewed and shared; edit or delete lines as with any
|
|
1431
|
+
pack. Rejections are remembered on this machine: findings that fall under a
|
|
1432
|
+
rejected rule keep it rejected.
|
|
1433
|
+
|
|
1434
|
+
### The org library (brindle Team)
|
|
1435
|
+
|
|
1436
|
+
With a Team plan, an org's admins can share profiles and rule packs with every
|
|
1437
|
+
member. An admin publishes a file with `brindle account org profiles push
|
|
1438
|
+
reviewer.md` (a rule pack: `push --pack security.md`, named like
|
|
1439
|
+
`security/backend` with `--name`), and `brindle account org profiles rm <name>`
|
|
1440
|
+
removes one. `brindle account org profiles` lists what the org shares.
|
|
1441
|
+
|
|
1442
|
+
Members get them in the lookup order repo, user, org, built-in (for profiles
|
|
1443
|
+
and rule packs alike), so a repo or user file of the same name still wins.
|
|
1444
|
+
`push --pinned` changes that for an item: a pinned org profile or pack is used
|
|
1445
|
+
over any repo or user file of that name, which is ignored with a warning, so a
|
|
1446
|
+
repo can't loosen what the org requires.
|
|
1447
|
+
|
|
1448
|
+
The library is signed by the backend and verified before use. A copy is kept
|
|
1449
|
+
in `~/.brindle/pro/profiles-<org>/` and used while the backend can't be
|
|
1450
|
+
reached (for a week past its signature); with no usable copy brindle uses
|
|
1451
|
+
only what it has locally, and says so. Air-gap mode never fetches.
|
|
1124
1452
|
|
|
1125
1453
|
### Directories outside the workspace
|
|
1126
1454
|
|
|
@@ -1224,9 +1552,9 @@ ollama pull qwen3-coder:30b
|
|
|
1224
1552
|
brindle doctor # "model qwen3-coder:30b ... is available"
|
|
1225
1553
|
```
|
|
1226
1554
|
|
|
1227
|
-
Run `ollama serve` yourself, or let brindle do it
|
|
1228
|
-
of GPU memory, so
|
|
1229
|
-
|
|
1555
|
+
Run `ollama serve` yourself, or let brindle do it, with no setup. A 30B model holds
|
|
1556
|
+
about 20 GB of GPU memory, so set `"local_models": false` if you don't want brindle
|
|
1557
|
+
to start or preload one. When `brindle` (or `brindle continue`)
|
|
1230
1558
|
starts and a native profile points at Ollama on this machine that isn't answering,
|
|
1231
1559
|
brindle starts it in the background with `OLLAMA_CONTEXT_LENGTH` set to the largest
|
|
1232
1560
|
`context_tokens` any profile asks of it plus room for the reply (40960 for the
|