brindle 0.0.2__tar.gz → 0.0.3__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.
Files changed (88) hide show
  1. {brindle-0.0.2 → brindle-0.0.3}/PKG-INFO +75 -134
  2. {brindle-0.0.2 → brindle-0.0.3}/README.md +74 -133
  3. {brindle-0.0.2 → brindle-0.0.3}/pyproject.toml +1 -1
  4. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/agents.py +81 -24
  5. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/autopilot.py +67 -25
  6. brindle-0.0.3/src/brindle/ci_adapters.py +533 -0
  7. brindle-0.0.3/src/brindle/ci_client.py +1111 -0
  8. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/cli.py +136 -77
  9. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/config.py +1 -1
  10. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/cull.py +34 -12
  11. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/db.py +66 -11
  12. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/doctor.py +73 -13
  13. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/mcp_server.py +162 -41
  14. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/native/client.py +25 -1
  15. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/account.py +3 -2
  16. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/auth.py +24 -4
  17. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/license.py +47 -31
  18. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/providers.py +64 -19
  19. brindle-0.0.3/src/brindle/repo_cmds.py +74 -0
  20. brindle-0.0.3/src/brindle/repos.py +245 -0
  21. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/sessions.py +35 -4
  22. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/tasks.py +45 -11
  23. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/tmux.py +161 -36
  24. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/view.py +24 -1
  25. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/watch.py +34 -13
  26. brindle-0.0.2/src/brindle/ci.py +0 -948
  27. {brindle-0.0.2 → brindle-0.0.3}/.gitignore +0 -0
  28. {brindle-0.0.2 → brindle-0.0.3}/LICENSE +0 -0
  29. {brindle-0.0.2 → brindle-0.0.3}/SCHEDULE-A +0 -0
  30. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/__init__.py +0 -0
  31. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/__main__.py +0 -0
  32. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/account.py +0 -0
  33. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/airgap.py +0 -0
  34. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/antigravity.py +0 -0
  35. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/__init__.py +0 -0
  36. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/developer-codex.md +0 -0
  37. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/developer-heavy.md +0 -0
  38. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/developer-local.md +0 -0
  39. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/developer.md +0 -0
  40. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
  41. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/reviewer-local.md +0 -0
  42. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/reviewer.md +0 -0
  43. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/subagent.md +0 -0
  44. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/builtin_agents/supervisor.md +0 -0
  45. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/codemap.py +0 -0
  46. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/codex_hook.py +0 -0
  47. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/demo.py +0 -0
  48. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/detect.py +0 -0
  49. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/events.py +0 -0
  50. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/gates.py +0 -0
  51. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/git.py +0 -0
  52. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/history.py +0 -0
  53. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/inbox.py +0 -0
  54. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/learning.py +0 -0
  55. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/legacy.py +0 -0
  56. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/native/__init__.py +0 -0
  57. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/native/loop.py +0 -0
  58. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/native/permissions.py +0 -0
  59. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/native/runner.py +0 -0
  60. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/native/serve.py +0 -0
  61. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/native/tools.py +0 -0
  62. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/permissions.py +0 -0
  63. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pipeline.py +0 -0
  64. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/plugins.py +0 -0
  65. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/policy.py +0 -0
  66. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pool.py +0 -0
  67. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/__init__.py +0 -0
  68. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/_files.py +0 -0
  69. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/audit_chain.py +0 -0
  70. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/credentials.py +0 -0
  71. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/features.py +0 -0
  72. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/keys.py +0 -0
  73. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/learning.py +0 -0
  74. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/loopback.py +0 -0
  75. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/orgkey.py +0 -0
  76. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/settings_sync.py +0 -0
  77. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/team_events.py +0 -0
  78. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/pro/team_policy.py +0 -0
  79. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/procs.py +0 -0
  80. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/profiles.py +0 -0
  81. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/quota.py +0 -0
  82. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/savings.py +0 -0
  83. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/scratch.py +0 -0
  84. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/services.py +0 -0
  85. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/sidebar_follow.py +0 -0
  86. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/status_cache.py +0 -0
  87. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/usage.py +0 -0
  88. {brindle-0.0.2 → brindle-0.0.3}/src/brindle/workspaces.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brindle
3
- Version: 0.0.2
3
+ Version: 0.0.3
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
@@ -45,9 +45,9 @@ curl -fsSL pawdelta.com/brindle/install | sh
45
45
  brindle demo # watch it finish a practice repo in a few minutes
46
46
  ```
47
47
 
48
- ![brindle demo: a supervisor splits a goal between two workers, each branch is reviewed and merged, and both milestones turn green once their checks pass](https://pawdelta.com/brindle/brindle-demo.gif)
48
+ ![brindle demo: a supervisor splits a goal between two workers, each branch is reviewed and merged, and both milestones turn green once their checks pass](https://pawdelta.com/brindle/brindle-demo.gif?v=0.0.2)
49
49
 
50
- *`brindle demo`, recorded on brindle 0.14.6 (sped up 4×): two workers in parallel, each branch reviewed and merged, both milestones verified by their check commands.*
50
+ *`brindle demo`, recorded on brindle 0.0.2 (sped up 4×): two workers in parallel, each branch reviewed and merged, both milestones verified by their check commands.*
51
51
 
52
52
  - **Done means a command passed.** A goal is split into milestones, each with a
53
53
  check command that brindle runs itself. A milestone is verified when its check exits
@@ -89,13 +89,9 @@ 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.11.5`). A session started before an upgrade keeps
92
+ session shows it too (`brindle 0.0.3`). 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
- ### Upgrading from copse
96
-
97
- brindle was called copse until 0.0.1, and it doesn't read anything copse saved. Install `brindle`, then move `~/.copse` to `~/.brindle` and each repo's `.copse/` to `.brindle/`. Until you do, saved permission rules, policy and config there don't apply. `brindle doctor` lists every leftover it finds, including old `copse` entries in `.agents/hooks.json` and `mcp_config.json` to remove.
98
-
99
95
  ## Quick start
