brigade-cli 0.10.2__tar.gz → 0.11.0__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.
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/PKG-INFO +21 -4
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/QUICKSTART.md +44 -11
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/README.md +18 -2
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/pyproject.toml +14 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/__init__.py +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/__main__.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/aboyeur.py +118 -51
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/actionqueue.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/add.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/agents.py +46 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/budgets.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/budgets_cmd.py +2 -4
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/center_cmd.py +921 -131
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/chat_cmd.py +82 -17
- brigade_cli-0.11.0/src/brigade/cli/__init__.py +147 -0
- brigade_cli-0.11.0/src/brigade/cli/_common.py +56 -0
- brigade_cli-0.11.0/src/brigade/cli/add.py +20 -0
- brigade_cli-0.11.0/src/brigade/cli/budgets.py +30 -0
- brigade_cli-0.11.0/src/brigade/cli/center.py +326 -0
- brigade_cli-0.11.0/src/brigade/cli/chat.py +91 -0
- brigade_cli-0.11.0/src/brigade/cli/context.py +87 -0
- brigade_cli-0.11.0/src/brigade/cli/daily.py +225 -0
- brigade_cli-0.11.0/src/brigade/cli/doctor.py +24 -0
- brigade_cli-0.11.0/src/brigade/cli/dogfood.py +90 -0
- brigade_cli-0.11.0/src/brigade/cli/friction.py +84 -0
- brigade_cli-0.11.0/src/brigade/cli/handoff.py +390 -0
- brigade_cli-0.11.0/src/brigade/cli/handoff_template.py +25 -0
- brigade_cli-0.11.0/src/brigade/cli/hermes_fragments.py +19 -0
- brigade_cli-0.11.0/src/brigade/cli/ingest.py +35 -0
- brigade_cli-0.11.0/src/brigade/cli/init.py +102 -0
- brigade_cli-0.11.0/src/brigade/cli/learn.py +189 -0
- brigade_cli-0.11.0/src/brigade/cli/memory.py +109 -0
- brigade_cli-0.11.0/src/brigade/cli/notifications.py +106 -0
- brigade_cli-0.11.0/src/brigade/cli/openclaw_fragments.py +19 -0
- brigade_cli-0.11.0/src/brigade/cli/operator.py +404 -0
- brigade_cli-0.11.0/src/brigade/cli/pantry.py +90 -0
- brigade_cli-0.11.0/src/brigade/cli/projects.py +122 -0
- brigade_cli-0.11.0/src/brigade/cli/reconfigure.py +46 -0
- brigade_cli-0.11.0/src/brigade/cli/release.py +289 -0
- brigade_cli-0.11.0/src/brigade/cli/repos.py +862 -0
- brigade_cli-0.11.0/src/brigade/cli/research.py +183 -0
- brigade_cli-0.11.0/src/brigade/cli/roadmap.py +58 -0
- brigade_cli-0.11.0/src/brigade/cli/roster.py +47 -0
- brigade_cli-0.11.0/src/brigade/cli/run.py +195 -0
- brigade_cli-0.11.0/src/brigade/cli/runbook.py +75 -0
- brigade_cli-0.11.0/src/brigade/cli/runs.py +56 -0
- brigade_cli-0.11.0/src/brigade/cli/scrub.py +25 -0
- brigade_cli-0.11.0/src/brigade/cli/security.py +203 -0
- brigade_cli-0.11.0/src/brigade/cli/skills.py +268 -0
- brigade_cli-0.11.0/src/brigade/cli/status.py +19 -0
- brigade_cli-0.11.0/src/brigade/cli/tools.py +494 -0
- brigade_cli-0.11.0/src/brigade/cli/untrusted.py +47 -0
- brigade_cli-0.11.0/src/brigade/cli/work.py +2044 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/config.py +2 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/context_cmd.py +78 -13
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/daily_cmd.py +860 -171
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/doctor.py +50 -49
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/dogfood_cmd.py +14 -37
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/fragments.py +4 -10
- brigade_cli-0.11.0/src/brigade/friction_cmd.py +588 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/handoff.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/handoff_cmd.py +145 -78
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/hermes_adapter.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/ingest.py +2 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/install.py +93 -80
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/learn_cmd.py +102 -22
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/localio.py +38 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/managed.py +71 -31
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/memory_cmd.py +106 -31
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/notifications_cmd.py +18 -6
- brigade_cli-0.11.0/src/brigade/operator_cmd/__init__.py +91 -0
- brigade_cli-0.11.0/src/brigade/operator_cmd/adoption.py +407 -0
- brigade_cli-0.11.0/src/brigade/operator_cmd/guide.py +190 -0
- brigade_cli-0.11.0/src/brigade/operator_cmd/health.py +528 -0
- brigade_cli-0.11.0/src/brigade/operator_cmd/lifecycle.py +628 -0
- brigade_cli-0.11.0/src/brigade/operator_cmd/migration.py +589 -0
- brigade_cli-0.11.0/src/brigade/operator_cmd/surfaces.py +1045 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/pantry_cmd.py +4 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/phases_cmd.py +1220 -205
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/proc.py +2 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/projects_cmd.py +49 -9
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/prompt.py +8 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/reconfigure.py +3 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/registry.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/release_cmd.py +669 -158
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/reportstore.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/repos_cmd.py +1439 -280
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/config.py +10 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/engine.py +64 -32
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/extract.py +26 -12
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/handoff.py +3 -2
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/llm.py +19 -10
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/registry.py +50 -12
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/report.py +26 -8
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/sources/cli.py +13 -2
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/sources/local.py +23 -7
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/sources/web.py +18 -5
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/types.py +13 -5
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research_cmd.py +141 -60
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/roadmap_cmd.py +22 -26
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/roster.py +15 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/roster_cmd.py +19 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/runbook_cmd.py +48 -11
- brigade_cli-0.11.0/src/brigade/runguard.py +192 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/runs_cmd.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/scrub.py +22 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/security_cmd.py +226 -53
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/selection.py +12 -15
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/skills_cmd.py +171 -44
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/station.py +3 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/status.py +2 -4
- brigade_cli-0.11.0/src/brigade/templates/harnesses/amp.json +11 -0
- brigade_cli-0.11.0/src/brigade/templates/harnesses/crush.json +11 -0
- brigade_cli-0.11.0/src/brigade/templates/harnesses/grok.json +11 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/hermes/memory-handoff.harness.json +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/hermes/model-lanes.harness.json +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/hermes/workspace.harness.json +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/chat-memory-sweep.example.json +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/memory-care.example.json +1 -1
- brigade_cli-0.11.0/src/brigade/templates/openhands/memory-handoffs/TEMPLATE.md +64 -0
- brigade_cli-0.11.0/src/brigade/templates/pi/memory-handoffs/TEMPLATE.md +64 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/policies/personal.json +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/policies/public-content.json +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/policies/public-repo.json +1 -1
- brigade_cli-0.11.0/src/brigade/templates/qwen/memory-handoffs/TEMPLATE.md +64 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/toml_compat.py +10 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/tools_cmd.py +516 -133
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/untrusted.py +3 -4
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/untrusted_cmd.py +2 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/work_cmd/__init__.py +108 -80
- brigade_cli-0.11.0/src/brigade/work_cmd/backup.py +174 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/work_cmd/config.py +121 -32
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/work_cmd/helpers.py +12 -13
- brigade_cli-0.11.0/src/brigade/work_cmd/imports.py +934 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/work_cmd/ledger.py +28 -20
- brigade_cli-0.11.0/src/brigade/work_cmd/reviews.py +1328 -0
- brigade_cli-0.11.0/src/brigade/work_cmd/scanners.py +1178 -0
- brigade_cli-0.11.0/src/brigade/work_cmd/services.py +1105 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/work_cmd/session.py +269 -104
- brigade_cli-0.11.0/src/brigade/work_cmd/sweeps.py +771 -0
- brigade_cli-0.11.0/src/brigade/work_cmd/verification.py +724 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/PKG-INFO +21 -4
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/SOURCES.txt +74 -4
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/requires.txt +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_aboyeur.py +177 -0
- brigade_cli-0.11.0/tests/test_actionqueue.py +143 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_add.py +0 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_agents.py +100 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_budgets.py +3 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_config.py +1 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_doctor.py +10 -9
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_dogfood_cmd.py +4 -5
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_fragments.py +3 -0
- brigade_cli-0.11.0/tests/test_friction_cmd.py +117 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_gitignore.py +7 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_handoff.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_handoff_cmd.py +197 -144
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_ingest.py +87 -10
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_init.py +29 -13
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_install.py +8 -2
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_managed.py +14 -13
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_memory_cmd.py +92 -17
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_notifications_cmd.py +27 -27
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_operator_cmd.py +287 -117
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_pantry_cmd.py +9 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase100_cmd.py +6 -2
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase101_cmd.py +183 -33
- brigade_cli-0.11.0/tests/test_phase165_cmd.py +3030 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase36_cmd.py +88 -28
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase37_cmd.py +54 -8
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase38_cmd.py +105 -20
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase39_cmd.py +60 -9
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase40_cmd.py +53 -10
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase41_cmd.py +30 -5
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase42_cmd.py +70 -13
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase43_cmd.py +92 -15
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase44_cmd.py +148 -37
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase45_cmd.py +49 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase46_50_cmd.py +26 -5
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase51_55_cmd.py +82 -10
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase56_60_cmd.py +146 -50
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase97_cmd.py +44 -30
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_prompt.py +4 -4
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_registry.py +1 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_release_cmd.py +92 -25
- brigade_cli-0.11.0/tests/test_reportstore.py +150 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_repos_cmd.py +4 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_cmd.py +38 -12
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_config.py +5 -2
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_engine.py +29 -12
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_extract.py +21 -7
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_handoff.py +12 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_llm.py +36 -6
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_local_sources.py +3 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_registry.py +3 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_report.py +11 -7
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_types.py +3 -1
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_web.py +19 -5
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_roadmap_cmd.py +1 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_roster.py +43 -3
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_roster_cmd.py +7 -0
- brigade_cli-0.11.0/tests/test_run_cli.py +688 -0
- brigade_cli-0.11.0/tests/test_runguard.py +141 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_scrub.py +9 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_security_cmd.py +62 -17
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_selection.py +28 -7
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_skills_cmd.py +93 -18
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_untrusted.py +3 -0
- brigade_cli-0.11.0/tests/test_work_cmd_backup.py +448 -0
- brigade_cli-0.11.0/tests/test_work_cmd_imports.py +3170 -0
- brigade_cli-0.11.0/tests/test_work_cmd_ledger.py +1055 -0
- brigade_cli-0.11.0/tests/test_work_cmd_reviews.py +727 -0
- brigade_cli-0.11.0/tests/test_work_cmd_scanners.py +719 -0
- brigade_cli-0.11.0/tests/test_work_cmd_services.py +2699 -0
- brigade_cli-0.11.0/tests/test_work_cmd_session.py +1699 -0
- brigade_cli-0.11.0/tests/test_work_cmd_sweeps.py +699 -0
- brigade_cli-0.11.0/tests/test_work_cmd_verification.py +259 -0
- brigade_cli-0.10.2/src/brigade/cli.py +0 -5063
- brigade_cli-0.10.2/src/brigade/operator_cmd.py +0 -3040
- brigade_cli-0.10.2/src/brigade/work_cmd/services.py +0 -5983
- brigade_cli-0.10.2/tests/test_phase165_cmd.py +0 -1294
- brigade_cli-0.10.2/tests/test_run_cli.py +0 -242
- brigade_cli-0.10.2/tests/test_work_cmd.py +0 -11417
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/LICENSE +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/MANIFEST.in +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/setup.cfg +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/py.typed +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/__init__.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/research/sources/__init__.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/adal/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/aider/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/antigravity → brigade_cli-0.11.0/src/brigade/templates/amp}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/claude → brigade_cli-0.11.0/src/brigade/templates/antigravity}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/codex → brigade_cli-0.11.0/src/brigade/templates/claude}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/continue → brigade_cli-0.11.0/src/brigade/templates/codex}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/copilot → brigade_cli-0.11.0/src/brigade/templates/continue}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/cursor → brigade_cli-0.11.0/src/brigade/templates/copilot}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/goose → brigade_cli-0.11.0/src/brigade/templates/crush}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/hermes → brigade_cli-0.11.0/src/brigade/templates/cursor}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/depth/repo.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/depth/workspace.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/generic/harness-adapter-checklist.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/generic/memory-contract.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/kimi → brigade_cli-0.11.0/src/brigade/templates/goose}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/opencode → brigade_cli-0.11.0/src/brigade/templates/grok}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/handoff/handoff-sources.example.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/handoff/openclaw-ingest-receipt.example.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/adal.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/aider.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/antigravity.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/claude.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/codex.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/continue.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/copilot.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/cursor.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/goose.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/hermes.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/kimi.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/openclaw.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/opencode.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/openhands.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/pi.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/qwen.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/hermes/README.md +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/openhands → brigade_cli-0.11.0/src/brigade/templates/hermes}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/hooks/pre-push +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/includes/publisher.json +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/pi → brigade_cli-0.11.0/src/brigade/templates/kimi}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/backup-restic.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/chat-surface-crawlers.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/content-safety.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/handoff-flow.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/memory-architecture.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/memory-care-staleness.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/memory-scanner.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/multi-workspace-handoff-admin.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/obsidian-notes.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/pipeline-standups.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/tokenjuice-output-compaction.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/README.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/acp-escalation.openclaw.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/memory-sweep-cron.openclaw.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/model-aliases.openclaw.json +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/ollama-memory-search.openclaw.json +0 -0
- {brigade_cli-0.10.2/src/brigade/templates/qwen → brigade_cli-0.11.0/src/brigade/templates/opencode}/memory-handoffs/TEMPLATE.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/scripts/backup-restic.sh +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/skills/note/SKILL.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/AGENTS.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/CLAUDE.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/HEARTBEAT.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/IDENTITY.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/INSTALL_FOR_AGENTS.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/MEMORY.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/SAFETY_RULES.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/SOUL.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/TOOLS.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/USER.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/rules/acceptance-driven-work.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/templates/workspace/rules/issue-tdd-loop.md +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade/work_cmd/constants.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/dependency_links.txt +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/entry_points.txt +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/top_level.txt +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_cli_alias.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_cli_help.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_neutrality.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase96_cmd.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase98_cmd.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_phase99_cmd.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_privacy_regression.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_proc.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_reconfigure.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_research_cli_sources.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_runbook_cmd.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_runs_cmd.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_station.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_status.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_toml_compat.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_untrusted_cmd.py +0 -0
- {brigade_cli-0.10.2 → brigade_cli-0.11.0}/tests/test_work_cmd_facade.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brigade-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: AI agent memory, handoffs, and local guardrails for Codex, Claude Code, OpenCode, Hermes, and OpenClaw.
|
|
5
|
-
Author-email: Solomon Neas <
|
|
5
|
+
Author-email: Solomon Neas <me@solomonneas.dev>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://brigade.tools
|
|
8
8
|
Project-URL: Repository, https://github.com/escoffier-labs/brigade
|
|
@@ -23,6 +23,7 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest>=7; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff>=0.15; extra == "dev"
|
|
26
27
|
Provides-Extra: research
|
|
27
28
|
Requires-Dist: playwright>=1.40; extra == "research"
|
|
28
29
|
Dynamic: license-file
|
|
@@ -34,7 +35,7 @@ Dynamic: license-file
|
|
|
34
35
|
<h1 align="center">Brigade CLI</h1>
|
|
35
36
|
|
|
36
37
|
<p align="center">
|
|
37
|
-
<strong>AI agent memory, handoffs, and local guardrails for Codex, Claude Code, OpenCode, and a dozen other harnesses.</strong>
|
|
38
|
+
<strong>AI agent memory, handoffs, and local guardrails for Codex, Claude Code, OpenCode, and over a dozen other harnesses.</strong>
|
|
38
39
|
</p>
|
|
39
40
|
|
|
40
41
|
<p align="center">
|
|
@@ -46,6 +47,16 @@ Dynamic: license-file
|
|
|
46
47
|
|
|
47
48
|
Your agents run loops. Brigade keeps the receipts.
|
|
48
49
|
|
|
50
|
+
## Try it in 60 seconds
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pipx install brigade-cli
|
|
54
|
+
brigade operator quickstart --target ./my-repo --harnesses codex # wire one repo
|
|
55
|
+
brigade operator doctor --target ./my-repo --profile local-operator # verify
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
That installs the CLI, wires memory, handoffs, and local guardrails into one repo for a single harness, and prints a readiness check. Nothing leaves your machine and no daemon is started. Add `--dry-run` to preview the file-by-file plan before anything is written. More harnesses, workspace setups, and the homegrown-adoption path are under [Install](#install).
|
|
59
|
+
|
|
49
60
|
## Why I built this
|
|
50
61
|
|
|
51
62
|
I run an always-on OpenClaw agent next to daily Codex and Claude Code sessions, and I have since January. Every one of those tools wakes up empty. Whatever a session learned about my machine, my rules, or yesterday's dead ends scattered across tool-specific folders and died there.
|
|
@@ -117,7 +128,7 @@ brigade handoff lint --target ./my-repo
|
|
|
117
128
|
brigade handoff doctor --target ./my-repo
|
|
118
129
|
```
|
|
119
130
|
|
|
120
|
-
New here? Start with [docs/first-10-minutes.md](docs/first-10-minutes.md). Already have a homegrown setup with scripts, crons, and handoff folders? Brigade has an adoption path that inventories what you have before changing anything: start with `brigade operator adopt plan` and see the [technical guide](docs/technical-guide.md). Want an agent to set this up for you? Point it at this repo; [AGENTS.md](AGENTS.md) tells it exactly what to do and where to stop.
|
|
131
|
+
New here? Start with [QUICKSTART.md](QUICKSTART.md) for the five-minute install, then [docs/first-10-minutes.md](docs/first-10-minutes.md) for the guided first session. Already have a homegrown setup with scripts, crons, and handoff folders? Brigade has an adoption path that inventories what you have before changing anything: start with `brigade operator adopt plan` and see the [technical guide](docs/technical-guide.md). Want an agent to set this up for you? Point it at this repo; [AGENTS.md](AGENTS.md) tells it exactly what to do and where to stop.
|
|
121
132
|
|
|
122
133
|
## Harness support
|
|
123
134
|
|
|
@@ -139,6 +150,9 @@ Each writer gets its own local inbox; one canonical owner ingests. Brigade keeps
|
|
|
139
150
|
| Kimi Code | `kimi` | `.kimi/memory-handoffs/` |
|
|
140
151
|
| AdaL | `adal` | `.adal/memory-handoffs/` |
|
|
141
152
|
| OpenHands | `openhands` | `.openhands/memory-handoffs/` |
|
|
153
|
+
| Grok CLI | `grok` | `.grok/memory-handoffs/` |
|
|
154
|
+
| Amp | `amp` | `.amp/memory-handoffs/` |
|
|
155
|
+
| Crush | `crush` | `.crush/memory-handoffs/` |
|
|
142
156
|
| Hermes | `hermes` | `.hermes/memory-handoffs/` |
|
|
143
157
|
| OpenClaw | `openclaw` | usually the memory owner, not a writer |
|
|
144
158
|
|
|
@@ -149,9 +163,11 @@ All of them get handoff templates, ingest source coverage, and projected tools/s
|
|
|
149
163
|
The memory loop is the core. Around it, the same review-and-receipt pattern covers the rest of an operator's day, and you can ignore all of it until you need it:
|
|
150
164
|
|
|
151
165
|
- **Daily loop**: `brigade work brief` shows pending work, imports, and warnings; `brigade daily status` keeps it bounded and cheap.
|
|
166
|
+
- **Friction logs**: `brigade friction scan --days 30 --import-candidates` mines recent notes, handoffs, session artifacts, and optional local agent logs for reviewable workflow friction.
|
|
152
167
|
- **Security**: `brigade security scan` is a local read-only scanner for agent workspaces (secrets, risky hooks, MCP configs, prompt-injection patterns); `brigade scrub` gates content before it leaves the machine.
|
|
153
168
|
- **Tools and skills**: one reviewed catalog projected into every harness's native format, with approval gates for anything that executes.
|
|
154
169
|
- **Research**: `brigade research run` turns a question into a cited local report and a reviewable memory handoff.
|
|
170
|
+
- **Cross-model runs**: `brigade run "<task>"` plans, dispatches, and synthesizes one bounded task across the agent CLIs in your roster, so an expensive model can think while cheaper ones do the grunt work. Rosters pin a model per agent, plans can stage dependent workers, and `--worktree` runs everything in a detached git checkout that comes back as a reviewable `changes.patch`. A dirty-tree guard and a run lock keep agents away from your work in progress.
|
|
155
171
|
- **Fleet and release**: health evidence across your local repos and release-readiness receipts, with no publish step.
|
|
156
172
|
|
|
157
173
|
The full tour of every station lives in [docs/overview.md](docs/overview.md).
|
|
@@ -187,6 +203,7 @@ That pause is the point. Agent memory should be useful, not noisy.
|
|
|
187
203
|
- [Handoff promotion](docs/handoff-promotion.md): how notes move toward memory.
|
|
188
204
|
- [Repo fleet](docs/repo-fleet.md) and [Tool catalog](docs/tool-catalog.md).
|
|
189
205
|
- [Command inventory](docs/command-inventory.md): every public CLI command.
|
|
206
|
+
- [Maintainers](MAINTAINERS.md), [Governance](GOVERNANCE.md), [Security](SECURITY.md), and [Contributing](CONTRIBUTING.md).
|
|
190
207
|
- [Roadmap](ROADMAP.md) and [roadmap archive](docs/roadmap-archive.md).
|
|
191
208
|
|
|
192
209
|
Project identity: GitHub [`escoffier-labs/brigade`](https://github.com/escoffier-labs/brigade), website [brigade.tools](https://brigade.tools), PyPI [`brigade-cli`](https://pypi.org/project/brigade-cli/), command `brigade`. The name comes from the kitchen: a *brigade de cuisine* runs the line, and *mise en place* means the station is prepped before service. Set up the rules, memory, tools, and receipts before the session gets expensive.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Quickstart
|
|
2
2
|
|
|
3
|
-
Five minutes from
|
|
3
|
+
Five minutes from install to a working agent kitchen.
|
|
4
4
|
|
|
5
5
|
## 1. Install
|
|
6
6
|
|
|
@@ -23,7 +23,19 @@ python3 -m pipx ensurepath
|
|
|
23
23
|
|
|
24
24
|
## First install
|
|
25
25
|
|
|
26
|
-
The
|
|
26
|
+
The canonical first-run command is `brigade operator quickstart`. It installs the template files, wires the operator config, and runs the health checks in one shot:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Code repo with Codex as the writer
|
|
30
|
+
brigade operator quickstart --target ./my-repo --harnesses codex
|
|
31
|
+
|
|
32
|
+
# OpenClaw or Hermes workspace instead of a code repo
|
|
33
|
+
brigade operator quickstart --target ~/agent-workspace --depth workspace --harnesses openclaw,hermes --owner openclaw
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Pass `--dry-run` first to preview the planned steps without writing anything.
|
|
37
|
+
|
|
38
|
+
Two commands share this surface: `brigade init` installs the template files only, and `brigade operator quickstart` wraps it (init, then operator config, then doctor). Use `init` when you want the interactive harness picker or just the files:
|
|
27
39
|
|
|
28
40
|
```bash
|
|
29
41
|
$ brigade init --target ~/agent-kitchen
|
|
@@ -31,8 +43,23 @@ $ brigade init --target ~/agent-kitchen
|
|
|
31
43
|
Which harnesses do you use? (type numbers separated by space/comma to toggle, enter to confirm)
|
|
32
44
|
[x] 1. Claude Code
|
|
33
45
|
[ ] 2. Codex
|
|
34
|
-
[ ] 3.
|
|
35
|
-
[ ] 4.
|
|
46
|
+
[ ] 3. OpenCode
|
|
47
|
+
[ ] 4. Antigravity
|
|
48
|
+
[ ] 5. Pi
|
|
49
|
+
[ ] 6. Cursor
|
|
50
|
+
[ ] 7. Aider
|
|
51
|
+
[ ] 8. Goose
|
|
52
|
+
[ ] 9. Continue
|
|
53
|
+
[ ] 10. GitHub Copilot CLI
|
|
54
|
+
[ ] 11. Qwen Code
|
|
55
|
+
[ ] 12. Kimi Code
|
|
56
|
+
[ ] 13. AdaL
|
|
57
|
+
[ ] 14. OpenHands
|
|
58
|
+
[ ] 15. Grok CLI
|
|
59
|
+
[ ] 16. Amp
|
|
60
|
+
[ ] 17. Crush
|
|
61
|
+
[ ] 18. OpenClaw
|
|
62
|
+
[ ] 19. Hermes (experimental)
|
|
36
63
|
|
|
37
64
|
Depth? (type a number, enter for default)
|
|
38
65
|
* 1. repo (handoff flow + publish guard)
|
|
@@ -46,24 +73,30 @@ Defaults are claude harness, repo depth, no includes. Enter ships the install.
|
|
|
46
73
|
|
|
47
74
|
## CI / scripted install
|
|
48
75
|
|
|
49
|
-
Pass flags directly to skip the prompt
|
|
76
|
+
Pass flags directly to skip the prompt. The same flags work on `operator quickstart`:
|
|
50
77
|
|
|
51
78
|
```bash
|
|
52
79
|
# Claude Code + Codex + OpenClaw, full workspace
|
|
53
|
-
brigade
|
|
80
|
+
brigade operator quickstart --target ~/agent-kitchen \
|
|
54
81
|
--depth workspace \
|
|
55
82
|
--harnesses claude,codex,openclaw
|
|
56
83
|
|
|
57
84
|
# Codex-only project, minimal install
|
|
58
|
-
brigade
|
|
85
|
+
brigade operator quickstart --target ./my-project --depth repo --harnesses codex
|
|
59
86
|
|
|
60
|
-
#
|
|
87
|
+
# Template files only, no harness-specific files
|
|
61
88
|
brigade init --target ./my-project --harnesses none
|
|
62
89
|
```
|
|
63
90
|
|
|
64
91
|
## Verifying
|
|
65
92
|
|
|
66
|
-
After install,
|
|
93
|
+
After install, check the operator profile:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
brigade operator doctor --target <path> --profile local-operator
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For the file-by-file view, `brigade doctor --target <path>` reports the apparent harness shape and checks every configured inbox and adapter:
|
|
67
100
|
|
|
68
101
|
```
|
|
69
102
|
brigade doctor: target /home/you/agent-kitchen
|
|
@@ -103,6 +136,6 @@ See the [Solo Cookbook](https://github.com/escoffier-labs/solos-cookbook) for th
|
|
|
103
136
|
- Read [the cookbook](https://github.com/escoffier-labs/solos-cookbook) for the deep version of every concept here.
|
|
104
137
|
- Customize `USER.md` and `TOOLS.md` with your real preferences and runbooks (kept private; do not commit personal details).
|
|
105
138
|
- Wire the ingester on a cron or a manual end-of-day workflow.
|
|
106
|
-
- Add a memory-care staleness scan when your card set starts to matter. See
|
|
107
|
-
- If you use TokenJuice, wire Claude Code and Codex hooks deliberately and tell agents what the wrapper means. See
|
|
139
|
+
- Add a memory-care staleness scan when your card set starts to matter. See [docs/memory-care.md](docs/memory-care.md).
|
|
140
|
+
- If you use TokenJuice, wire Claude Code and Codex hooks deliberately and tell agents what the wrapper means. See the tokens station in [docs/technical-guide.md](docs/technical-guide.md#managed-stations).
|
|
108
141
|
- Run `brigade work bootstrap` inside active repos when you want the dogfood-backed daily work loop, scanner inbox, and local evidence receipts.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<h1 align="center">Brigade CLI</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>AI agent memory, handoffs, and local guardrails for Codex, Claude Code, OpenCode, and a dozen other harnesses.</strong>
|
|
8
|
+
<strong>AI agent memory, handoffs, and local guardrails for Codex, Claude Code, OpenCode, and over a dozen other harnesses.</strong>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
@@ -17,6 +17,16 @@
|
|
|
17
17
|
|
|
18
18
|
Your agents run loops. Brigade keeps the receipts.
|
|
19
19
|
|
|
20
|
+
## Try it in 60 seconds
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pipx install brigade-cli
|
|
24
|
+
brigade operator quickstart --target ./my-repo --harnesses codex # wire one repo
|
|
25
|
+
brigade operator doctor --target ./my-repo --profile local-operator # verify
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That installs the CLI, wires memory, handoffs, and local guardrails into one repo for a single harness, and prints a readiness check. Nothing leaves your machine and no daemon is started. Add `--dry-run` to preview the file-by-file plan before anything is written. More harnesses, workspace setups, and the homegrown-adoption path are under [Install](#install).
|
|
29
|
+
|
|
20
30
|
## Why I built this
|
|
21
31
|
|
|
22
32
|
I run an always-on OpenClaw agent next to daily Codex and Claude Code sessions, and I have since January. Every one of those tools wakes up empty. Whatever a session learned about my machine, my rules, or yesterday's dead ends scattered across tool-specific folders and died there.
|
|
@@ -88,7 +98,7 @@ brigade handoff lint --target ./my-repo
|
|
|
88
98
|
brigade handoff doctor --target ./my-repo
|
|
89
99
|
```
|
|
90
100
|
|
|
91
|
-
New here? Start with [docs/first-10-minutes.md](docs/first-10-minutes.md). Already have a homegrown setup with scripts, crons, and handoff folders? Brigade has an adoption path that inventories what you have before changing anything: start with `brigade operator adopt plan` and see the [technical guide](docs/technical-guide.md). Want an agent to set this up for you? Point it at this repo; [AGENTS.md](AGENTS.md) tells it exactly what to do and where to stop.
|
|
101
|
+
New here? Start with [QUICKSTART.md](QUICKSTART.md) for the five-minute install, then [docs/first-10-minutes.md](docs/first-10-minutes.md) for the guided first session. Already have a homegrown setup with scripts, crons, and handoff folders? Brigade has an adoption path that inventories what you have before changing anything: start with `brigade operator adopt plan` and see the [technical guide](docs/technical-guide.md). Want an agent to set this up for you? Point it at this repo; [AGENTS.md](AGENTS.md) tells it exactly what to do and where to stop.
|
|
92
102
|
|
|
93
103
|
## Harness support
|
|
94
104
|
|
|
@@ -110,6 +120,9 @@ Each writer gets its own local inbox; one canonical owner ingests. Brigade keeps
|
|
|
110
120
|
| Kimi Code | `kimi` | `.kimi/memory-handoffs/` |
|
|
111
121
|
| AdaL | `adal` | `.adal/memory-handoffs/` |
|
|
112
122
|
| OpenHands | `openhands` | `.openhands/memory-handoffs/` |
|
|
123
|
+
| Grok CLI | `grok` | `.grok/memory-handoffs/` |
|
|
124
|
+
| Amp | `amp` | `.amp/memory-handoffs/` |
|
|
125
|
+
| Crush | `crush` | `.crush/memory-handoffs/` |
|
|
113
126
|
| Hermes | `hermes` | `.hermes/memory-handoffs/` |
|
|
114
127
|
| OpenClaw | `openclaw` | usually the memory owner, not a writer |
|
|
115
128
|
|
|
@@ -120,9 +133,11 @@ All of them get handoff templates, ingest source coverage, and projected tools/s
|
|
|
120
133
|
The memory loop is the core. Around it, the same review-and-receipt pattern covers the rest of an operator's day, and you can ignore all of it until you need it:
|
|
121
134
|
|
|
122
135
|
- **Daily loop**: `brigade work brief` shows pending work, imports, and warnings; `brigade daily status` keeps it bounded and cheap.
|
|
136
|
+
- **Friction logs**: `brigade friction scan --days 30 --import-candidates` mines recent notes, handoffs, session artifacts, and optional local agent logs for reviewable workflow friction.
|
|
123
137
|
- **Security**: `brigade security scan` is a local read-only scanner for agent workspaces (secrets, risky hooks, MCP configs, prompt-injection patterns); `brigade scrub` gates content before it leaves the machine.
|
|
124
138
|
- **Tools and skills**: one reviewed catalog projected into every harness's native format, with approval gates for anything that executes.
|
|
125
139
|
- **Research**: `brigade research run` turns a question into a cited local report and a reviewable memory handoff.
|
|
140
|
+
- **Cross-model runs**: `brigade run "<task>"` plans, dispatches, and synthesizes one bounded task across the agent CLIs in your roster, so an expensive model can think while cheaper ones do the grunt work. Rosters pin a model per agent, plans can stage dependent workers, and `--worktree` runs everything in a detached git checkout that comes back as a reviewable `changes.patch`. A dirty-tree guard and a run lock keep agents away from your work in progress.
|
|
126
141
|
- **Fleet and release**: health evidence across your local repos and release-readiness receipts, with no publish step.
|
|
127
142
|
|
|
128
143
|
The full tour of every station lives in [docs/overview.md](docs/overview.md).
|
|
@@ -158,6 +173,7 @@ That pause is the point. Agent memory should be useful, not noisy.
|
|
|
158
173
|
- [Handoff promotion](docs/handoff-promotion.md): how notes move toward memory.
|
|
159
174
|
- [Repo fleet](docs/repo-fleet.md) and [Tool catalog](docs/tool-catalog.md).
|
|
160
175
|
- [Command inventory](docs/command-inventory.md): every public CLI command.
|
|
176
|
+
- [Maintainers](MAINTAINERS.md), [Governance](GOVERNANCE.md), [Security](SECURITY.md), and [Contributing](CONTRIBUTING.md).
|
|
161
177
|
- [Roadmap](ROADMAP.md) and [roadmap archive](docs/roadmap-archive.md).
|
|
162
178
|
|
|
163
179
|
Project identity: GitHub [`escoffier-labs/brigade`](https://github.com/escoffier-labs/brigade), website [brigade.tools](https://brigade.tools), PyPI [`brigade-cli`](https://pypi.org/project/brigade-cli/), command `brigade`. The name comes from the kitchen: a *brigade de cuisine* runs the line, and *mise en place* means the station is prepped before service. Set up the rules, memory, tools, and receipts before the session gets expensive.
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "brigade-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.11.0"
|
|
8
8
|
description = "AI agent memory, handoffs, and local guardrails for Codex, Claude Code, OpenCode, Hermes, and OpenClaw."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = { text = "MIT" }
|
|
12
|
-
authors = [{ name = "Solomon Neas", email = "
|
|
12
|
+
authors = [{ name = "Solomon Neas", email = "me@solomonneas.dev" }] # content-guard: allow email
|
|
13
13
|
keywords = ["agents", "ai-agents", "agent-memory", "agent-handoffs", "ai-memory", "openclaw", "claude-code", "codex", "opencode", "memory", "bootstrap", "brigade", "brigade-cli", "operator", "local-first", "guardrails", "agents-md"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 3 - Alpha",
|
|
@@ -22,7 +22,7 @@ classifiers = [
|
|
|
22
22
|
dependencies = []
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
|
-
dev = ["pytest>=7"]
|
|
25
|
+
dev = ["pytest>=7", "ruff>=0.15"]
|
|
26
26
|
research = ["playwright>=1.40"]
|
|
27
27
|
|
|
28
28
|
[project.urls]
|
|
@@ -47,3 +47,14 @@ brigade = ["py.typed", "templates/**/*"]
|
|
|
47
47
|
[tool.pytest.ini_options]
|
|
48
48
|
testpaths = ["tests"]
|
|
49
49
|
addopts = "-ra"
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 120
|
|
53
|
+
src = ["src", "tests"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
# Pragmatic gate: ruff's default rule set (pyflakes plus the E4/E7/E9
|
|
57
|
+
# pycodestyle errors) plus bugbear (B). Deferred for later, separate
|
|
58
|
+
# passes: import sorting (I), pyupgrade (UP), the full pycodestyle E/W
|
|
59
|
+
# set, and any type checking (mypy/pyright).
|
|
60
|
+
select = ["B", "E4", "E7", "E9", "F"]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Bounded cross-model orchestration for `brigade run`."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import json
|
|
@@ -19,6 +20,7 @@ from .roster import Agent, Roster, is_cli_allowed, timeout_for, workers
|
|
|
19
20
|
class Assignment:
|
|
20
21
|
worker: str
|
|
21
22
|
task: str
|
|
23
|
+
stage: int = 1
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
@dataclass(frozen=True)
|
|
@@ -36,9 +38,7 @@ def build_plan_prompt(
|
|
|
36
38
|
corrective_note: str | None = None,
|
|
37
39
|
read_only: bool = False,
|
|
38
40
|
) -> str:
|
|
39
|
-
worker_lines = "\n".join(
|
|
40
|
-
f"- {agent.name}: cli={agent.cli}; role={agent.role}" for agent in workers(roster)
|
|
41
|
-
)
|
|
41
|
+
worker_lines = "\n".join(f"- {agent.name}: cli={agent.cli}; role={agent.role}" for agent in workers(roster))
|
|
42
42
|
if not worker_lines:
|
|
43
43
|
worker_lines = "- no workers configured"
|
|
44
44
|
|
|
@@ -47,11 +47,14 @@ def build_plan_prompt(
|
|
|
47
47
|
return (
|
|
48
48
|
"You are the Brigade aboyeur. Split the user's task across the available workers.\n"
|
|
49
49
|
"Return exactly one JSON object, with no prose outside JSON:\n"
|
|
50
|
-
'{"assignments":[{"worker":"<worker-name>","task":"<specific sub-task>"}]}\n'
|
|
50
|
+
'{"assignments":[{"stage":1,"worker":"<worker-name>","task":"<specific sub-task>"}]}\n'
|
|
51
51
|
f"{note}\n"
|
|
52
52
|
f"User task:\n{task}\n\n"
|
|
53
53
|
f"Available workers, excluding you:\n{worker_lines}\n\n"
|
|
54
|
-
f"Rules:\n- Use at most {roster.max_workers} assignments.\n"
|
|
54
|
+
f"Rules:\n- Use at most {roster.max_workers} assignments per stage.\n"
|
|
55
|
+
"- Stage must be a positive integer starting at stage 1.\n"
|
|
56
|
+
"- Assignments in the same stage run in parallel; later stages receive earlier-stage worker results.\n"
|
|
57
|
+
"- Omit stage only for backwards-compatible stage 1 assignments.\n"
|
|
55
58
|
"- Assign only listed workers.\n"
|
|
56
59
|
"- Use zero assignments only if no worker is useful."
|
|
57
60
|
f"{policy}"
|
|
@@ -139,14 +142,21 @@ def write_run_handoff(
|
|
|
139
142
|
timestamp = (now or datetime.now(timezone.utc)).strftime("%Y-%m-%d-%H%M")
|
|
140
143
|
safe_task = _one_line(task)
|
|
141
144
|
path = inbox / f"{timestamp}-brigade-run-{_slug(safe_task)}.md"
|
|
142
|
-
worker_summary =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
)
|
|
145
|
+
worker_summary = (
|
|
146
|
+
"\n".join(
|
|
147
|
+
f"- {result.worker}: {'ok' if result.ok else 'failed'}"
|
|
148
|
+
+ (f" ({_one_line(result.detail)})" if result.detail else "")
|
|
149
|
+
for result in worker_results
|
|
150
|
+
)
|
|
151
|
+
or "- no workers dispatched"
|
|
152
|
+
)
|
|
153
|
+
assignment_summary = (
|
|
154
|
+
"\n".join(
|
|
155
|
+
f"- stage {assignment.stage} -> {assignment.worker}: {_one_line(assignment.task)}"
|
|
156
|
+
for assignment in assignments
|
|
157
|
+
)
|
|
158
|
+
or "- no worker assignments"
|
|
159
|
+
)
|
|
150
160
|
artifact_line = f"- artifacts: `{output_dir}`" if output_dir is not None else "- artifacts: none"
|
|
151
161
|
cwd_line = f"- cwd: `{cwd}`" if cwd is not None else "- cwd: not set"
|
|
152
162
|
mode_line = "- mode: read-only" if read_only else "- mode: normal"
|
|
@@ -229,30 +239,38 @@ def parse_plan(text: str, roster: Roster) -> list[Assignment]:
|
|
|
229
239
|
raw_assignments = payload.get("assignments")
|
|
230
240
|
if not isinstance(raw_assignments, list):
|
|
231
241
|
raise ValueError("plan JSON needs an assignments list")
|
|
232
|
-
if len(raw_assignments) > roster.max_workers:
|
|
233
|
-
raise ValueError(f"plan has {len(raw_assignments)} assignments, limit is {roster.max_workers}")
|
|
234
242
|
|
|
235
243
|
assignments: list[Assignment] = []
|
|
236
|
-
seen: set[tuple[str, str]] = set()
|
|
244
|
+
seen: set[tuple[int, str, str]] = set()
|
|
245
|
+
stage_counts: dict[int, int] = {}
|
|
237
246
|
for item in raw_assignments:
|
|
238
247
|
if not isinstance(item, dict):
|
|
239
248
|
raise ValueError("each assignment must be an object")
|
|
240
|
-
|
|
249
|
+
stage = item.get("stage", 1)
|
|
250
|
+
if isinstance(stage, bool) or not isinstance(stage, int) or stage < 1:
|
|
251
|
+
raise ValueError("assignment.stage must be a positive integer")
|
|
252
|
+
raw_worker = item.get("worker")
|
|
241
253
|
subtask = item.get("task")
|
|
242
|
-
if not isinstance(
|
|
254
|
+
if not isinstance(raw_worker, str) or not raw_worker.strip():
|
|
243
255
|
raise ValueError("assignment.worker must be a non-empty string")
|
|
256
|
+
worker = raw_worker.strip()
|
|
244
257
|
if worker not in roster.agents:
|
|
245
258
|
raise ValueError(f"assignment references unknown worker: {worker!r}")
|
|
246
259
|
if worker == roster.orchestrator:
|
|
247
260
|
raise ValueError("assignment cannot target the orchestrator")
|
|
248
261
|
if not isinstance(subtask, str) or not subtask.strip():
|
|
249
262
|
raise ValueError("assignment.task must be a non-empty string")
|
|
250
|
-
assignment = Assignment(worker=worker
|
|
251
|
-
key = (assignment.worker, assignment.task)
|
|
263
|
+
assignment = Assignment(worker=worker, task=subtask.strip(), stage=stage)
|
|
264
|
+
key = (assignment.stage, assignment.worker, assignment.task)
|
|
252
265
|
if key not in seen:
|
|
253
266
|
assignments.append(assignment)
|
|
254
267
|
seen.add(key)
|
|
255
|
-
|
|
268
|
+
stage_counts[assignment.stage] = stage_counts.get(assignment.stage, 0) + 1
|
|
269
|
+
|
|
270
|
+
for stage, count in stage_counts.items():
|
|
271
|
+
if count > roster.max_workers:
|
|
272
|
+
raise ValueError(f"plan has {count} assignments in stage {stage}, limit is {roster.max_workers}")
|
|
273
|
+
return sorted(assignments, key=lambda assignment: assignment.stage)
|
|
256
274
|
|
|
257
275
|
|
|
258
276
|
def _record_plan_attempt(
|
|
@@ -299,6 +317,8 @@ def _run_orchestrator(
|
|
|
299
317
|
}
|
|
300
318
|
if sandbox is not None:
|
|
301
319
|
kwargs["sandbox"] = sandbox
|
|
320
|
+
if orchestrator.model is not None:
|
|
321
|
+
kwargs["model"] = orchestrator.model
|
|
302
322
|
return agents.run_agent(orchestrator.cli, prompt, **kwargs)
|
|
303
323
|
|
|
304
324
|
|
|
@@ -353,13 +373,39 @@ def plan(
|
|
|
353
373
|
raise RuntimeError(f"orchestrator returned an invalid plan: {second_exc}") from second_exc
|
|
354
374
|
|
|
355
375
|
|
|
356
|
-
def
|
|
376
|
+
def _render_prior_results(results: list[WorkerResult]) -> str:
|
|
377
|
+
return "\n\n".join(
|
|
378
|
+
"\n".join(
|
|
379
|
+
[
|
|
380
|
+
f"Worker: {result.worker}",
|
|
381
|
+
f"Sub-task: {result.task}",
|
|
382
|
+
f"Status: {'ok' if result.ok else 'failed'}",
|
|
383
|
+
f"Detail: {result.detail}" if result.detail else "Detail:",
|
|
384
|
+
"Output:",
|
|
385
|
+
result.text or "(no output)",
|
|
386
|
+
]
|
|
387
|
+
)
|
|
388
|
+
for result in results
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _worker_prompt(
|
|
393
|
+
agent: Agent,
|
|
394
|
+
assignment: Assignment,
|
|
395
|
+
*,
|
|
396
|
+
prior_results: list[WorkerResult] | None = None,
|
|
397
|
+
read_only: bool = False,
|
|
398
|
+
) -> str:
|
|
399
|
+
prior_context = ""
|
|
400
|
+
if prior_results:
|
|
401
|
+
prior_context = f"\n\nEarlier-stage context:\n{_render_prior_results(prior_results)}"
|
|
357
402
|
policy = f"\n\n{_read_only_rules()}" if read_only else ""
|
|
358
403
|
return (
|
|
359
404
|
f"You are Brigade worker {agent.name}.\n"
|
|
360
405
|
f"Role:\n{agent.role}\n\n"
|
|
361
406
|
f"Sub-task:\n{assignment.task}\n\n"
|
|
362
407
|
"Return a concise, complete result for the orchestrator to synthesize."
|
|
408
|
+
f"{prior_context}"
|
|
363
409
|
f"{policy}"
|
|
364
410
|
)
|
|
365
411
|
|
|
@@ -372,7 +418,7 @@ def dispatch(
|
|
|
372
418
|
sandbox_read_only: bool | None = None,
|
|
373
419
|
sandbox: str | None = None,
|
|
374
420
|
) -> list[WorkerResult]:
|
|
375
|
-
def run_one(assignment: Assignment) -> WorkerResult:
|
|
421
|
+
def run_one(assignment: Assignment, prior_results: list[WorkerResult]) -> WorkerResult:
|
|
376
422
|
agent = roster.agents[assignment.worker]
|
|
377
423
|
if not is_cli_allowed(agent.cli, roster):
|
|
378
424
|
return WorkerResult(
|
|
@@ -389,7 +435,13 @@ def dispatch(
|
|
|
389
435
|
}
|
|
390
436
|
if sandbox is not None:
|
|
391
437
|
kwargs["sandbox"] = sandbox
|
|
392
|
-
|
|
438
|
+
if agent.model is not None:
|
|
439
|
+
kwargs["model"] = agent.model
|
|
440
|
+
result = agents.run_agent(
|
|
441
|
+
agent.cli,
|
|
442
|
+
_worker_prompt(agent, assignment, prior_results=prior_results, read_only=read_only),
|
|
443
|
+
**kwargs,
|
|
444
|
+
)
|
|
393
445
|
return WorkerResult(
|
|
394
446
|
worker=assignment.worker,
|
|
395
447
|
task=assignment.task,
|
|
@@ -401,28 +453,34 @@ def dispatch(
|
|
|
401
453
|
if not assignments:
|
|
402
454
|
return []
|
|
403
455
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
456
|
+
all_results: list[WorkerResult] = []
|
|
457
|
+
stages = sorted({assignment.stage for assignment in assignments})
|
|
458
|
+
for stage in stages:
|
|
459
|
+
stage_assignments = [assignment for assignment in assignments if assignment.stage == stage]
|
|
460
|
+
stage_results_by_index: dict[int, WorkerResult] = {}
|
|
461
|
+
prior_results = list(all_results)
|
|
462
|
+
max_workers = min(roster.max_workers, len(stage_assignments))
|
|
463
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
464
|
+
future_to_index = {
|
|
465
|
+
executor.submit(run_one, assignment, prior_results): index
|
|
466
|
+
for index, assignment in enumerate(stage_assignments)
|
|
467
|
+
}
|
|
468
|
+
for future in as_completed(future_to_index):
|
|
469
|
+
index = future_to_index[future]
|
|
470
|
+
try:
|
|
471
|
+
stage_results_by_index[index] = future.result()
|
|
472
|
+
except Exception as exc: # pragma: no cover - defensive boundary
|
|
473
|
+
assignment = stage_assignments[index]
|
|
474
|
+
stage_results_by_index[index] = WorkerResult(
|
|
475
|
+
worker=assignment.worker,
|
|
476
|
+
task=assignment.task,
|
|
477
|
+
text="",
|
|
478
|
+
ok=False,
|
|
479
|
+
detail=str(exc)[:200],
|
|
480
|
+
)
|
|
481
|
+
all_results.extend(stage_results_by_index[index] for index in range(len(stage_assignments)))
|
|
482
|
+
|
|
483
|
+
return all_results
|
|
426
484
|
|
|
427
485
|
|
|
428
486
|
def build_synth_prompt(task: str, results: list[WorkerResult], read_only: bool = False) -> str:
|
|
@@ -458,8 +516,16 @@ def _print_plan(assignments: list[Assignment]) -> None:
|
|
|
458
516
|
if not assignments:
|
|
459
517
|
print(" (no worker assignments)")
|
|
460
518
|
return
|
|
461
|
-
for assignment in assignments
|
|
462
|
-
|
|
519
|
+
stages = sorted({assignment.stage for assignment in assignments})
|
|
520
|
+
if len(stages) == 1:
|
|
521
|
+
for assignment in assignments:
|
|
522
|
+
print(f" -> {assignment.worker}: {assignment.task}")
|
|
523
|
+
return
|
|
524
|
+
for stage in stages:
|
|
525
|
+
print(f" stage {stage}:")
|
|
526
|
+
for assignment in assignments:
|
|
527
|
+
if assignment.stage == stage:
|
|
528
|
+
print(f" -> {assignment.worker}: {assignment.task}")
|
|
463
529
|
|
|
464
530
|
|
|
465
531
|
def _print_worker_status(results: list[WorkerResult]) -> None:
|
|
@@ -473,10 +539,9 @@ def _print_worker_status(results: list[WorkerResult]) -> None:
|
|
|
473
539
|
print(f" [{marker}] {result.worker}{detail}")
|
|
474
540
|
|
|
475
541
|
|
|
476
|
-
def _assignment_payload(assignments: list[Assignment]) -> list[dict[str,
|
|
542
|
+
def _assignment_payload(assignments: list[Assignment]) -> list[dict[str, object]]:
|
|
477
543
|
return [
|
|
478
|
-
{"worker": assignment.worker, "task": assignment.task}
|
|
479
|
-
for assignment in assignments
|
|
544
|
+
{"stage": assignment.stage, "worker": assignment.worker, "task": assignment.task} for assignment in assignments
|
|
480
545
|
]
|
|
481
546
|
|
|
482
547
|
|
|
@@ -507,9 +572,11 @@ def _roster_payload(roster: Roster) -> dict[str, object]:
|
|
|
507
572
|
"max_workers": roster.max_workers,
|
|
508
573
|
"timeout_seconds": roster.timeout_seconds,
|
|
509
574
|
"allow_models": list(roster.allow_models),
|
|
575
|
+
"sandbox": roster.sandbox,
|
|
510
576
|
"agents": {
|
|
511
577
|
name: {
|
|
512
578
|
"cli": agent.cli,
|
|
579
|
+
"model": agent.model,
|
|
513
580
|
"role": agent.role,
|
|
514
581
|
"timeout_seconds": agent.timeout_seconds,
|
|
515
582
|
}
|
|
@@ -11,6 +11,7 @@ The phase-ledger queue in phases_cmd stays local: it stores one JSON file
|
|
|
11
11
|
per action and stamps `reviewed_at`/`review_reason` instead of the
|
|
12
12
|
`started_at`/`completed_at`/`deferred_at` lifecycle fields below.
|
|
13
13
|
"""
|
|
14
|
+
|
|
14
15
|
from __future__ import annotations
|
|
15
16
|
|
|
16
17
|
import json
|