brindle 0.4.0__tar.gz → 0.4.2__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.4.0 → brindle-0.4.2}/PKG-INFO +149 -29
- {brindle-0.4.0 → brindle-0.4.2}/README.md +148 -28
- {brindle-0.4.0 → brindle-0.4.2}/pyproject.toml +1 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/agents.py +250 -41
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/antigravity.py +205 -52
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/autopilot.py +43 -51
- brindle-0.4.2/src/brindle/builtin_agents/developer-opencode.md +6 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/reviewer-codex.md +3 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/reviewer-gemini.md +6 -2
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/reviewer-local.md +3 -1
- brindle-0.4.0/src/brindle/builtin_agents/reviewer.md → brindle-0.4.2/src/brindle/builtin_agents/reviewer-opencode.md +6 -8
- brindle-0.4.2/src/brindle/builtin_agents/reviewer.md +36 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/supervisor.md +1 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/cli.py +70 -9
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/codemap.py +35 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/config.py +7 -5
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/cull.py +10 -2
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/db.py +75 -2
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/demo.py +3 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/doctor.py +9 -5
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/gates.py +186 -36
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/git.py +13 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/inbox.py +5 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/launch_health.py +250 -15
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/mcp_server.py +58 -6
- brindle-0.4.2/src/brindle/native/catalog.py +192 -0
- brindle-0.4.2/src/brindle/native/lsp.py +293 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/native/permissions.py +7 -3
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/native/runner.py +10 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/native/tools.py +36 -5
- brindle-0.4.2/src/brindle/opencode.py +461 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/permissions.py +173 -29
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/picker.py +3 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pipeline.py +125 -55
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/account.py +36 -15
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/team_policy.py +1 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/procs.py +1 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/profiles.py +35 -1
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/prompt_answer.py +63 -29
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/providers.py +167 -34
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/self_serve.py +74 -2
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/sessions.py +18 -10
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/sidebar_follow.py +5 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/tmux.py +10 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/view.py +1 -0
- brindle-0.4.2/src/brindle/wake.py +250 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/watch.py +3 -2
- brindle-0.4.2/src/brindle/web/__init__.py +5 -0
- brindle-0.4.2/src/brindle/web/chat.py +125 -0
- brindle-0.4.2/src/brindle/web/dashboard.py +51 -0
- brindle-0.4.2/src/brindle/web/diff.py +41 -0
- brindle-0.4.2/src/brindle/web/server.py +237 -0
- brindle-0.4.2/src/brindle/web/static/app.js +270 -0
- brindle-0.4.2/src/brindle/web/static/index.html +94 -0
- {brindle-0.4.0 → brindle-0.4.2}/.gitignore +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/LICENSE +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/SCHEDULE-A +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/__init__.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/__main__.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/account.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/airgap.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/assembler.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/assets/braid_dark.png +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/assets/braid_light.png +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/budget.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/__init__.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/backend-auditor.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/coordinator.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/developer-codex.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/developer-fable.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/developer-gemini.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/developer-heavy.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/developer-light.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/developer-local.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/developer.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/qa.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_agents/subagent.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_rules/__init__.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_rules/security/backend.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/builtin_rules/tests/only.md +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/ci_adapters.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/ci_client.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/ci_federation.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/ci_hosts.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/cloud_credential.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/cloud_login.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/codemap_docs.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/codex_hook.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/company_identity.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/company_login.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/conflicts.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/coordinator.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/cost.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/cost_estimate.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/detect.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/escapes.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/events.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/guardrails.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/history.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/identity_lock.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/kb.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/key_check.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/keys_cmds.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/keystore.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/knowledge.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/learned_rules.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/learning.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/legacy.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/local_setup.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/login_pause.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/model_access.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/model_availability.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/native/__init__.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/native/client.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/native/loop.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/native/serve.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pane_auth.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/plugins.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/policy.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pool.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pricing.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/__init__.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/_files.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/audit_chain.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/audit_export.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/auth.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/brief_learning.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/cost_centers.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/credentials.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/features.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/keys.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/learning.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/license.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/loopback.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/managed_models.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/org_profiles.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/orgkey.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/rollout.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/route_health.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/settings_sync.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/status.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/pro/team_events.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/quota.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/repo_cmds.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/repos.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/rewind.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/rule_checks.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/savings.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/scratch.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/secrets.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/services.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/sidebar_logo.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/signin_pause.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/status_cache.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/tasks.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/updates.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/upgrade.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/usage.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/watch_costs.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/worker_models.py +0 -0
- {brindle-0.4.0 → brindle-0.4.2}/src/brindle/workspaces.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brindle
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
@@ -42,13 +42,20 @@ that exact commit and your checks pass. A goal is done only when its check comma
|
|
|
42
42
|
exit 0.
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
curl -fsSL pawdelta.com/brindle/install | sh
|
|
45
|
+
curl -fsSL pawdelta.com/brindle/install | sh # installs uv and tmux if missing, then brindle
|
|
46
|
+
# or, if you already have uv and tmux:
|
|
47
|
+
uv tool install brindle
|
|
48
|
+
|
|
46
49
|
brindle demo # watch it finish a practice repo in a few minutes
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
brindle is free to use, including at work, under the [brindle License 1.0](LICENSE): you
|
|
53
|
+
may install and run it, but not modify or redistribute it or use it to build a competing
|
|
54
|
+
service ([details](#license)).
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
*`brindle demo`, recorded on brindle 0.4.1 (sped up about 2.7×, 20 seconds): two workers in parallel, each branch reviewed and merged, both milestones verified by their check commands.*
|
|
52
59
|
|
|
53
60
|
- **Done means a command passed.** A goal is split into milestones, each with a
|
|
54
61
|
check command that brindle runs itself. A milestone is verified when its check exits
|
|
@@ -94,7 +101,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
|
|
|
94
101
|
brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
|
|
95
102
|
|
|
96
103
|
`brindle --version` prints the installed version; the tmux status bar of every brindle
|
|
97
|
-
session shows it too (`brindle 0.4.
|
|
104
|
+
session shows it too (`brindle 0.4.2`). A session started before an upgrade keeps
|
|
98
105
|
running the old code, and shows the old number, until you restart it.
|
|
99
106
|
|
|
100
107
|
## Quick start
|
|
@@ -115,7 +122,8 @@ as `brindle doctor`. Commit the config so your team gets it too.
|
|
|
115
122
|
New to brindle? `brindle demo` runs it on a tiny practice repo (under `~/.brindle/demo/`)
|
|
116
123
|
with two failing test files and a two-milestone goal: in a few minutes you watch two
|
|
117
124
|
workers fix them in parallel, each branch get reviewed and merged, and both milestones
|
|
118
|
-
turn green only once their test command passes
|
|
125
|
+
turn green only once their test command passes (the demo repo's config sets
|
|
126
|
+
`"delegation": "fast"`, so the supervisor always splits the work). `brindle demo --local` keeps the workers
|
|
119
127
|
and reviewers on a local model (Ollama).
|
|
120
128
|
|
|
121
129
|
`brindle doctor` checks that everything brindle needs is there (tmux, the agent CLIs,
|
|
@@ -347,6 +355,11 @@ or once its goal was replaced from the chat. From a linked worktree, it writes
|
|
|
347
355
|
the `goals.md` it loaded (the main checkout's), never another session's. A
|
|
348
356
|
write that fails never fails the check.
|
|
349
357
|
|
|
358
|
+
A new session takes a hand-written `goals.md` over by moving it to its own
|
|
359
|
+
`.brindle/goals/<session id>.md` (git-ignored). If `goals.md` was committed,
|
|
360
|
+
its deletion is left for you to commit or restore; brindle doesn't count it as
|
|
361
|
+
an uncommitted change, so it never blocks a merge or a milestone's check.
|
|
362
|
+
|
|
350
363
|
`brindle autopilot` shows progress, `brindle autopilot check` runs the checks
|
|
351
364
|
now, and `brindle autopilot off` (or `on`) hands the wheel back (or takes it
|
|
352
365
|
again). `brindle --no-autopilot`, or `"autopilot": false` in the repo config,
|
|
@@ -399,6 +412,7 @@ your own status line prints, so what you see doesn't change.
|
|
|
399
412
|
| `brindle login` | the same as `brindle account login`; when your org's policy has an `agent_setup` block it also signs you in to the Claude and Codex route the company chose (AWS SSO profile, `gcloud`, `az`, `claude auth login`, `codex login`), stores the Claude Code environment for brindle's panes in `~/.brindle/config.json`, and checks the pinned models. `brindle doctor --fix` does the same |
|
|
400
413
|
| `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) |
|
|
401
414
|
| `brindle watch [--all] [--once]` | the dashboard on its own (the same view as the sidebar): enter attaches, `p` peeks, `x` closes |
|
|
415
|
+
| `brindle web [--port N] [--no-open]` | this session in your browser (see "Web UI") |
|
|
402
416
|
| `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 |
|
|
403
417
|
| `brindle attach / cd / open [WS]` | tmux session (at the agent waiting on you, else the busiest or newest) / path / editor |
|
|
404
418
|
| `brindle status / diff [--stat] [WS]` | compared with the base branch (committed + uncommitted) |
|
|
@@ -421,6 +435,21 @@ your own status line prints, so what you see doesn't change.
|
|
|
421
435
|
|
|
422
436
|
With no `WS` argument, commands act on the workspace you're in.
|
|
423
437
|
|
|
438
|
+
### Web UI
|
|
439
|
+
|
|
440
|
+
`brindle web` serves the current session to your browser: workspaces, agents
|
|
441
|
+
and their status, and the goal's milestones with each one's last check. Run
|
|
442
|
+
it from a terminal in the session's tmux session to show that session's
|
|
443
|
+
goal; elsewhere it shows the newest autopilot session
|
|
444
|
+
in the repo. The page refreshes itself every couple of seconds.
|
|
445
|
+
|
|
446
|
+
It listens on `127.0.0.1` only, on a free port (or `--port N`), and prints a
|
|
447
|
+
link with a random token, `http://127.0.0.1:PORT/?token=...`, which it also
|
|
448
|
+
opens. The token is new each run and is needed on every request (the first
|
|
449
|
+
one sets a cookie, so the page drops it from the address bar). Requests
|
|
450
|
+
naming another host, or changing anything from another site's page, are
|
|
451
|
+
refused. Ctrl-C stops it.
|
|
452
|
+
|
|
424
453
|
### Agent tools
|
|
425
454
|
|
|
426
455
|
Agents launched by brindle get these MCP tools. You don't call them yourself, but
|
|
@@ -651,6 +680,7 @@ Autopilot, merge gates and cleanup:
|
|
|
651
680
|
| `usage_limit` | `90` | autopilot stops pushing on at this % of your Claude usage limit |
|
|
652
681
|
| `limit_cooldown_minutes` | `300` for Antigravity | how long a provider that hit its limit counts as unavailable |
|
|
653
682
|
| `graphify` | if the graph is there | point agents at the repo's [graphify](https://github.com/safishamsi/graphify) code map (`false` turns it off) |
|
|
683
|
+
| `graphify_auto_refresh` | `true` | automatically rebuild untracked code maps; `false` disables rebuilds while keeping queries available |
|
|
654
684
|
| `stale_after` | `30` | minutes before a worker that reported and sat idle is closed, or `brindle prune` closes a stale paused/exited agent whose workspace is merged or gone (`0`: never) |
|
|
655
685
|
| `pipeline` | `true` | brindle reviews and merges reported branches itself; the supervisor gets one message per branch |
|
|
656
686
|
| `review_rounds` | `2` | fix-and-re-review rounds the pipeline runs before handing findings to the supervisor |
|
|
@@ -658,7 +688,7 @@ Autopilot, merge gates and cleanup:
|
|
|
658
688
|
| `merge_into` | none | branch that worker branches are cut from and merge into, whatever branch the supervisor is on |
|
|
659
689
|
| `auto_merge_default_branch` | `false` | let the pipeline merge into the repo's default branch (origin HEAD, else `main`/`master`) on its own; by default it sends a "needs you" message instead, and you run `merge_workspace` yourself (manual merges are never gated) |
|
|
660
690
|
| `permission_policy` | `"off"` | `"on"`: brindle answers Claude Code workers' permission prompts from its rules (see "Permission policy" below); off, the prompts behave as they always did |
|
|
661
|
-
| `agy_approvals` |
|
|
691
|
+
| `agy_approvals` | unset | `"brindle"`: Antigravity agents run without approval prompts and brindle's hook allows or denies every call, applying the profile's `allowed_tools` (see "Antigravity" under "Permission policy"); `"prompt"`: agy asks as usual. Unset, workers get `"brindle"` and interactive agy sessions `"prompt"` |
|
|
662
692
|
| `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 |
|
|
663
693
|
| `plan_first` | `false` | workers propose a plan (`submit_plan`) and wait for `approve_plan` before editing |
|
|
664
694
|
| `overlap` | unset | a task whose `files` overlap a running task's: `"block"` refuses it, `"warn"` starts it with a warning, `"queue"` queues it until that task merges. Unset, it is refused within a session and queued behind another session's task (both sessions' supervisors are told; the other session's work is never cancelled), and the queued task starts from that task's merge commit |
|
|
@@ -931,7 +961,8 @@ A session override of `features.hooks=false` reported false. No feature-enable
|
|
|
931
961
|
flag is needed on these versions, and brindle leaves explicit disablement alone.
|
|
932
962
|
|
|
933
963
|
**Antigravity.** agy's hook can deny but its "allow" is ignored (an agy bug),
|
|
934
|
-
and it has no "no opinion" answer. So
|
|
964
|
+
and it has no "no opinion" answer. So, where agy prompts (an interactive agy
|
|
965
|
+
session, or any agy agent under `"agy_approvals": "prompt"`), the hook denies what the policy denies,
|
|
935
966
|
answers "allow" for a file inside the workspace (which agy reads and writes
|
|
936
967
|
without asking anyway) and "ask" for everything else, and brindle copies the
|
|
937
968
|
rules agy can express exactly into your `~/.gemini/antigravity-cli/settings.json`
|
|
@@ -946,8 +977,10 @@ does it by hand. To undo, turn `permission_policy` off and run `sync-agy`: brind
|
|
|
946
977
|
removes exactly its own entries. brindle doesn't learn from agy approvals (its
|
|
947
978
|
hook runs for every tool call, so a tool running doesn't mean you approved it).
|
|
948
979
|
|
|
949
|
-
|
|
950
|
-
|
|
980
|
+
For workers (any agy agent but an interactive one) when `agy_approvals` is
|
|
981
|
+
unset, and for every agy agent under `"agy_approvals": "brindle"`, whether or
|
|
982
|
+
not the policy is on, brindle's hook is the
|
|
983
|
+
only gate instead: they start with `--dangerously-skip-permissions`,
|
|
951
984
|
under which agy still honors a hook's deny, and the hook runs on every tool,
|
|
952
985
|
subagents' calls included. It allows what a rule allows (a repo's checks
|
|
953
986
|
included), the shell commands in the profile's `allowed_tools` (what a Claude
|
|
@@ -955,11 +988,20 @@ Code worker on it runs without asking), files inside the workspace given as one
|
|
|
955
988
|
absolute path (writes under `.agents/`, `.git/` and
|
|
956
989
|
`.brindle/` only by a rule, since those hold the hook, git's hooks and the
|
|
957
990
|
repo's checks), brindle's own tools and agy's tools that act only on the
|
|
958
|
-
conversation, and denies everything else
|
|
959
|
-
|
|
991
|
+
conversation, and denies everything else. A call no rule covers (not one a
|
|
992
|
+
rule denies) is what a Claude Code worker would wait on you for: brindle
|
|
993
|
+
records it as the worker's pending request and tells its supervisor, once per
|
|
994
|
+
request in a turn, with the `brindle permissions allow ...` commands that allow
|
|
995
|
+
it (one per part of a compound command that no rule covers yet); once you run
|
|
996
|
+
them, the worker's retry goes through. A command no rule can allow as written
|
|
997
|
+
(`$(...)`, a redirection that writes, a heredoc) comes with that instead, so
|
|
998
|
+
the worker can split it into simple commands. A `dontAsk` profile (a
|
|
999
|
+
reviewer) and an agent with no supervisor are only told to ask. Any failure
|
|
960
1000
|
in the hook is a deny, and a tool brindle doesn't know is denied. Nobody can
|
|
961
|
-
approve a prompt in the agent's pane in this mode
|
|
962
|
-
leaves agy asking as described above
|
|
1001
|
+
approve a prompt in the agent's pane in this mode, so a worker never waits at
|
|
1002
|
+
one nobody is watching. `"prompt"` leaves agy asking as described above (and
|
|
1003
|
+
`assign`/`handoff` name the profile's `allowed_tools` agy's own settings don't
|
|
1004
|
+
already allow).
|
|
963
1005
|
|
|
964
1006
|
## brindle Pro and Team
|
|
965
1007
|
|
|
@@ -1003,7 +1045,7 @@ brindle account login # opens your browser to sign in (--device: enter a co
|
|
|
1003
1045
|
brindle account status # your plan, features, hosted learning on or off, when the entitlement expires
|
|
1004
1046
|
brindle account savings # what hosted learning's picks gained in this repo, this month and last: estimates, from this machine's records only
|
|
1005
1047
|
brindle account upgrade # opens the checkout for brindle Pro (and prints its URL)
|
|
1006
|
-
brindle account upgrade --enterprise
|
|
1048
|
+
brindle account upgrade --enterprise # brindle Enterprise is set up by contact (prints support@pawdelta.com); not a checkout
|
|
1007
1049
|
brindle account portal # opens the billing portal (invoices, payment method, cancellation); --org ORG for a team org
|
|
1008
1050
|
brindle account seats N # set a Team or Enterprise org's seat count (billing admin); --org ORG; Stripe prorates the change
|
|
1009
1051
|
brindle account org list # the orgs you belong to; `org use <id>` switches, `org policy` shows the current one
|
|
@@ -1454,7 +1496,11 @@ knowledge graph (`graphify-out/graph.json`, built with `/graphify`) and `graphif
|
|
|
1454
1496
|
installed, brindle tells the supervisor and every worker to find code with
|
|
1455
1497
|
`graphify query` before grepping or reading whole files. Those commands are
|
|
1456
1498
|
pre-approved, and brindle refreshes the graph's code (`graphify update`, no LLM) in
|
|
1457
|
-
the background after each merge.
|
|
1499
|
+
the background after each merge. Automatic rebuilds skip checkouts with any
|
|
1500
|
+
git-tracked files under `graphify-out/`, preserving richer semantic graphs and
|
|
1501
|
+
their generated reports. Update tracked graphs explicitly with graphify.
|
|
1502
|
+
Set `"graphify_auto_refresh": false` in `.brindle/config.json` to also disable
|
|
1503
|
+
automatic rebuilds and session seeding for untracked graphs while keeping queries.
|
|
1458
1504
|
|
|
1459
1505
|
When `pool_size` is greater than `0`, a claimed worktree keeps the path and
|
|
1460
1506
|
port block it was built with -- it's never moved, and its port block is fixed
|
|
@@ -1536,6 +1582,7 @@ change files under a directory without prompts.
|
|
|
1536
1582
|
| `claude` (Claude Code) | yes (the default) | yes |
|
|
1537
1583
|
| `codex` | yes | yes |
|
|
1538
1584
|
| `antigravity` (`agy`) | yes | yes |
|
|
1585
|
+
| `opencode` (OpenCode) | yes | yes (`developer-opencode`, `reviewer-opencode`) |
|
|
1539
1586
|
| `native` (local models) | no: its loop has only the worker tools (report, message, diff), not `assign` or `handoff` | yes |
|
|
1540
1587
|
| `subagent` | no: it runs inside a supervisor's own Agent tool | yes, through `assign`/`handoff` |
|
|
1541
1588
|
| `shell` | only as a stand-in, for testing brindle itself | a plain shell, for dev servers and testing brindle |
|
|
@@ -1561,6 +1608,7 @@ it's signed in without a plan that includes it, is reported to its supervisor.
|
|
|
1561
1608
|
| `claude` | yes: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, or Bedrock/Vertex/Foundry (`CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY`) |
|
|
1562
1609
|
| `codex` | yes: `OPENAI_API_KEY`, `CODEX_API_KEY`. With Codex signed in with ChatGPT (checked on Codex CLI 0.162.0): interactive `codex`, which brindle runs in agent panes, ignores both keys and uses the ChatGPT plan, and only `codex login --with-api-key` switches it to a key; headless `codex exec` (Brindle-CI) bills `CODEX_API_KEY` over the login and ignores `OPENAI_API_KEY`. `brindle doctor` and `brindle keys set` say so, and `auth: api_key` on an interactive Codex profile is refused under a ChatGPT login (run `codex login --with-api-key`, or use `auth: subscription`) |
|
|
1563
1610
|
| `antigravity` | `GEMINI_API_KEY`, only with `"modelProvider": "gemini"` in `agy`'s `settings.json` (see [Google Antigravity](#google-antigravity)); otherwise `agy`'s own browser sign-in |
|
|
1611
|
+
| `opencode` | whatever the model's provider takes (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, ...), or `opencode auth login`; its free `opencode/...` models need neither |
|
|
1564
1612
|
|
|
1565
1613
|
Workers that run unattended should use key login where the CLI supports it.
|
|
1566
1614
|
A personal login is one interactive session with one quota shared by every
|
|
@@ -1595,7 +1643,39 @@ When running long, unattended autopilot sessions with API keys:
|
|
|
1595
1643
|
- **Recovering failed/blocked workers**: When an agent fails on an expired key or autopilot pauses with a credential error, export the fresh key (or update `brindle keys set`), then run `brindle continue`. This unpauses the session and relaunches paused/failed agents with the new credentials. The replacement credentials are automatically synced to the session credential store, dynamically refreshed by the supervisor's launcher on all subsequent worker delegations (`assign`/`handoff`).
|
|
1596
1644
|
- **Proactive rotation in live sessions**: For Claude keys rotated outside brindle, set Claude Code's `apiKeyHelper` in `~/.claude/settings.json` or the project's `.claude/settings.json`. Claude Code re-runs the helper every 5 minutes (configurable with `CLAUDE_CODE_API_KEY_HELPER_TTL_MS`) and after a 401, so running workers pick up the new key. Don't also export `ANTHROPIC_API_KEY` or set a Claude key with `brindle keys set`, since an environment key takes precedence over the helper. Codex and Antigravity keys still need workers stopped, then `brindle keys set`, then `brindle continue`. Exporting a new key while an active session is healthy does not restart running workers or the supervisor (running `brindle continue` on an active healthy session simply reattaches to tmux without restarting workers). To proactively rotate credentials for the entire session and all running workers: run `brindle stop` -> update credentials in your environment or via `brindle keys set` -> run `brindle continue`.
|
|
1597
1645
|
- **Precedence**: Newly stored keys (`brindle keys set`) or session credentials updated during recovery take effect dynamically for all new panes, overriding stale launcher environments. For profile-level overrides, an explicit profile key (`env.NAME`) takes precedence over default provider credentials.
|
|
1598
|
-
- **Antigravity permissions prerequisite**: Antigravity (`agy`) does not support approving shell commands solely via external hooks.
|
|
1646
|
+
- **Antigravity permissions prerequisite**: Antigravity (`agy`) does not support approving shell commands solely via external hooks. brindle therefore runs agy workers with `--dangerously-skip-permissions` and its own hook as the gate, applying their profile's `allowed_tools` and denying the rest (see Permissions). Under an explicit `agy_approvals: "prompt"` they instead stall on approval prompts for anything `settings.json` doesn't allow, their profile's `allowed_tools` included.
|
|
1647
|
+
|
|
1648
|
+
#### OpenCode
|
|
1649
|
+
|
|
1650
|
+
[OpenCode](https://opencode.ai) (`npm install -g opencode-ai`) runs in an agent
|
|
1651
|
+
pane like the other CLIs; `brindle doctor` says whether it's installed. Run a
|
|
1652
|
+
supervisor on it with `brindle -s opencode`, or workers with the built-in
|
|
1653
|
+
`developer-opencode` and `reviewer-opencode` profiles (or any profile with
|
|
1654
|
+
`provider: opencode`). Checked with OpenCode 1.19.3.
|
|
1655
|
+
|
|
1656
|
+
brindle writes nothing into the worktree. It hands OpenCode its MCP server, a
|
|
1657
|
+
small plugin and the profile's prompt through `OPENCODE_CONFIG_CONTENT`, which
|
|
1658
|
+
OpenCode merges over your own config, so your providers, models and other MCP
|
|
1659
|
+
servers still apply. The plugin reports each turn's start, its end (and any
|
|
1660
|
+
error, such as a rate limit), and permission prompts from OpenCode's own
|
|
1661
|
+
events, so a worker's status never depends on reading its screen.
|
|
1662
|
+
|
|
1663
|
+
| Profile field | OpenCode |
|
|
1664
|
+
|---|---|
|
|
1665
|
+
| `model` | `--model`. Use OpenCode's `provider/model` form (`anthropic/claude-sonnet-4-5`, `opencode/big-pickle`); a bare `claude-…`, `gpt-…` or `gemini-…` name gets its provider added. With no model, OpenCode's own default. |
|
|
1666
|
+
| `allowed_tools` | Each `Bash(cmd:*)` becomes an allowed `bash` pattern; any other command asks. |
|
|
1667
|
+
| `permission_mode` | `bypassPermissions`: everything runs. `dontAsk`: only `allowed_tools` commands run, edits and web fetches are refused. `acceptEdits`/`auto`: edits run. `plan`: OpenCode's `plan` agent. Unset: edits ask too. |
|
|
1668
|
+
| `add_dirs`, `worker_extra_read_dirs` | Allowed as external directories. |
|
|
1669
|
+
|
|
1670
|
+
A `dontAsk` OpenCode worker (the built-in reviewer, for one) can't read outside
|
|
1671
|
+
its worktree or its `add_dirs`: with nobody to ask, anything else is refused.
|
|
1672
|
+
|
|
1673
|
+
`effort` isn't passed (OpenCode's terminal UI has no flag for it). With
|
|
1674
|
+
`permission_policy` on, brindle's plugin also asks brindle before every tool
|
|
1675
|
+
call and stops the ones the policy denies (as for Claude Code and Codex
|
|
1676
|
+
workers); calls the policy allows or asks about are left to the profile's
|
|
1677
|
+
permissions above, so the policy can narrow what an OpenCode worker does but
|
|
1678
|
+
never widens it.
|
|
1599
1679
|
|
|
1600
1680
|
### Cheap workers
|
|
1601
1681
|
|
|
@@ -1886,6 +1966,17 @@ No hooks, no screen scraping, and messages sent to the worker arrive between
|
|
|
1886
1966
|
its model calls. It works with any OpenAI-compatible chat-completions endpoint
|
|
1887
1967
|
or Anthropic Messages endpoint.
|
|
1888
1968
|
|
|
1969
|
+
The native worker automatically starts language servers found on `PATH` when it
|
|
1970
|
+
first reads or changes a supported file (Python: `pyright-langserver` or `pylsp`;
|
|
1971
|
+
JS/TS: `typescript-language-server`; Go: `gopls`; Rust: `rust-analyzer`; C/C++:
|
|
1972
|
+
`clangd`; Ruby: `ruby-lsp`; Lua: `lua-language-server`). Servers use the worktree
|
|
1973
|
+
as their root and stop with the worker. `Diagnostics`, `Definition`, and
|
|
1974
|
+
`References` provide code information; diagnostics also follow successful Edit
|
|
1975
|
+
and Write calls. Navigation takes 1-based lines and character columns. Explicit
|
|
1976
|
+
LSP tools report missing servers; ordinary file tools stay silent when no server
|
|
1977
|
+
can run. Profiles with a narrowed read scope skip language
|
|
1978
|
+
servers because workspace indexing cannot enforce that boundary.
|
|
1979
|
+
|
|
1889
1980
|
```markdown
|
|
1890
1981
|
---
|
|
1891
1982
|
name: developer-local
|
|
@@ -1947,6 +2038,14 @@ Other endpoints, same fields:
|
|
|
1947
2038
|
| DeepSeek | anthropic | `https://api.deepseek.com/anthropic` | paid; `api_key_env: DEEPSEEK_API_KEY` |
|
|
1948
2039
|
| Anthropic | anthropic | `https://api.anthropic.com` | `api_key_env: ANTHROPIC_API_KEY` |
|
|
1949
2040
|
|
|
2041
|
+
Or name any [Models.dev](https://models.dev) model as `model: <provider>/<model>`
|
|
2042
|
+
(say `nano-gpt/deepseek/deepseek-v3`) and leave out `base_url`: brindle fills in
|
|
2043
|
+
`base_url`, `api`, `api_key_env` and `context_tokens` from the catalog, and
|
|
2044
|
+
anything you set in the profile wins. The catalog is cached in
|
|
2045
|
+
`~/.brindle/models-catalog.json` for a day. It is never fetched in air-gap mode
|
|
2046
|
+
or offline; brindle uses the cached copy, and says so if there isn't one. A profile
|
|
2047
|
+
with its own `base_url` is left alone, so OpenRouter-style ids still work.
|
|
2048
|
+
|
|
1950
2049
|
What to expect: a 30B-class local model does well on small, well-specified
|
|
1951
2050
|
tasks (the kind brindle hands out: one change, the test named up front) and on
|
|
1952
2051
|
reviews of modest diffs, and less well on long multi-step work. The native
|
|
@@ -2098,10 +2197,25 @@ you already have them, and won't change one that's committed. Each agent's first
|
|
|
2098
2197
|
message is a short warm-up with its instructions, because `agy` connects MCP servers
|
|
2099
2198
|
only once a conversation has started.
|
|
2100
2199
|
|
|
2101
|
-
**Permissions.** `agy` doesn't let hooks approve shell commands, so
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2200
|
+
**Permissions.** `agy` doesn't let hooks approve shell commands, so by default
|
|
2201
|
+
brindle runs Antigravity workers with `--dangerously-skip-permissions` and its
|
|
2202
|
+
own hook as the only gate (see "Antigravity" under "Permission policy"): a
|
|
2203
|
+
profile's `allowed_tools` shell entries and brindle's rules apply as they do
|
|
2204
|
+
for a Claude Code worker, and a call nothing allows is denied, with its
|
|
2205
|
+
supervisor told how you can allow it (a `brindle permissions allow` you run),
|
|
2206
|
+
never left at a prompt nobody is watching. A profile's `permission_mode` maps to agy's modes
|
|
2207
|
+
(`auto` to agy's Auto mode, `acceptEdits` to accept-edits, `plan` to plan;
|
|
2208
|
+
`dontAsk` runs brindle's hook as the only gate even in an interactive session).
|
|
2209
|
+
|
|
2210
|
+
An interactive agy session, or any agy agent under `"agy_approvals": "prompt"`,
|
|
2211
|
+
asks before running anything your own `agy` settings don't already allow, and
|
|
2212
|
+
the sidebar shows it as needing you. There a profile's `allowed_tools` don't
|
|
2213
|
+
apply by themselves: agy reads no hook's "allow", and its only allow list,
|
|
2214
|
+
`~/.gemini/antigravity-cli/settings.json`, is shared by every agy session, so brindle
|
|
2215
|
+
doesn't add one worker's rules there (another worker, or you, would get them too).
|
|
2216
|
+
`assign` and `handoff` name the `allowed_tools` that settings file doesn't already
|
|
2217
|
+
allow. To let such agents run tests and commit without asking,
|
|
2218
|
+
add rules to that file yourself, for example:
|
|
2105
2219
|
|
|
2106
2220
|
```json
|
|
2107
2221
|
{ "permissions": { "allow": ["command(uv run pytest)", "command(git status)",
|
|
@@ -2117,14 +2231,20 @@ add rules to `~/.gemini/antigravity-cli/settings.json`, for example:
|
|
|
2117
2231
|
and every agent's MCP server share it. Worktrees live in
|
|
2118
2232
|
`~/.brindle/worktrees/<repo>/<branch>`, and the base branch is recorded in git
|
|
2119
2233
|
config as `branch.<b>.brindle-base`.
|
|
2120
|
-
- **Messages
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2234
|
+
- **Messages wake a supervisor through brindle's own hook.** Your interactive
|
|
2235
|
+
Claude Code chat runs a small background waiter (an `asyncRewake` hook started
|
|
2236
|
+
at `SessionStart` and at every `Stop`). When a worker reports, or brindle has a
|
|
2237
|
+
"new messages" notice or an autopilot nudge, the waiter hands it over and Claude
|
|
2238
|
+
Code wakes the model with it: an idle chat starts a new turn, a busy one gets it
|
|
2239
|
+
between steps, and the chat shows one "Stop hook feedback" line. Each message
|
|
2240
|
+
is wrapped as `<brindle-msg nonce="..." from="developer agent w1">...</brindle-msg>`
|
|
2241
|
+
with a fresh random nonce every hand-over, and `from` comes from who really sent
|
|
2242
|
+
it, never from its text: a worker can't make its words read as brindle's or yours.
|
|
2243
|
+
A session paused on an expired cloud login gets its mail once the login is back.
|
|
2244
|
+
Workers and older sessions get messages through Claude Code's own inbox
|
|
2245
|
+
(another-session messages; a `crossSessionInbound` of `hold` or `refuse` in your
|
|
2246
|
+
Claude settings would hold or drop them). Typing into the pane is the fallback
|
|
2247
|
+
when neither is there (Codex, Antigravity, older Claude Code).
|
|
2128
2248
|
- **Agent status comes from hooks, not screen-scraping.** Guessing an agent's
|
|
2129
2249
|
state by pattern-matching terminal output breaks whenever a CLI redesigns its
|
|
2130
2250
|
interface. brindle launches Claude Code with `--settings` hooks
|