100
96
 
101
97
  ```sh
@@ -345,6 +341,7 @@ your own status line prints, so what you see doesn't change.
345
341
  | `brindle delegation [conservative\|balanced\|fast]` | how readily the supervisor delegates: fewest tokens, the default, or quickest |
346
342
  | `brindle transfer [REPO] [--from SESSION] [-b BRANCH]` | move a scratch session's work into a real repo |
347
343
  | `brindle ls [--all]` | workspaces and agents |
344
+ | `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") |
348
345
  | `brindle history [--limit N] [--kind K] [--all]` | durable log of worker results, reviews, merges and milestone checks |
349
346
  | `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 |
350
347
  | `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") |
@@ -380,6 +377,7 @@ knowing them helps when you tell the supervisor how to work.
380
377
  | `send_message` | any agent | message another agent; delivered when it's idle |
381
378
  | `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 |
382
379
  | `list_agents` / `list_tasks` / `list_agent_profiles` | supervisor | who's running, what's queued, which profiles exist |
380
+ | `list_repos` | supervisor | the repos attached to the session (brindle Pro); `repo=<alias>` on `assign`/`handoff` puts a worker there |
383
381
  | `cancel_task` | supervisor | cancel a queued task (and its dependents) to re-plan |
384
382
  | `workspace_diff` | supervisor | a worker branch's changes against its base |
385
383
  | `request_review` / `submit_review` | supervisor / reviewer | start a reviewer on a branch / record its verdict |
@@ -484,7 +482,7 @@ Autopilot, merge gates and cleanup:
484
482
  | `sidebar` | `"left"` | where the dashboard sits in each window: `"left"` of the chat, or `"bottom"` (full-width rows under it) |
485
483
  | `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 |
486
484
  | `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) |
487
- | `pr_footer` | `true` | `brindle pr` and `brindle ci` end the PR description with one line: "🌲 Built in parallel and verified with brindle" (a link). `false` leaves it out. Never added to commit messages |
485
+ | `pr_footer` | `true` | `brindle pr` ends the PR description with one line: "🌲 Built in parallel and verified with brindle" (a link). `false` leaves it out. Never added to commit messages |
488
486
  | `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 |
489
487
  | `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 |
490
488
  | `learning_candidates` | `[]` | the profile names the hosted learner may pick from |
@@ -672,8 +670,9 @@ how to use them, and the next step to get the rest:
672
670
  |---|---|---|
673
671
  | hosted learning | Pro | on by itself; `brindle learning` |
674
672
  | per-worktree services | Pro | `"services"` in `.brindle/config.json` |
673
+ | several repos in one session | Pro | `brindle repo add <path>` |
675
674
  | org policies + audit feed | Team | `brindle account org policy` |
676
- | Brindle-CI | Team | `brindle ci init` |
675
+ | Brindle-CI | Team | back in a later release |
677
676
  | audit log, air-gap | Enterprise | `brindle audit verify`, `"airgap": true` |
678
677
 
679
678
  ```sh
@@ -689,6 +688,21 @@ brindle account org member policy-role <member> <role|none> # give a member a
689
688
  brindle account org company [link <org_id> | unlink] # link orgs you own into one company; learning is pooled only within it
690
689
  ```
691
690
 
691
+ ### Several repos in one session (brindle Pro)
692
+
693
+ A session can work across several repos. Attach another local repo you own
694
+ to the running session and the supervisor can put workers there: each gets a
695
+ worktree in that repo, that repo's own checks and review, and merges into that
696
+ repo's branch. A task may depend on one in another repo, a milestone check can
697
+ run in another repo (`check@<alias>: <command>` in `goals.md`, or `repo` in
698
+ `set_goal`), and the sidebar and `brindle ls` group workers by repo.
699
+
700
+ ```sh
701
+ brindle repo add ../pawdelta-web --name web # attach a repo to the current session (alias: web)
702
+ brindle repo ls # the repos attached to this session
703
+ brindle repo rm web # detach it (its worktrees and branches stay)
704
+ ```
705
+
692
706
  Setting up a team takes no sign-up form:
693
707
 
694
708
  ```sh
@@ -792,129 +806,27 @@ brindle audit pubkey # this install's public key (
792
806
  one altered in place (hash or signature), one removed, inserted or reordered
793
807
  (seq and prev_hash), or a truncated tail. Verifying and exporting never need
794
808
  the entitlement, so a log keeps its value after a plan lapses.
