brindle 0.4.3__tar.gz → 0.4.4__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 (163) hide show
  1. {brindle-0.4.3 → brindle-0.4.4}/PKG-INFO +1 -1
  2. {brindle-0.4.3 → brindle-0.4.4}/pyproject.toml +1 -1
  3. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/agents.py +31 -0
  4. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/cli.py +1 -4
  5. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/codex_hook.py +11 -1
  6. {brindle-0.4.3 → brindle-0.4.4}/.gitignore +0 -0
  7. {brindle-0.4.3 → brindle-0.4.4}/LICENSE +0 -0
  8. {brindle-0.4.3 → brindle-0.4.4}/README.md +0 -0
  9. {brindle-0.4.3 → brindle-0.4.4}/SCHEDULE-A +0 -0
  10. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/__init__.py +0 -0
  11. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/__main__.py +0 -0
  12. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/account.py +0 -0
  13. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/airgap.py +0 -0
  14. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/antigravity.py +0 -0
  15. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/assembler.py +0 -0
  16. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/assets/braid_dark.png +0 -0
  17. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/assets/braid_light.png +0 -0
  18. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/autopilot.py +0 -0
  19. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/budget.py +0 -0
  20. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/__init__.py +0 -0
  21. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/backend-auditor.md +0 -0
  22. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/coordinator.md +0 -0
  23. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-codex.md +0 -0
  24. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-fable.md +0 -0
  25. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-gemini.md +0 -0
  26. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-heavy.md +0 -0
  27. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-light.md +0 -0
  28. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-local.md +0 -0
  29. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-opencode.md +0 -0
  30. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer-sonnet.md +0 -0
  31. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/developer.md +0 -0
  32. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/qa.md +0 -0
  33. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
  34. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/reviewer-gemini.md +0 -0
  35. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/reviewer-local.md +0 -0
  36. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/reviewer-opencode.md +0 -0
  37. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/reviewer.md +0 -0
  38. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/subagent.md +0 -0
  39. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_agents/supervisor.md +0 -0
  40. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_rules/__init__.py +0 -0
  41. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_rules/security/backend.md +0 -0
  42. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
  43. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/builtin_rules/tests/only.md +0 -0
  44. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/ci_adapters.py +0 -0
  45. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/ci_client.py +0 -0
  46. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/ci_federation.py +0 -0
  47. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/ci_hosts.py +0 -0
  48. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/cloud_credential.py +0 -0
  49. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/cloud_login.py +0 -0
  50. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/codemap.py +0 -0
  51. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/codemap_docs.py +0 -0
  52. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/company_identity.py +0 -0
  53. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/company_login.py +0 -0
  54. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/config.py +0 -0
  55. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/conflicts.py +0 -0
  56. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/coordinator.py +0 -0
  57. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/cost.py +0 -0
  58. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/cost_estimate.py +0 -0
  59. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/cull.py +0 -0
  60. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/db.py +0 -0
  61. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/demo.py +0 -0
  62. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/detect.py +0 -0
  63. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/doctor.py +0 -0
  64. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/escapes.py +0 -0
  65. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/events.py +0 -0
  66. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/gates.py +0 -0
  67. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/git.py +0 -0
  68. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/guardrails.py +0 -0
  69. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/history.py +0 -0
  70. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/identity_lock.py +0 -0
  71. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/inbox.py +0 -0
  72. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/kb.py +0 -0
  73. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/key_check.py +0 -0
  74. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/keys_cmds.py +0 -0
  75. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/keystore.py +0 -0
  76. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/knowledge.py +0 -0
  77. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/launch_health.py +0 -0
  78. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/learned_rules.py +0 -0
  79. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/learning.py +0 -0
  80. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/legacy.py +0 -0
  81. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/local_setup.py +0 -0
  82. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/login_pause.py +0 -0
  83. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/mcp_server.py +0 -0
  84. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/model_access.py +0 -0
  85. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/model_availability.py +0 -0
  86. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/__init__.py +0 -0
  87. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/catalog.py +0 -0
  88. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/client.py +0 -0
  89. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/loop.py +0 -0
  90. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/lsp.py +0 -0
  91. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/permissions.py +0 -0
  92. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/runner.py +0 -0
  93. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/serve.py +0 -0
  94. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/native/tools.py +0 -0
  95. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/opencode.py +0 -0
  96. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pane_auth.py +0 -0
  97. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/permissions.py +0 -0
  98. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/picker.py +0 -0
  99. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pipeline.py +0 -0
  100. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/plugins.py +0 -0
  101. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/policy.py +0 -0
  102. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pool.py +0 -0
  103. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pricing.py +0 -0
  104. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/__init__.py +0 -0
  105. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/_files.py +0 -0
  106. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/account.py +0 -0
  107. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/audit_chain.py +0 -0
  108. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/audit_export.py +0 -0
  109. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/auth.py +0 -0
  110. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/brief_learning.py +0 -0
  111. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/cost_centers.py +0 -0
  112. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/credentials.py +0 -0
  113. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/features.py +0 -0
  114. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/keys.py +0 -0
  115. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/learning.py +0 -0
  116. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/license.py +0 -0
  117. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/loopback.py +0 -0
  118. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/managed_models.py +0 -0
  119. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/org_profiles.py +0 -0
  120. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/orgkey.py +0 -0
  121. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/rollout.py +0 -0
  122. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/route_health.py +0 -0
  123. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/settings_sync.py +0 -0
  124. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/status.py +0 -0
  125. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/team_events.py +0 -0
  126. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/pro/team_policy.py +0 -0
  127. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/procs.py +0 -0
  128. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/profiles.py +0 -0
  129. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/prompt_answer.py +0 -0
  130. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/providers.py +0 -0
  131. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/quota.py +0 -0
  132. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/repo_cmds.py +0 -0
  133. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/repos.py +0 -0
  134. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/rewind.py +0 -0
  135. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/rule_checks.py +0 -0
  136. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/savings.py +0 -0
  137. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/scratch.py +0 -0
  138. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/secrets.py +0 -0
  139. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/self_serve.py +0 -0
  140. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/services.py +0 -0
  141. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/sessions.py +0 -0
  142. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/sidebar_follow.py +0 -0
  143. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/sidebar_logo.py +0 -0
  144. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/signin_pause.py +0 -0
  145. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/status_cache.py +0 -0
  146. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/tasks.py +0 -0
  147. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/tmux.py +0 -0
  148. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/updates.py +0 -0
  149. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/upgrade.py +0 -0
  150. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/usage.py +0 -0
  151. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/view.py +0 -0
  152. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/wake.py +0 -0
  153. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/watch.py +0 -0
  154. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/watch_costs.py +0 -0
  155. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/web/__init__.py +0 -0
  156. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/web/chat.py +0 -0
  157. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/web/dashboard.py +0 -0
  158. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/web/diff.py +0 -0
  159. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/web/server.py +0 -0
  160. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/web/static/app.js +0 -0
  161. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/web/static/index.html +0 -0
  162. {brindle-0.4.3 → brindle-0.4.4}/src/brindle/worker_models.py +0 -0
  163. {brindle-0.4.3 → brindle-0.4.4}/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
