brindle 0.3.8__tar.gz → 0.3.11__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 (132) hide show
  1. {brindle-0.3.8 → brindle-0.3.11}/.gitignore +2 -0
  2. {brindle-0.3.8 → brindle-0.3.11}/PKG-INFO +8 -2
  3. {brindle-0.3.8 → brindle-0.3.11}/README.md +6 -1
  4. {brindle-0.3.8 → brindle-0.3.11}/pyproject.toml +2 -1
  5. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/agents.py +3 -1
  6. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/cli.py +29 -0
  7. brindle-0.3.11/src/brindle/cloud_credential.py +626 -0
  8. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/company_identity.py +46 -7
  9. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/company_login.py +129 -19
  10. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/config.py +2 -1
  11. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/doctor.py +4 -1
  12. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/gates.py +3 -1
  13. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/mcp_server.py +19 -1
  14. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/policy.py +4 -0
  15. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/account.py +17 -3
  16. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/auth.py +18 -7
  17. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/license.py +63 -10
  18. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/managed_models.py +1 -1
  19. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/org_profiles.py +2 -1
  20. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/status.py +20 -1
  21. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/team_policy.py +129 -17
  22. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/providers.py +5 -2
  23. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/tmux.py +5 -1
  24. brindle-0.3.11/src/brindle/updates.py +72 -0
  25. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/watch.py +11 -3
  26. {brindle-0.3.8 → brindle-0.3.11}/LICENSE +0 -0
  27. {brindle-0.3.8 → brindle-0.3.11}/SCHEDULE-A +0 -0
  28. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/__init__.py +0 -0
  29. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/__main__.py +0 -0
  30. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/account.py +0 -0
  31. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/airgap.py +0 -0
  32. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/antigravity.py +0 -0
  33. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/autopilot.py +0 -0
  34. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/budget.py +0 -0
  35. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/__init__.py +0 -0
  36. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/backend-auditor.md +0 -0
  37. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/developer-codex.md +0 -0
  38. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/developer-fable.md +0 -0
  39. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/developer-gemini.md +0 -0
  40. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/developer-heavy.md +0 -0
  41. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/developer-light.md +0 -0
  42. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/developer-local.md +0 -0
  43. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/developer.md +0 -0
  44. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/qa.md +0 -0
  45. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
  46. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/reviewer-gemini.md +0 -0
  47. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/reviewer-local.md +0 -0
  48. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/reviewer.md +0 -0
  49. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/subagent.md +0 -0
  50. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_agents/supervisor.md +0 -0
  51. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_rules/__init__.py +0 -0
  52. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_rules/security/backend.md +0 -0
  53. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
  54. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/builtin_rules/tests/only.md +0 -0
  55. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/ci_adapters.py +0 -0
  56. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/ci_client.py +0 -0
  57. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/ci_federation.py +0 -0
  58. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/ci_hosts.py +0 -0
  59. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/codemap.py +0 -0
  60. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/codex_hook.py +0 -0
  61. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/conflicts.py +0 -0
  62. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/cost.py +0 -0
  63. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/cost_estimate.py +0 -0
  64. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/cull.py +0 -0
  65. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/db.py +0 -0
  66. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/demo.py +0 -0
  67. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/detect.py +0 -0
  68. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/escapes.py +0 -0
  69. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/events.py +0 -0
  70. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/git.py +0 -0
  71. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/guardrails.py +0 -0
  72. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/history.py +0 -0
  73. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/identity_lock.py +0 -0
  74. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/inbox.py +0 -0
  75. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/keys_cmds.py +0 -0
  76. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/keystore.py +0 -0
  77. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/launch_health.py +0 -0
  78. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/learned_rules.py +0 -0
  79. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/learning.py +0 -0
  80. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/legacy.py +0 -0
  81. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/model_access.py +0 -0
  82. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/model_availability.py +0 -0
  83. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/native/__init__.py +0 -0
  84. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/native/client.py +0 -0
  85. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/native/loop.py +0 -0
  86. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/native/permissions.py +0 -0
  87. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/native/runner.py +0 -0
  88. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/native/serve.py +0 -0
  89. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/native/tools.py +0 -0
  90. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pane_auth.py +0 -0
  91. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/permissions.py +0 -0
  92. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pipeline.py +0 -0
  93. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/plugins.py +0 -0
  94. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pool.py +0 -0
  95. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pricing.py +0 -0
  96. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/__init__.py +0 -0
  97. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/_files.py +0 -0
  98. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/audit_chain.py +0 -0
  99. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/audit_export.py +0 -0
  100. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/brief_learning.py +0 -0
  101. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/cost_centers.py +0 -0
  102. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/credentials.py +0 -0
  103. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/features.py +0 -0
  104. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/keys.py +0 -0
  105. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/learning.py +0 -0
  106. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/loopback.py +0 -0
  107. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/orgkey.py +0 -0
  108. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/rollout.py +0 -0
  109. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/route_health.py +0 -0
  110. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/settings_sync.py +0 -0
  111. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/pro/team_events.py +0 -0
  112. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/procs.py +0 -0
  113. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/profiles.py +0 -0
  114. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/quota.py +0 -0
  115. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/repo_cmds.py +0 -0
  116. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/repos.py +0 -0
  117. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/rewind.py +0 -0
  118. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/rule_checks.py +0 -0
  119. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/savings.py +0 -0
  120. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/scratch.py +0 -0
  121. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/secrets.py +0 -0
  122. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/self_serve.py +0 -0
  123. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/services.py +0 -0
  124. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/sessions.py +0 -0
  125. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/sidebar_follow.py +0 -0
  126. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/signin_pause.py +0 -0
  127. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/status_cache.py +0 -0
  128. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/tasks.py +0 -0
  129. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/usage.py +0 -0
  130. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/view.py +0 -0
  131. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/watch_costs.py +0 -0
  132. {brindle-0.3.8 → brindle-0.3.11}/src/brindle/workspaces.py +0 -0
@@ -2,6 +2,8 @@
2
2
  __pycache__/
3
3
  *.egg-info/
4
4
  .pytest_cache/
5
+ .coverage
6
+ .coverage.*
5
7
  graphify-out/
6
8
  # brindle local state that is not part of the repo
7
9
  .brindle/*.log
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brindle
3
- Version: 0.3.8
3
+ Version: 0.3.11
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
@@ -25,6 +25,7 @@ Classifier: Topic :: Software Development :: Version Control :: Git
25
25
  Requires-Python: >=3.11
26
26
  Requires-Dist: cryptography>=42
27
27
  Requires-Dist: mcp<3,>=2.2
28
+ Requires-Dist: packaging>=24
28
29
  Requires-Dist: typer>=0.12
29
30
  Description-Content-Type: text/markdown
30
31
 
@@ -78,6 +79,10 @@ curl -fsSL pawdelta.com/brindle/install | sh
78
79
 
79
80
  The script ([scripts/install.sh](scripts/install.sh)) installs uv and tmux if
80
81
  they're missing, then brindle, then runs `brindle doctor`. Run it again to upgrade.
82
+ Session starts check PyPI at most once a day and show available updates in the status
83
+ bar and `brindle doctor`; set `"auto_update": true` in `~/.brindle/config.json` to
84
+ upgrade in the background (restart to use it). Checks skip local and editable
85
+ installs and can be disabled with `BRINDLE_NO_UPDATE_CHECK`.
81
86
  To do it by hand:
82
87
 
83
88
  ```sh
@@ -89,7 +94,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
89
94
  brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
90
95
 
91
96
  `brindle --version` prints the installed version; the tmux status bar of every brindle
92
- session shows it too (`brindle 0.3.8`). A session started before an upgrade keeps
97
+ session shows it too (`brindle 0.3.11`). A session started before an upgrade keeps
93
98
  running the old code, and shows the old number, until you restart it.
94
99
 
95
100
  ## Quick start
@@ -523,6 +528,7 @@ Autopilot, merge gates and cleanup:
523
528
  | Key | Default | |
524
529
  |---|---|---|
525
530
  | `autopilot` | `true` | start the supervisor with autopilot on |
531
+ | `auto_update` | `false` | run `uv tool upgrade brindle` in the background at session start when PyPI has a newer version; restart to use it. Can also be set in `~/.brindle/config.json` for every repo |
526
532
  | `checks` | `[]` | commands that must pass in a worker's branch before it merges |
527
533
  | `review` | only under autopilot | require a reviewer's approval before merging |
528
534
  | `reviewer` | `"reviewer"` | the agent profile that reviews (a Codex profile gives a second model's view) |
@@ -48,6 +48,10 @@ curl -fsSL pawdelta.com/brindle/install | sh
48
48
 
49
49
  The script ([scripts/install.sh](scripts/install.sh)) installs uv and tmux if
50
50
  they're missing, then brindle, then runs `brindle doctor`. Run it again to upgrade.
51
+ Session starts check PyPI at most once a day and show available updates in the status
52
+ bar and `brindle doctor`; set `"auto_update": true` in `~/.brindle/config.json` to
53
+ upgrade in the background (restart to use it). Checks skip local and editable
54
+ installs and can be disabled with `BRINDLE_NO_UPDATE_CHECK`.
51
55
  To do it by hand:
52
56
 
53
57
  ```sh
@@ -59,7 +63,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
59
63
  brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
60
64
 
61
65
  `brindle --version` prints the installed version; the tmux status bar of every brindle
62
- session shows it too (`brindle 0.3.8`). A session started before an upgrade keeps
66
+ session shows it too (`brindle 0.3.11`). A session started before an upgrade keeps
63
67
  running the old code, and shows the old number, until you restart it.
64
68
 
65
69
  ## Quick start
@@ -493,6 +497,7 @@ Autopilot, merge gates and cleanup:
493
497
  | Key | Default | |
494
498
  |---|---|---|
495
499
  | `autopilot` | `true` | start the supervisor with autopilot on |
500
+ | `auto_update` | `false` | run `uv tool upgrade brindle` in the background at session start when PyPI has a newer version; restart to use it. Can also be set in `~/.brindle/config.json` for every repo |
496
501
  | `checks` | `[]` | commands that must pass in a worker's branch before it merges |
497
502
  | `review` | only under autopilot | require a reviewer's approval before merging |
498
503
  | `reviewer` | `"reviewer"` | the agent profile that reviews (a Codex profile gives a second model's view) |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brindle"
3
- version = "0.3.8"
3
+ version = "0.3.11"
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"
@@ -22,6 +22,7 @@ classifiers = [
22
22
  "Topic :: Software Development :: Version Control :: Git",
23
23
  ]
24
24
  dependencies = [
25
+ "packaging>=24",
25
26
  "typer>=0.12",
26
27
  "mcp>=2.2,<3",
27
28
  "cryptography>=42",
@@ -20,7 +20,7 @@ import sys
20
20
  import time
21
21
  import uuid
22
22
 
23
- from brindle import company_identity, company_login, git, keystore, pane_auth, providers, secrets, tmux, workspaces
23
+ from brindle import cloud_credential, company_identity, company_login, git, keystore, pane_auth, providers, secrets, tmux, workspaces
24
24
  from brindle.config import RepoConfig
25
25
  from brindle.db import DB, Agent, Workspace
26
26
  from brindle.model_availability import ModelAvailabilityChecker, PROVISIONING_TIMEOUT, provisioning_context
@@ -193,6 +193,8 @@ def agent_env(ws: Workspace, agent_id: str, agent: Agent | None = None,
193
193
  # org's managed route (an enforced agent_setup, or provider_config) both
194
194
  # beat it; none at all under Claude Code's managed settings (stored_env).
195
195
  company = {} if agent.provider != "claude" else company_login.stored_env()
196
+ if "AZURE_FEDERATED_TOKEN_FILE" in company: # Azure has no hook: a launch freshens the file
197
+ cloud_credential.freshen_azure(company["AZURE_FEDERATED_TOKEN_FILE"])
196
198
  managed_env = {} if org else managed_models.agent_env(m, agent.provider)
197
199
  enforced = None if agent.provider != "claude" or org else company_identity.enforced_route_env()
198
200
  session_creds = {}
@@ -302,6 +302,7 @@ def start(
302
302
  # processes, old paused sessions' worktrees and the pool refill are all
303
303
  # handled by the detached cull.
304
304
  _pause_running(db, ws, stop_procs=False)
305
+ _check_updates(ws.repo_root)
305
306
  if autopilot is None:
306
307
  autopilot = _run(load_repo_config, ws.repo_root).autopilot
307
308
  a = _run(agents.spawn, db, ws, agents.SUPERVISOR, prompt=prompt, provider_name=provider,
@@ -319,6 +320,23 @@ def start(
319
320
  _attach(ws, a.tmux_window)
320
321
 
321
322
 
323
+ def _check_updates(repo_root: str) -> None:
324
+ from brindle import updates
325
+ from brindle.config import load_repo_config
326
+
327
+ cfg = load_repo_config(repo_root)
328
+ latest = updates.newer_version()
329
+ if not latest or cfg.auto_update is not True:
330
+ return
331
+ try:
332
+ subprocess.Popen(["uv", "tool", "upgrade", "brindle"], start_new_session=True,
333
+ stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
334
+ stderr=subprocess.DEVNULL)
335
+ except OSError:
336
+ return
337
+ typer.echo(f" updating brindle to {latest} in the background; the new version takes effect after a restart")
338
+
339
+
322
340
  def _preflight(agent: str, provider: Optional[str], repo_root: str) -> None:
323
341
  """Stop before launching when the chat can't start (no tmux, no CLI)."""
324
342
  from brindle import doctor as doctor_mod
@@ -509,6 +527,7 @@ def continue_cmd(
509
527
  ws = scratch.for_origin(db, cwd)
510
528
  if ws is None:
511
529
  _fail("no scratch session started from this folder to continue; run `brindle` to start one")
530
+ _check_updates(ws.repo_root)
512
531
  live = agents.find_running(db, ws, "supervisor") or next(
513
532
  (a for a in reversed(db.list_agents(ws.id)) if a.mode == "interactive" and (agents.is_alive(a) or a.status == "failed")),
514
533
  None,
@@ -2716,6 +2735,16 @@ def local_models_cmd(repo: Optional[str] = typer.Option(None, "--repo")) -> None
2716
2735
  pass
2717
2736
 
2718
2737
 
2738
+ @app.command("cloud-credential", hidden=True)
2739
+ def cloud_credential_cmd(cloud: str = typer.Argument(..., help="aws, gcp or azure"),
2740
+ org: Optional[str] = typer.Option(None, "--org", help="The org id")) -> None:
2741
+ """The hook the clouds run for a federated org (`credential_process`, the GCP
2742
+ executable source): prints the credential the cloud's contract asks for."""
2743
+ from brindle import cloud_credential
2744
+
2745
+ raise typer.Exit(cloud_credential.run(cloud, org))
2746
+
2747
+
2719
2748
  @app.command("_sync-settings", hidden=True)
2720
2749
  def sync_settings_cmd() -> None:
2721
2750
  """Pull synced settings (brindle Pro); detached from `brindle`."""