795
- ### Brindle-CI: issues into pull requests
796
-
797
- brindle Team can run brindle with nobody at a terminal. `brindle ci run` cuts a
798
- `brindle/ci-<issue or slug>` branch, starts a supervisor with autopilot on in a
799
- detached tmux session, gives it the goal, and waits until every milestone's
800
- check passes. Then it pushes the branch and opens the pull request with `gh`
801
- (the body lists the goal, the milestones and their checks, and `Closes #N`
802
- for an issue), prints the PR URL and exits 0; with `--bundle` it writes the
803
- branch to a file instead, for `brindle ci publish` (see below). It exits 1, with what happened,
804
- when the supervisor asks for a decision (`need_user`: the question is the
805
- reason), stalls, or runs out of time. The session and its workers are always
806
- stopped at the end, and a JSON summary goes to `$GITHUB_STEP_SUMMARY` when
807
- that is set.
808
809
 
809
- ```sh
810
- brindle ci run --issue 42 # the goal is the issue's title and body
811
- brindle ci run --goal "Add a /health endpoint" # or typed; a goals.md-shaped text brings its milestones
812
- brindle ci run --goal-file .brindle/goals.md --timeout 90 --max-workers 2 --base develop --no-pr
813
- brindle ci init --label brindle # the GitHub Actions workflow (see below)
814
-
815
- # The same run in three steps, so no secret worth stealing is near the agents:
816
- brindle ci entitle --out ent.jwt # uses BRINDLE_PRO_TOKEN, then exits
817
- brindle ci run --issue 42 --entitlement ent.jwt --bundle out/brindle.bundle # no CI token, no push token
818
- brindle ci publish out/brindle.bundle --repo acme/api # elsewhere: pushes and opens the PR
819
- ```
810
+ ### Brindle-CI
820
811
 
821
- With `--issue` and `--goal`, the supervisor derives the milestones and their
822
- checks itself; a goals.md-shaped goal (`# Goal`, `## Milestone`, `check:`) is
823
- recorded as written. `--max-workers` sets `max_agents` in the repo's
824
- `.brindle/config.local.json`.
825
-
826
- **Who is trusted with what.** Agents run your repo's code: its tests, its
827
- scripts, and whatever an issue talks them into. They run as the same user as
828
- brindle, so treat everything on that machine as theirs to read: environment
829
- variables, files, git and `gh` settings. brindle does take its tokens out of the
830
- environment before agents start and pushes from a clean copy of the commits,
831
- but that only makes theft harder. What actually protects a secret is that it
832
- isn't on the machine while agents run. So the work can be split:
833
-
834
- 1. `brindle ci entitle --out FILE` exchanges `BRINDLE_PRO_TOKEN` for the signed,
835
- short-lived entitlement and writes it to a file (mode 0600). Run it on a
836
- different machine from the agents (the workflow gives it its own job):
837
- on hosted runners agents have sudo, and the runner holds every secret of
838
- the job they run in. `brindle ci run` reads the file and deletes it before
839
- any agent starts.
840
- 2. `brindle ci run --entitlement FILE --bundle PATH` does the work with no CI
841
- token and no token that can write to GitHub (`--issue` needs one that can
842
- read). When the goal is verified it writes the new commits to PATH as a git
843
- bundle, and `PATH.json` with the branch, base, title and body of the pull
844
- request. `--bundle` implies `--no-pr`: nothing is pushed.
845
- 3. `brindle ci publish PATH [--repo owner/name]` runs where no agent ever ran,
846
- with the token that can push. It verifies the bundle, fetches its one
847
- `brindle/ci-…` branch into a fresh bare repo, pushes that branch to
848
- `https://github.com/<repo>` (default: `$GITHUB_REPOSITORY`) and opens the
849
- pull request with `gh`. It never checks out or runs repo code, hooks or
850
- agents, and it refuses a bundle whose branch isn't a `brindle/ci-` branch, so
851
- a run can't publish over `main`. The pull request targets `--base`, else
852
- the repository's default branch; the bundle doesn't get to choose. A
853
- hostile run can still add commits to an existing `brindle/ci-` branch.
854
-
855
- The workflow `brindle ci init` writes pins brindle to the version that wrote it,
856
- since the publishing job holds a write token. The short-lived entitlement
857
- passes between jobs as an artifact kept one day. A CI entitlement expires
858
- three hours after it's issued, so anyone who can download the repo's
859
- artifacts could use it for that long at most.
860
-
861
- `brindle ci run` without `--bundle` still pushes and opens the pull request
862
- itself. Use that only where you trust the repo's code and everyone who can
863
- steer the agents.
864
-
865
- `brindle ci init` writes `.github/workflows/brindle.yml`, which runs on
866
- `workflow_dispatch` and whenever an issue gets the label (`brindle` by
867
- default). It has three jobs:
868
-
869
- - `entitle` (no permissions) runs `brindle ci entitle` with `BRINDLE_PRO_TOKEN`
870
- on a machine that checks out and runs nothing from the repo. It hands the
871
- short-lived entitlement (three hours) to the next job as an artifact kept
872
- one day, so the CI token is never on the agents' machine.
873
- - `run` (permissions: `contents: read`, `issues: read`) checks the repo out
874
- without keeping credentials, installs tmux, brindle (pinned to the version
875
- that wrote the workflow) and Claude Code, then runs `brindle ci run --issue
876
- <number> --entitlement … --bundle …` with only `ANTHROPIC_API_KEY` and a
877
- read-only `GH_TOKEN`. brindle deletes the entitlement file before any agent
878
- starts. The job uploads the bundle as an artifact.
879
- - `publish` (permissions:
880
- `contents: write`, `pull-requests: write`) starts on a fresh machine, checks
881
- nothing out, downloads the artifact and runs `brindle ci publish`, targeting
882
- the repository's default branch.
883
-
884
- `brindle ci init` won't
885
- overwrite an existing file without `--force`. The workflow needs two secrets,
886
- `BRINDLE_PRO_TOKEN` (an org CI token, below) and `ANTHROPIC_API_KEY`, and the
887
- repo's Actions settings must allow GitHub Actions to create pull requests.
888
- The model API key is the one secret agents must have; give it a spending
889
- limit.
890
-
891
- An org admin creates the CI token; it is shown once, so store it straight away:
812
+ Brindle-CI (brindle Team) brings brindle into your CI: it fixes broken builds,
813
+ turns issues into pull requests whose checks it has verified, and reviews pull
814
+ requests with evidence. It runs on your own CI with your own model keys; the
815
+ hosted control plane decides what each run does and what it reports. The
816
+ workflows call these commands:
892
817
 