3
+ Version: 0.4.4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brindle"
3
- version = "0.4.3"
3
+ version = "0.4.4"
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"
@@ -2159,9 +2159,40 @@ def finish_review(db: DB, reviewer_id: str, ws_id: str) -> None:
2159
2159
 
2160
2160
  learned_rules.refresh_later(ws.repo_root) # the finding just landed in the history
2161
2161
  finally:
2162
+ # A merge removes the workspace but leaves the session this reviewer
2163
+ # runs in; once the reviewer is gone nobody else would end it.
2164
+ if ws and db.get_workspace(ws_id) is None:
2165
+ _closing_session(reviewer_id).write_text(ws.tmux_session)
2162
2166
  close_later(reviewer_id)
2163
2167
 
2164
2168
 
2169
+ def _closing_session(agent_id: str):
2170
+ from brindle.config import brindle_home
2171
+
2172
+ d = brindle_home() / "closing"
2173
+ d.mkdir(parents=True, exist_ok=True)
2174
+ return d / agent_id
2175
+
2176
+
2177
+ def close_removed(db: DB, agent_id: str) -> None:
2178
+ """Kill an agent and, when its workspace was removed with the session left
2179
+ running (noted by ``finish_review``), that session too: only brindle's own,
2180
+ unattached session of a workspace that no longer exists."""
2181
+ note = _closing_session(agent_id)
2182
+ try:
2183
+ session = note.read_text().strip()
2184
+ note.unlink()
2185
+ except OSError:
2186
+ session = ""
2187
+ try:
2188
+ kill(db, agent_id)
2189
+ except AgentError:
2190
+ pass
2191
+ if session and db.workspace_by_tmux_session(session) is None \
2192
+ and not tmux.session_attached(session):
2193
+ tmux.kill_session(session)
2194
+
2195
+
2165
2196
  def close_later(agent_id: str, delay: float = 5.0) -> None:
2166
2197
  """Stop an agent shortly, from a detached process: used by an agent's own
2167
2198
  tool call, which must return before its CLI goes away."""
@@ -3080,10 +3080,7 @@ def quit_cmd(root_id: str) -> None:
3080
3080
  @app.command("_close", hidden=True)
3081
3081
  def close_cmd(agent_id: str, delay: float = typer.Option(0.0)) -> None:
3082
3082
  time.sleep(delay)
3083
- try:
3084
- agents.kill(_helper_db(), agent_id)
3085
- except agents.AgentError:
3086
- pass
3083
+ agents.close_removed(_helper_db(), agent_id)
3087
3084
 
3088
3085
 
3089
3086
  @app.command("_finish-review", hidden=True)
@@ -36,6 +36,7 @@ import json
36
36
  import os
37
37
  import queue
38
38
  import shlex
39
+ import signal
39
40
  import subprocess
40
41
  import threading
41
42
  import time
@@ -228,7 +229,8 @@ class _AppServer:
228
229
  self.timeout = timeout
229
230
  try:
230
231
  self.proc = subprocess.Popen([binary, "app-server", *flags], stdin=subprocess.PIPE,
231
- stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)
232
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True,
233
+ start_new_session=True)
232
234
  except OSError as e:
233
235
  raise CodexHookError(f"couldn't run {binary}: {e}") from e
234
236
  self.lines: queue.Queue = queue.Queue()
@@ -279,6 +281,14 @@ class _AppServer:
279
281
  return result
280
282
 
281
283
  def close(self) -> None:
284
+ # The app-server leads its own process group (see __init__): kill the
285
+ # group so nothing it spawned outlives it.
286
+ pid = getattr(self.proc, "pid", None)
287
+ if pid:
288
+ try:
289
+ os.killpg(pid, signal.SIGKILL)
290
+ except OSError:
291
+ pass
282
292
  try:
283
293
  self.proc.kill()
284
294
  except OSError:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes