brindle 0.3.16__tar.gz → 0.3.17__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.3.16 → brindle-0.3.17}/PKG-INFO +2 -2
- {brindle-0.3.16 → brindle-0.3.17}/README.md +1 -1
- {brindle-0.3.16 → brindle-0.3.17}/pyproject.toml +1 -1
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/agents.py +75 -13
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/autopilot.py +324 -85
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/cull.py +37 -5
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/db.py +17 -2
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/history.py +96 -4
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/mcp_server.py +17 -8
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/providers.py +31 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/sessions.py +13 -3
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/sidebar_follow.py +3 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/usage.py +21 -2
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/view.py +32 -12
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/watch.py +24 -10
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/watch_costs.py +33 -9
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/workspaces.py +15 -4
- {brindle-0.3.16 → brindle-0.3.17}/.gitignore +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/LICENSE +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/SCHEDULE-A +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/__init__.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/__main__.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/account.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/airgap.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/antigravity.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/budget.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/__init__.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/backend-auditor.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/developer-codex.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/developer-fable.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/developer-gemini.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/developer-heavy.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/developer-light.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/developer-local.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/developer.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/qa.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/reviewer-gemini.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/reviewer-local.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/reviewer.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/subagent.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_agents/supervisor.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_rules/__init__.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_rules/security/backend.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/builtin_rules/tests/only.md +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/ci_adapters.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/ci_client.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/ci_federation.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/ci_hosts.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/cli.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/cloud_credential.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/cloud_login.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/codemap.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/codemap_docs.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/codex_hook.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/company_identity.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/company_login.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/config.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/conflicts.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/cost.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/cost_estimate.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/demo.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/detect.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/doctor.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/escapes.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/events.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/gates.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/git.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/guardrails.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/identity_lock.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/inbox.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/keys_cmds.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/keystore.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/launch_health.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/learned_rules.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/learning.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/legacy.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/login_pause.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/model_access.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/model_availability.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/native/__init__.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/native/client.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/native/loop.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/native/permissions.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/native/runner.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/native/serve.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/native/tools.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pane_auth.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/permissions.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pipeline.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/plugins.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/policy.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pool.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pricing.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/__init__.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/_files.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/account.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/audit_chain.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/audit_export.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/auth.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/brief_learning.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/cost_centers.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/credentials.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/features.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/keys.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/learning.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/license.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/loopback.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/managed_models.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/org_profiles.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/orgkey.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/rollout.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/route_health.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/settings_sync.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/status.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/team_events.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/pro/team_policy.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/procs.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/profiles.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/prompt_answer.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/quota.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/repo_cmds.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/repos.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/rewind.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/rule_checks.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/savings.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/scratch.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/secrets.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/self_serve.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/services.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/signin_pause.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/status_cache.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/tasks.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/tmux.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/updates.py +0 -0
- {brindle-0.3.16 → brindle-0.3.17}/src/brindle/upgrade.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brindle
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
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
|
|
@@ -94,7 +94,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
|
|
|
94
94
|
brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
|
|
95
95
|
|
|
96
96
|
`brindle --version` prints the installed version; the tmux status bar of every brindle
|
|
97
|
-
session shows it too (`brindle 0.3.
|
|
97
|
+
session shows it too (`brindle 0.3.17`). A session started before an upgrade keeps
|
|
98
98
|
running the old code, and shows the old number, until you restart it.
|
|
99
99
|
|
|
100
100
|
## Quick start
|
|
@@ -63,7 +63,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
|
|
|
63
63
|
brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
|
|
64
64
|
|
|
65
65
|
`brindle --version` prints the installed version; the tmux status bar of every brindle
|
|
66
|
-
session shows it too (`brindle 0.3.
|
|
66
|
+
session shows it too (`brindle 0.3.17`). A session started before an upgrade keeps
|
|
67
67
|
running the old code, and shows the old number, until you restart it.
|
|
68
68
|
|
|
69
69
|
## Quick start
|
|
@@ -322,10 +322,12 @@ def spawn(
|
|
|
322
322
|
autopilot: bool = False,
|
|
323
323
|
plan_first: bool = False,
|
|
324
324
|
review_sha: str | None = None,
|
|
325
|
+
claim_goals: bool = True,
|
|
325
326
|
) -> Agent:
|
|
326
327
|
"""Start an agent in ``ws``. Workers (handoff/assign) given a ``done_when``
|
|
327
328
|
finish line run it as a Claude Code ``/goal``. With ``autopilot``, the
|
|
328
|
-
agent is a session root that drives toward a goal (see brindle.autopilot)
|
|
329
|
+
agent is a session root that drives toward a goal (see brindle.autopilot);
|
|
330
|
+
with ``claim_goals`` it claims a hand-written ``.brindle/goals.md``."""
|
|
329
331
|
from brindle import autopilot as pilot
|
|
330
332
|
from brindle import identity_lock
|
|
331
333
|
|
|
@@ -377,9 +379,9 @@ def spawn(
|
|
|
377
379
|
db.update_agent(agent_id, plan_first=1)
|
|
378
380
|
agent.plan_first = 1
|
|
379
381
|
if autopilot:
|
|
380
|
-
plan = pilot.enable(db, agent_id, ws)
|
|
382
|
+
plan = pilot.enable(db, agent_id, ws, claim=claim_goals)
|
|
381
383
|
if plan and not prompt:
|
|
382
|
-
prompt = agent.task = pilot.kickoff(plan)
|
|
384
|
+
prompt = agent.task = pilot.kickoff(plan, agent_id)
|
|
383
385
|
db.update_agent(agent_id, task=prompt)
|
|
384
386
|
if mode == "interactive":
|
|
385
387
|
from brindle import codemap
|
|
@@ -1177,6 +1179,10 @@ def resume(db: DB, root_id: str, *, watch_pane: bool = True,
|
|
|
1177
1179
|
if a.status in ("paused", "failed") and (only is None or a.id in only)]
|
|
1178
1180
|
for a in candidates: # all or nothing: a session never half-resumes under another identity
|
|
1179
1181
|
identity_lock.ensure(a)
|
|
1182
|
+
if any(a.id == root_id for a in candidates):
|
|
1183
|
+
from brindle import autopilot as pilot
|
|
1184
|
+
|
|
1185
|
+
pilot.reload_goals_file(db, root_id) # edits the person made to this session's goal file
|
|
1180
1186
|
resumed = []
|
|
1181
1187
|
for a in candidates:
|
|
1182
1188
|
if a.status == "failed":
|
|
@@ -1506,14 +1512,22 @@ def screen_status(db: DB, agent: Agent, samples: int = 2, gap: float = 0.7) -> s
|
|
|
1506
1512
|
single cheap read, as the dashboard does, leaves it alone. Moving an
|
|
1507
1513
|
agent to idle also delivers anything queued for it, but only when
|
|
1508
1514
|
``samples`` >= 2: a single-sample read must never pop a message into a
|
|
1509
|
-
terminal as a side effect of just rendering the dashboard.
|
|
1515
|
+
terminal as a side effect of just rendering the dashboard.
|
|
1516
|
+
|
|
1517
|
+
An agent whose CLI runs none of brindle's hooks (``hooks_off``: the
|
|
1518
|
+
person's settings turn them off, at launch or later through /hooks, or
|
|
1519
|
+
none reported within HOOKS_GRACE of its input box appearing) has only the
|
|
1520
|
+
screen to go by: it may leave 'starting', a single read may move it from
|
|
1521
|
+
'idle' to busy, and a ``samples`` >= 2 idle read delivers what's queued
|
|
1522
|
+
even when the status was already idle, since no Stop hook ever will."""
|
|
1510
1523
|
provider = get_provider(agent.provider)
|
|
1511
1524
|
if (agent.headless or not provider.uses_hooks
|
|
1512
1525
|
or agent.status not in ("starting", "idle", "processing", "waiting")):
|
|
1513
1526
|
return None # a headless pane shows output, not a TUI to read
|
|
1514
|
-
|
|
1527
|
+
_sync_hooks_off(db, agent, provider.hooks_disabled(*_settings_scope(db, agent)))
|
|
1528
|
+
if agent.status == "idle" and samples < 2 and not agent.hooks_off:
|
|
1515
1529
|
return None
|
|
1516
|
-
seen = set()
|
|
1530
|
+
seen, ready = set(), True
|
|
1517
1531
|
for i in range(samples):
|
|
1518
1532
|
if i:
|
|
1519
1533
|
time.sleep(gap)
|
|
@@ -1525,25 +1539,71 @@ def screen_status(db: DB, agent: Agent, samples: int = 2, gap: float = 0.7) -> s
|
|
|
1525
1539
|
if agent.status == "idle" and state == "busy" and not provider.busy_in_footer(screen):
|
|
1526
1540
|
state = None
|
|
1527
1541
|
seen.add(state)
|
|
1542
|
+
ready = ready and provider.input_ready(screen)
|
|
1543
|
+
if (agent.status == "starting" and not agent.hooks_off and ready and provider.announces_start
|
|
1544
|
+
and time.time() - (agent.status_since or agent.created_at) >= HOOKS_GRACE):
|
|
1545
|
+
# Its input box is up, and SessionStart, which runs before it, never reported.
|
|
1546
|
+
# (A CLI without one is marked ready by after_launch instead: see ready.)
|
|
1547
|
+
_sync_hooks_off(db, agent, f"{NO_HOOK_EVENTS} from {provider.name}")
|
|
1528
1548
|
if len(seen) != 1:
|
|
1529
1549
|
return None
|
|
1530
1550
|
state = seen.pop()
|
|
1531
1551
|
new = {"idle": "idle", "busy": "processing", "waiting": "waiting"}.get(state or "")
|
|
1532
|
-
if agent.status == "starting" and new != "waiting":
|
|
1552
|
+
if agent.status == "starting" and new != "waiting" and not agent.hooks_off:
|
|
1533
1553
|
# Its hooks say when it's ready; the screen only adds that it's
|
|
1534
1554
|
# stuck on a dialog before they could (e.g. folder trust).
|
|
1535
1555
|
return None
|
|
1536
|
-
|
|
1556
|
+
changed = bool(new) and new != agent.status
|
|
1557
|
+
if changed:
|
|
1537
1558
|
db.set_status(agent.id, new, only_if=agent.status)
|
|
1538
1559
|
agent.status = new
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1560
|
+
if (new == "idle" and samples >= 2 and (changed or agent.hooks_off)
|
|
1561
|
+
and db.pending_count(agent.id)):
|
|
1562
|
+
try:
|
|
1563
|
+
flush(db, agent.id)
|
|
1564
|
+
except tmux.TmuxError:
|
|
1565
|
+
pass # stays queued; its Stop hook hands it over
|
|
1544
1566
|
return new
|
|
1545
1567
|
|
|
1546
1568
|
|
|
1569
|
+
# How long a hooks CLI may show its input box while still 'starting' before
|
|
1570
|
+
# brindle takes it that none of its hooks run (see screen_status).
|
|
1571
|
+
HOOKS_GRACE = 60.0
|
|
1572
|
+
# How hooks_off reads when no settings explain it, only the missing events.
|
|
1573
|
+
NO_HOOK_EVENTS = "no hook events"
|
|
1574
|
+
|
|
1575
|
+
|
|
1576
|
+
_settings_scopes: dict[tuple[str, float], tuple[str | None, list[str] | None]] = {}
|
|
1577
|
+
|
|
1578
|
+
|
|
1579
|
+
def _settings_scope(db: DB, agent: Agent) -> tuple[str | None, list[str] | None]:
|
|
1580
|
+
"""Where ``agent``'s CLI reads its settings: its workspace, and the
|
|
1581
|
+
profile's setting_sources (None: all of them). Kept per agent, as the
|
|
1582
|
+
sidebar asks on every refresh and a running CLI keeps its launch flags."""
|
|
1583
|
+
key = (agent.id, agent.created_at)
|
|
1584
|
+
if key in _settings_scopes:
|
|
1585
|
+
return _settings_scopes[key]
|
|
1586
|
+
from brindle.profiles import ProfileError
|
|
1587
|
+
|
|
1588
|
+
ws = db.get_workspace(agent.workspace_id)
|
|
1589
|
+
try:
|
|
1590
|
+
sources = load_profile(agent.profile, ws.repo_root if ws else None).setting_sources
|
|
1591
|
+
except (KeyError, ProfileError):
|
|
1592
|
+
sources = None
|
|
1593
|
+
scope = _settings_scopes[key] = (ws.path if ws else None), sources
|
|
1594
|
+
return scope
|
|
1595
|
+
|
|
1596
|
+
|
|
1597
|
+
def _sync_hooks_off(db: DB, agent: Agent, why: str | None) -> None:
|
|
1598
|
+
"""Record ``why`` its hooks are off; None clears a reason the settings
|
|
1599
|
+
gave (turned back on), but not missing events, which only a hook clears."""
|
|
1600
|
+
if why is None and agent.hooks_off and agent.hooks_off.startswith(NO_HOOK_EVENTS):
|
|
1601
|
+
return
|
|
1602
|
+
if why != agent.hooks_off:
|
|
1603
|
+
db.update_agent(agent.id, hooks_off=why)
|
|
1604
|
+
agent.hooks_off = why
|
|
1605
|
+
|
|
1606
|
+
|
|
1547
1607
|
def _deliver_to_inbox(db: DB, agent: Agent, message_id: int, text: str,
|
|
1548
1608
|
sender_id: str | None) -> bool:
|
|
1549
1609
|
"""Send a queued message through Claude Code's own inbox (see
|
|
@@ -2661,6 +2721,8 @@ def handle_hook(db: DB, agent_id: str, event: str, payload: dict) -> dict | None
|
|
|
2661
2721
|
agent = db.get_agent(agent_id)
|
|
2662
2722
|
if agent is None:
|
|
2663
2723
|
return None
|
|
2724
|
+
if agent.hooks_off:
|
|
2725
|
+
db.update_agent(agent_id, hooks_off=None) # its hooks run after all: turned back on
|
|
2664
2726
|
# A hook runs only once the session is going, so its launch is past any
|
|
2665
2727
|
# startup prompt: a menu on its pane from now on is the agent's own (a
|
|
2666
2728
|
# question for the user), which no sweep may answer.
|