893
- ```sh
894
- brindle account org ci-token create "acme/api actions" --org org_... # prints cpc_... once
895
- gh secret set BRINDLE_PRO_TOKEN # paste it
896
- brindle account org ci-token list --org org_... # names, status, last used; never the secret
897
- brindle account org ci-token revoke ct_... --org org_... # CI stops at its next run
898
- ```
818
+ - `brindle ci init`: set a repository up in one go (the GitHub App, the secrets,
819
+ the workflows as a pull request, then a doctor check).
820
+ - `brindle ci doctor`: which provider CLIs and credential names a runner has, and
821
+ which providers CI may use on this repository.
822
+ - `brindle ci start`: start a run for an issue, a goal text or a dispatched run,
823
+ or a validation of a pull request.
824
+ - `brindle ci run`: run what was started: the supervisor and the result upload,
825
+ or the pull request's checks and reviews and the evidence upload.
826
+ - `brindle ci report`: tell the service how the workflow's jobs ended.
899
827
 
900
- On every run the token is presented to the backend, which checks it and the
901
- org's live plan and returns a signed entitlement. `brindle ci run` verifies it in
902
- memory and writes nothing to disk; `brindle ci entitle` writes only the signed
903
- entitlement, which expires soon, never the token. The token doesn't rotate, so one secret
904
- keeps working until it is revoked or the org's plan no longer includes CI. A
905
- refresh token from `brindle account login` won't do: it rotates on use.
906
-
907
- Two things to know before you add the label to your repo:
908
-
909
- - **Who can apply the label.** The issue body steers an unattended agent
910
- whose work becomes a pull request on a `brindle/ci-` branch. Only people you
911
- trust with write access should be able to apply the trigger label; on a
912
- public repo, anyone who can write the issue text is choosing what the agent
913
- is told to do. Review the pull request like any other before merging it.
914
- - **CI on the pull request.** A PR opened with the workflow's own
915
- `GITHUB_TOKEN` doesn't trigger the repo's other workflows. If you want your
916
- checks to run on brindle's PRs, set `GH_TOKEN` in the `publish` job to a
917
- GitHub App installation token or a personal access token instead.
828
+ Credential names are reported, never values. On a repository owned by a GitHub
829
+ organization, a provider signed in only with a personal subscription is not used.
918
830
 
919
831
  **Closing and cleaning up.** Press `x` on an agent in the sidebar (twice for one
920
832
  that's still running) or run `brindle close <id>` to stop it and hide it. Stopping means
@@ -1056,14 +968,34 @@ change files under a directory without prompts.
1056
968
  brindle only offers a profile whose CLI is installed and signed in. A profile on a
1057
969
  CLI that isn't signed in is left out of the supervisor's profile list and of
1058
970
  routing by weight, and naming it directly stops with how to sign in
1059
- (`claude auth login`, `codex login`) instead of opening the CLI's login screen.
1060
- `brindle doctor` shows each CLI's sign-in. Keys set in the environment
1061
- (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY` and the like) count as signed in.
1062
- `agy` has no sign-in status command, so brindle can't tell when it's signed out:
1063
- sign in once by running `agy` yourself. A worker that no hook reports on (Codex)
971
+ (`claude auth login`, `codex login`, running `agy`) instead of opening the CLI's
972
+ login screen. `brindle doctor` shows, for each installed CLI, how it's signed in
973
+ and whether a quota limit is in effect. Sign-in is its own login, an environment
974
+ key (by name, never its value) set in your shell or in a profile's
975
+ `env.NAME: value` lines, signed out, or unknown when the status check gives no
976
+ answer. Keys set in the environment (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
977
+ `GEMINI_API_KEY` and the like) count as signed in; see the table below for what
978
+ each CLI takes. A worker that no hook reports on (Codex)
1064
979
  and that shows nothing new for 10 minutes without reporting, for example because
1065
980
  it's signed in without a plan that includes it, is reported to its supervisor.
1066
981
 
982
+ #### Key login
983
+
984
+ | Provider | Key login (environment variables) |
985
+ |---|---|
986
+ | `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`) |
987
+ | `codex` | yes: `OPENAI_API_KEY`, `CODEX_API_KEY` |
988
+ | `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 |
989
+
990
+ Workers that run unattended should use key login where the CLI supports it.
991
+ A personal login is one interactive session with one quota shared by every
992
+ worker: when it expires or its quota runs out, every worker on it stalls until
993
+ someone signs in again in a browser. A key gives workers their own limits and
994
+ needs no browser step. Put the key in your environment, or in a profile's
995
+ `env.NAME: value` lines in `~/.brindle/agents` (never in the repo), and
996
+ `brindle doctor` names the variable each CLI gets, from your shell or from
997
+ which profiles.
998
+
1067
999
  ### Cheap workers
1068
1000
 
1069
1001
  By default a Claude Code worker loads everything your own `claude` does: your
@@ -1298,6 +1230,15 @@ curl -fsSL https://antigravity.google/cli/install.sh | bash
1298
1230
  agy # sign in with your Google account, then quit
1299
1231
  ```
1300
1232
 
1233
+ To run unattended workers without a browser login, `agy` (1.1.13 or newer) can use
1234
+ a Gemini API key instead: put `"modelProvider": "gemini"` in
1235
+ `~/.gemini/antigravity-cli/settings.json` and set `GEMINI_API_KEY`, in your
1236
+ environment or in a profile's `env.GEMINI_API_KEY: ...` line (keep the key out of
1237
+ the repo). The key alone does nothing: without `modelProvider`, `agy` still asks
1238
+ you to sign in. `agy` takes no other key or token from the environment; its
1239
+ Google Cloud, Workforce Identity and Application Default Credentials sign-ins are
1240
+ chosen on its sign-in screen.
1241
+
1301
1242
  Then run the whole session on it with `brindle --provider antigravity`, or mix models:
1302
1243
  give a profile `provider: antigravity` (for example a `gemini-reviewer` for a second
1303
1244
  model's review) and the supervisor can hand it tasks.
@@ -15,9 +15,9 @@ curl -fsSL pawdelta.com/brindle/install | sh
15
15
  brindle demo # watch it finish a practice repo in a few minutes
16
16
  ```
17
17
 
18
- ![brindle demo: a supervisor splits a goal between two workers, each branch is reviewed and merged, and both milestones turn green once their checks pass](https://pawdelta.com/brindle/brindle-demo.gif)
18
+ ![brindle demo: a supervisor splits a goal between two workers, each branch is reviewed and merged, and both milestones turn green once their checks pass](https://pawdelta.com/brindle/brindle-demo.gif?v=0.0.2)
19
19
 
20
- *`brindle demo`, recorded on brindle 0.14.6 (sped up 4×): two workers in parallel, each branch reviewed and merged, both milestones verified by their check commands.*
20
+ *`brindle demo`, recorded on brindle 0.0.2 (sped up 4×): two workers in parallel, each branch reviewed and merged, both milestones verified by their check commands.*
21
21
 
22
22
  - **Done means a command passed.** A goal is split into milestones, each with a
23
23
  check command that brindle runs itself. A milestone is verified when its check exits
@@ -59,13 +59,9 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
59
59
  brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
60
60
 
61
61
  `brindle --version` prints the installed version; the tmux status bar of every brindle
62
- session shows it too (`brindle 0.11.5`). A session started before an upgrade keeps
62
+ session shows it too (`brindle 0.0.3`). A session started before an upgrade keeps
63
63
  running the old code, and shows the old number, until you restart it.
64
64
 
65
- ### Upgrading from copse
66
-
67
- brindle was called copse until 0.0.1, and it doesn't read anything copse saved. Install `brindle`, then move `~/.copse` to `~/.brindle` and each repo's `.copse/` to `.brindle/`. Until you do, saved permission rules, policy and config there don't apply. `brindle doctor` lists every leftover it finds, including old `copse` entries in `.agents/hooks.json` and `mcp_config.json` to remove.
68
-
69
65
  ## Quick start
70
66
 
71
67
  ```sh
@@ -315,6 +311,7 @@ your own status line prints, so what you see doesn't change.
315
311
  | `brindle delegation [conservative\|balanced\|fast]` | how readily the supervisor delegates: fewest tokens, the default, or quickest |
316
312
  | `brindle transfer [REPO] [--from SESSION] [-b BRANCH]` | move a scratch session's work into a real repo |
317
313
  | `brindle ls [--all]` | workspaces and agents |
314
+ | `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") |
318
315
  | `brindle history [--limit N] [--kind K] [--all]` | durable log of worker results, reviews, merges and milestone checks |
319
316
  | `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 |
320
317
  | `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") |
@@ -350,6 +347,7 @@ knowing them helps when you tell the supervisor how to work.
350
347
  | `send_message` | any agent | message another agent; delivered when it's idle |
351
348
  | `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 |
352
349
  | `list_agents` / `list_tasks` / `list_agent_profiles` | supervisor | who's running, what's queued, which profiles exist |
350
+ | `list_repos` | supervisor | the repos attached to the session (brindle Pro); `repo=<alias>` on `assign`/`handoff` puts a worker there |
353
351
  | `cancel_task` | supervisor | cancel a queued task (and its dependents) to re-plan |
354
352
  | `workspace_diff` | supervisor | a worker branch's changes against its base |
355
353
  | `request_review` / `submit_review` | supervisor / reviewer | start a reviewer on a branch / record its verdict |
@@ -454,7 +452,7 @@ Autopilot, merge gates and cleanup:
454
452
  | `sidebar` | `"left"` | where the dashboard sits in each window: `"left"` of the chat, or `"bottom"` (full-width rows under it) |
455
453
  | `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 |
456
454
  | `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) |
457
- | `pr_footer` | `true` | `brindle pr` and `brindle ci` end the PR description with one line: "🌲 Built in parallel and verified with brindle" (a link). `false` leaves it out. Never added to commit messages |
455
+ | `pr_footer` | `true` | `brindle pr` ends the PR description with one line: "🌲 Built in parallel and verified with brindle" (a link). `false` leaves it out. Never added to commit messages |
458
456
  | `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 |
459
457
  | `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 |
460
458
  | `learning_candidates` | `[]` | the profile names the hosted learner may pick from |
@@ -642,8 +640,9 @@ how to use them, and the next step to get the rest:
642
640
  |---|---|---|
643
641
  | hosted learning | Pro | on by itself; `brindle learning` |
644
642
  | per-worktree services | Pro | `"services"` in `.brindle/config.json` |
643
+ | several repos in one session | Pro | `brindle repo add <path>` |
645
644
  | org policies + audit feed | Team | `brindle account org policy` |
646
- | Brindle-CI | Team | `brindle ci init` |
645
+ | Brindle-CI | Team | back in a later release |
647
646
  | audit log, air-gap | Enterprise | `brindle audit verify`, `"airgap": true` |
648
647
 
649
648
  ```sh
@@ -659,6 +658,21 @@ brindle account org member policy-role <member> <role|none> # give a member a
659
658
  brindle account org company [link <org_id> | unlink] # link orgs you own into one company; learning is pooled only within it
660
659
  ```
661
660
 
661
+ ### Several repos in one session (brindle Pro)
662
+
663
+ A session can work across several repos. Attach another local repo you own
664
+ to the running session and the supervisor can put workers there: each gets a
665
+ worktree in that repo, that repo's own checks and review, and merges into that
666
+ repo's branch. A task may depend on one in another repo, a milestone check can
667
+ run in another repo (`check@<alias>: <command>` in `goals.md`, or `repo` in
668
+ `set_goal`), and the sidebar and `brindle ls` group workers by repo.
669
+
670
+ ```sh
671
+ brindle repo add ../pawdelta-web --name web # attach a repo to the current session (alias: web)
672
+ brindle repo ls # the repos attached to this session
673
+ brindle repo rm web # detach it (its worktrees and branches stay)
674
+ ```
675
+
662
676
  Setting up a team takes no sign-up form:
663
677
 
664
678
  ```sh
@@ -762,129 +776,27 @@ brindle audit pubkey # this install's public key (
762
776
  one altered in place (hash or signature), one removed, inserted or reordered
763
777
  (seq and prev_hash), or a truncated tail. Verifying and exporting never need
764
778
  the entitlement, so a log keeps its value after a plan lapses.
765
- ### Brindle-CI: issues into pull requests
766
-
767
- brindle Team can run brindle with nobody at a terminal. `brindle ci run` cuts a
768
- `brindle/ci-<issue or slug>` branch, starts a supervisor with autopilot on in a
769
- detached tmux session, gives it the goal, and waits until every milestone's
770
- check passes. Then it pushes the branch and opens the pull request with `gh`
771
- (the body lists the goal, the milestones and their checks, and `Closes #N`
772
- for an issue), prints the PR URL and exits 0; with `--bundle` it writes the
773
- branch to a file instead, for `brindle ci publish` (see below). It exits 1, with what happened,
774
- when the supervisor asks for a decision (`need_user`: the question is the
775
- reason), stalls, or runs out of time. The session and its workers are always
776
- stopped at the end, and a JSON summary goes to `$GITHUB_STEP_SUMMARY` when
777
- that is set.
778
779
 
779
- ```sh
780
- brindle ci run --issue 42 # the goal is the issue's title and body
781
- brindle ci run --goal "Add a /health endpoint" # or typed; a goals.md-shaped text brings its milestones
782
- brindle ci run --goal-file .brindle/goals.md --timeout 90 --max-workers 2 --base develop --no-pr
783
- brindle ci init --label brindle # the GitHub Actions workflow (see below)
784
-
785
- # The same run in three steps, so no secret worth stealing is near the agents:
786
- brindle ci entitle --out ent.jwt # uses BRINDLE_PRO_TOKEN, then exits
787
- brindle ci run --issue 42 --entitlement ent.jwt --bundle out/brindle.bundle # no CI token, no push token
788
- brindle ci publish out/brindle.bundle --repo acme/api # elsewhere: pushes and opens the PR
789
- ```
780
+ ### Brindle-CI
790
781
 
791
- With `--issue` and `--goal`, the supervisor derives the milestones and their
792
- checks itself; a goals.md-shaped goal (`# Goal`, `## Milestone`, `check:`) is
793
- recorded as written. `--max-workers` sets `max_agents` in the repo's
794
- `.brindle/config.local.json`.
795
-
796
- **Who is trusted with what.** Agents run your repo's code: its tests, its
797
- scripts, and whatever an issue talks them into. They run as the same user as
798
- brindle, so treat everything on that machine as theirs to read: environment
799
- variables, files, git and `gh` settings. brindle does take its tokens out of the
800
- environment before agents start and pushes from a clean copy of the commits,
801
- but that only makes theft harder. What actually protects a secret is that it
802
- isn't on the machine while agents run. So the work can be split:
803
-
804
- 1. `brindle ci entitle --out FILE` exchanges `BRINDLE_PRO_TOKEN` for the signed,
805
- short-lived entitlement and writes it to a file (mode 0600). Run it on a
806
- different machine from the agents (the workflow gives it its own job):
807
- on hosted runners agents have sudo, and the runner holds every secret of
808
- the job they run in. `brindle ci run` reads the file and deletes it before
809
- any agent starts.
810
- 2. `brindle ci run --entitlement FILE --bundle PATH` does the work with no CI
811
- token and no token that can write to GitHub (`--issue` needs one that can
812
- read). When the goal is verified it writes the new commits to PATH as a git
813
- bundle, and `PATH.json` with the branch, base, title and body of the pull
814
- request. `--bundle` implies `--no-pr`: nothing is pushed.
815
- 3. `brindle ci publish PATH [--repo owner/name]` runs where no agent ever ran,
816
- with the token that can push. It verifies the bundle, fetches its one
817
- `brindle/ci-…` branch into a fresh bare repo, pushes that branch to
818
- `https://github.com/<repo>` (default: `$GITHUB_REPOSITORY`) and opens the
819
- pull request with `gh`. It never checks out or runs repo code, hooks or
820
- agents, and it refuses a bundle whose branch isn't a `brindle/ci-` branch, so
821
- a run can't publish over `main`. The pull request targets `--base`, else
822
- the repository's default branch; the bundle doesn't get to choose. A
823
- hostile run can still add commits to an existing `brindle/ci-` branch.
824
-
825
- The workflow `brindle ci init` writes pins brindle to the version that wrote it,
826
- since the publishing job holds a write token. The short-lived entitlement
827
- passes between jobs as an artifact kept one day. A CI entitlement expires
828
- three hours after it's issued, so anyone who can download the repo's
829
- artifacts could use it for that long at most.
830
-
831
- `brindle ci run` without `--bundle` still pushes and opens the pull request
832
- itself. Use that only where you trust the repo's code and everyone who can
833
- steer the agents.
834
-
835
- `brindle ci init` writes `.github/workflows/brindle.yml`, which runs on
836
- `workflow_dispatch` and whenever an issue gets the label (`brindle` by
837
- default). It has three jobs:
838
-
839
- - `entitle` (no permissions) runs `brindle ci entitle` with `BRINDLE_PRO_TOKEN`
840
- on a machine that checks out and runs nothing from the repo. It hands the
841
- short-lived entitlement (three hours) to the next job as an artifact kept
842
- one day, so the CI token is never on the agents' machine.
843
- - `run` (permissions: `contents: read`, `issues: read`) checks the repo out
844
- without keeping credentials, installs tmux, brindle (pinned to the version
845
- that wrote the workflow) and Claude Code, then runs `brindle ci run --issue
846
- <number> --entitlement … --bundle …` with only `ANTHROPIC_API_KEY` and a
847
- read-only `GH_TOKEN`. brindle deletes the entitlement file before any agent
848
- starts. The job uploads the bundle as an artifact.
849
- - `publish` (permissions:
850
- `contents: write`, `pull-requests: write`) starts on a fresh machine, checks
851
- nothing out, downloads the artifact and runs `brindle ci publish`, targeting
852
- the repository's default branch.
853
-
854
- `brindle ci init` won't
855
- overwrite an existing file without `--force`. The workflow needs two secrets,
856
- `BRINDLE_PRO_TOKEN` (an org CI token, below) and `ANTHROPIC_API_KEY`, and the
857
- repo's Actions settings must allow GitHub Actions to create pull requests.
858
- The model API key is the one secret agents must have; give it a spending
859
- limit.
860
-
861
- An org admin creates the CI token; it is shown once, so store it straight away:
782
+ Brindle-CI (brindle Team) brings brindle into your CI: it fixes broken builds,
783
+ turns issues into pull requests whose checks it has verified, and reviews pull
784
+ requests with evidence. It runs on your own CI with your own model keys; the
785
+ hosted control plane decides what each run does and what it reports. The
786
+ workflows call these commands:
862
787
 
863
- ```sh
864
- brindle account org ci-token create "acme/api actions" --org org_... # prints cpc_... once
865
- gh secret set BRINDLE_PRO_TOKEN # paste it
866
- brindle account org ci-token list --org org_... # names, status, last used; never the secret
867
- brindle account org ci-token revoke ct_... --org org_... # CI stops at its next run
868
- ```
788
+ - `brindle ci init`: set a repository up in one go (the GitHub App, the secrets,
789
+ the workflows as a pull request, then a doctor check).
790
+ - `brindle ci doctor`: which provider CLIs and credential names a runner has, and
791
+ which providers CI may use on this repository.
792
+ - `brindle ci start`: start a run for an issue, a goal text or a dispatched run,
793
+ or a validation of a pull request.
794
+ - `brindle ci run`: run what was started: the supervisor and the result upload,
795
+ or the pull request's checks and reviews and the evidence upload.
796
+ - `brindle ci report`: tell the service how the workflow's jobs ended.
869
797
 
870
- On every run the token is presented to the backend, which checks it and the
871
- org's live plan and returns a signed entitlement. `brindle ci run` verifies it in
872
- memory and writes nothing to disk; `brindle ci entitle` writes only the signed
873
- entitlement, which expires soon, never the token. The token doesn't rotate, so one secret
874
- keeps working until it is revoked or the org's plan no longer includes CI. A
875
- refresh token from `brindle account login` won't do: it rotates on use.
876
-
877
- Two things to know before you add the label to your repo:
878
-
879
- - **Who can apply the label.** The issue body steers an unattended agent
880
- whose work becomes a pull request on a `brindle/ci-` branch. Only people you
881
- trust with write access should be able to apply the trigger label; on a
882
- public repo, anyone who can write the issue text is choosing what the agent
883
- is told to do. Review the pull request like any other before merging it.
884
- - **CI on the pull request.** A PR opened with the workflow's own
885
- `GITHUB_TOKEN` doesn't trigger the repo's other workflows. If you want your
886
- checks to run on brindle's PRs, set `GH_TOKEN` in the `publish` job to a
887
- GitHub App installation token or a personal access token instead.
798
+ Credential names are reported, never values. On a repository owned by a GitHub
799
+ organization, a provider signed in only with a personal subscription is not used.
888
800
 
889
801
  **Closing and cleaning up.** Press `x` on an agent in the sidebar (twice for one
890
802
  that's still running) or run `brindle close <id>` to stop it and hide it. Stopping means
@@ -1026,14 +938,34 @@ change files under a directory without prompts.
1026
938
  brindle only offers a profile whose CLI is installed and signed in. A profile on a
1027
939
  CLI that isn't signed in is left out of the supervisor's profile list and of
1028
940
  routing by weight, and naming it directly stops with how to sign in
1029
- (`claude auth login`, `codex login`) instead of opening the CLI's login screen.
1030
- `brindle doctor` shows each CLI's sign-in. Keys set in the environment
1031
- (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY` and the like) count as signed in.
1032
- `agy` has no sign-in status command, so brindle can't tell when it's signed out:
1033
- sign in once by running `agy` yourself. A worker that no hook reports on (Codex)
941
+ (`claude auth login`, `codex login`, running `agy`) instead of opening the CLI's
942
+ login screen. `brindle doctor` shows, for each installed CLI, how it's signed in
943
+ and whether a quota limit is in effect. Sign-in is its own login, an environment
944
+ key (by name, never its value) set in your shell or in a profile's
945
+ `env.NAME: value` lines, signed out, or unknown when the status check gives no
946
+ answer. Keys set in the environment (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
947
+ `GEMINI_API_KEY` and the like) count as signed in; see the table below for what
948
+ each CLI takes. A worker that no hook reports on (Codex)
1034
949
  and that shows nothing new for 10 minutes without reporting, for example because
1035
950
  it's signed in without a plan that includes it, is reported to its supervisor.
1036
951
 
952
+ #### Key login
953
+
954
+ | Provider | Key login (environment variables) |
955
+ |---|---|
956
+ | `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`) |
957
+ | `codex` | yes: `OPENAI_API_KEY`, `CODEX_API_KEY` |
958
+ | `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 |
959
+
960
+ Workers that run unattended should use key login where the CLI supports it.
961
+ A personal login is one interactive session with one quota shared by every
962
+ worker: when it expires or its quota runs out, every worker on it stalls until
963
+ someone signs in again in a browser. A key gives workers their own limits and
964
+ needs no browser step. Put the key in your environment, or in a profile's
965
+ `env.NAME: value` lines in `~/.brindle/agents` (never in the repo), and
966
+ `brindle doctor` names the variable each CLI gets, from your shell or from
967
+ which profiles.
968
+
1037
969
  ### Cheap workers
1038
970
 
1039
971
  By default a Claude Code worker loads everything your own `claude` does: your
@@ -1268,6 +1200,15 @@ curl -fsSL https://antigravity.google/cli/install.sh | bash
1268
1200
  agy # sign in with your Google account, then quit
1269
1201
  ```
1270
1202
 
1203
+ To run unattended workers without a browser login, `agy` (1.1.13 or newer) can use
1204
+ a Gemini API key instead: put `"modelProvider": "gemini"` in
1205
+ `~/.gemini/antigravity-cli/settings.json` and set `GEMINI_API_KEY`, in your
1206
+ environment or in a profile's `env.GEMINI_API_KEY: ...` line (keep the key out of
1207
+ the repo). The key alone does nothing: without `modelProvider`, `agy` still asks
1208
+ you to sign in. `agy` takes no other key or token from the environment; its
1209
+ Google Cloud, Workforce Identity and Application Default Credentials sign-ins are
1210
+ chosen on its sign-in screen.
1211
+
1271
1212
  Then run the whole session on it with `brindle --provider antigravity`, or mix models:
1272
1213
  give a profile `provider: antigravity` (for example a `gemini-reviewer` for a second
1273
1214
  model's review) and the supervisor can hand it tasks.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brindle"
3
- version = "0.0.2"
3
+ version = "0.0.3"
4
4
  description = "Run coding agents in parallel, each on its own git branch, with a supervisor that delegates, reviews, and merges"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"