brigade-cli 0.10.3__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.
Files changed (320) hide show
  1. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/PKG-INFO +19 -3
  2. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/QUICKSTART.md +30 -9
  3. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/README.md +17 -1
  4. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/pyproject.toml +7 -7
  5. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/__init__.py +1 -1
  6. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/aboyeur.py +107 -37
  7. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/agents.py +45 -6
  8. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/center_cmd.py +3 -2
  9. brigade_cli-0.11.0/src/brigade/cli/__init__.py +147 -0
  10. brigade_cli-0.11.0/src/brigade/cli/_common.py +56 -0
  11. brigade_cli-0.11.0/src/brigade/cli/add.py +20 -0
  12. brigade_cli-0.11.0/src/brigade/cli/budgets.py +30 -0
  13. brigade_cli-0.11.0/src/brigade/cli/center.py +326 -0
  14. brigade_cli-0.11.0/src/brigade/cli/chat.py +91 -0
  15. brigade_cli-0.11.0/src/brigade/cli/context.py +87 -0
  16. brigade_cli-0.11.0/src/brigade/cli/daily.py +225 -0
  17. brigade_cli-0.11.0/src/brigade/cli/doctor.py +24 -0
  18. brigade_cli-0.11.0/src/brigade/cli/dogfood.py +90 -0
  19. brigade_cli-0.11.0/src/brigade/cli/friction.py +84 -0
  20. brigade_cli-0.11.0/src/brigade/cli/handoff.py +390 -0
  21. brigade_cli-0.11.0/src/brigade/cli/handoff_template.py +25 -0
  22. brigade_cli-0.11.0/src/brigade/cli/hermes_fragments.py +19 -0
  23. brigade_cli-0.11.0/src/brigade/cli/ingest.py +35 -0
  24. brigade_cli-0.11.0/src/brigade/cli/init.py +102 -0
  25. brigade_cli-0.11.0/src/brigade/cli/learn.py +189 -0
  26. brigade_cli-0.11.0/src/brigade/cli/memory.py +109 -0
  27. brigade_cli-0.11.0/src/brigade/cli/notifications.py +106 -0
  28. brigade_cli-0.11.0/src/brigade/cli/openclaw_fragments.py +19 -0
  29. brigade_cli-0.11.0/src/brigade/cli/operator.py +404 -0
  30. brigade_cli-0.11.0/src/brigade/cli/pantry.py +90 -0
  31. brigade_cli-0.11.0/src/brigade/cli/projects.py +122 -0
  32. brigade_cli-0.11.0/src/brigade/cli/reconfigure.py +46 -0
  33. brigade_cli-0.11.0/src/brigade/cli/release.py +289 -0
  34. brigade_cli-0.11.0/src/brigade/cli/repos.py +862 -0
  35. brigade_cli-0.11.0/src/brigade/cli/research.py +183 -0
  36. brigade_cli-0.11.0/src/brigade/cli/roadmap.py +58 -0
  37. brigade_cli-0.11.0/src/brigade/cli/roster.py +47 -0
  38. brigade_cli-0.11.0/src/brigade/cli/run.py +195 -0
  39. brigade_cli-0.11.0/src/brigade/cli/runbook.py +75 -0
  40. brigade_cli-0.11.0/src/brigade/cli/runs.py +56 -0
  41. brigade_cli-0.11.0/src/brigade/cli/scrub.py +25 -0
  42. brigade_cli-0.11.0/src/brigade/cli/security.py +203 -0
  43. brigade_cli-0.11.0/src/brigade/cli/skills.py +268 -0
  44. brigade_cli-0.11.0/src/brigade/cli/status.py +19 -0
  45. brigade_cli-0.11.0/src/brigade/cli/tools.py +494 -0
  46. brigade_cli-0.11.0/src/brigade/cli/untrusted.py +47 -0
  47. brigade_cli-0.11.0/src/brigade/cli/work.py +2044 -0
  48. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/doctor.py +6 -4
  49. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/dogfood_cmd.py +4 -34
  50. brigade_cli-0.11.0/src/brigade/friction_cmd.py +588 -0
  51. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/install.py +6 -0
  52. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/learn_cmd.py +3 -0
  53. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/localio.py +37 -0
  54. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/memory_cmd.py +2 -1
  55. brigade_cli-0.11.0/src/brigade/operator_cmd/__init__.py +91 -0
  56. brigade_cli-0.11.0/src/brigade/operator_cmd/adoption.py +407 -0
  57. brigade_cli-0.11.0/src/brigade/operator_cmd/guide.py +190 -0
  58. brigade_cli-0.11.0/src/brigade/operator_cmd/health.py +528 -0
  59. brigade_cli-0.11.0/src/brigade/operator_cmd/lifecycle.py +628 -0
  60. brigade_cli-0.11.0/src/brigade/operator_cmd/migration.py +589 -0
  61. brigade_cli-0.11.0/src/brigade/operator_cmd/surfaces.py +1045 -0
  62. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/proc.py +1 -0
  63. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/prompt.py +6 -0
  64. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/llm.py +6 -5
  65. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research_cmd.py +3 -1
  66. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/roster.py +14 -0
  67. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/roster_cmd.py +18 -0
  68. brigade_cli-0.11.0/src/brigade/runguard.py +192 -0
  69. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/security_cmd.py +9 -3
  70. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/selection.py +6 -0
  71. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/skills_cmd.py +3 -0
  72. brigade_cli-0.11.0/src/brigade/templates/harnesses/amp.json +11 -0
  73. brigade_cli-0.11.0/src/brigade/templates/harnesses/crush.json +11 -0
  74. brigade_cli-0.11.0/src/brigade/templates/harnesses/grok.json +11 -0
  75. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/hermes/memory-handoff.harness.json +1 -1
  76. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/hermes/model-lanes.harness.json +1 -1
  77. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/hermes/workspace.harness.json +1 -1
  78. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/chat-memory-sweep.example.json +1 -1
  79. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/memory-care.example.json +1 -1
  80. brigade_cli-0.11.0/src/brigade/templates/openhands/memory-handoffs/TEMPLATE.md +64 -0
  81. brigade_cli-0.11.0/src/brigade/templates/pi/memory-handoffs/TEMPLATE.md +64 -0
  82. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/policies/personal.json +1 -1
  83. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/policies/public-content.json +1 -1
  84. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/policies/public-repo.json +1 -1
  85. brigade_cli-0.11.0/src/brigade/templates/qwen/memory-handoffs/TEMPLATE.md +64 -0
  86. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/toml_compat.py +9 -0
  87. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/tools_cmd.py +31 -5
  88. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/work_cmd/__init__.py +108 -80
  89. brigade_cli-0.11.0/src/brigade/work_cmd/backup.py +174 -0
  90. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/work_cmd/config.py +4 -5
  91. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/work_cmd/helpers.py +3 -12
  92. brigade_cli-0.11.0/src/brigade/work_cmd/imports.py +934 -0
  93. brigade_cli-0.11.0/src/brigade/work_cmd/reviews.py +1328 -0
  94. brigade_cli-0.11.0/src/brigade/work_cmd/scanners.py +1178 -0
  95. brigade_cli-0.11.0/src/brigade/work_cmd/services.py +1105 -0
  96. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/work_cmd/session.py +20 -23
  97. brigade_cli-0.11.0/src/brigade/work_cmd/sweeps.py +771 -0
  98. brigade_cli-0.11.0/src/brigade/work_cmd/verification.py +724 -0
  99. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/PKG-INFO +19 -3
  100. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/SOURCES.txt +74 -4
  101. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_aboyeur.py +177 -0
  102. brigade_cli-0.11.0/tests/test_actionqueue.py +143 -0
  103. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_agents.py +76 -0
  104. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_dogfood_cmd.py +3 -2
  105. brigade_cli-0.11.0/tests/test_friction_cmd.py +117 -0
  106. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_ingest.py +71 -0
  107. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_install.py +3 -1
  108. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_operator_cmd.py +4 -4
  109. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_prompt.py +4 -4
  110. brigade_cli-0.11.0/tests/test_reportstore.py +150 -0
  111. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_cmd.py +20 -0
  112. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_llm.py +15 -1
  113. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_roster.py +43 -0
  114. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_roster_cmd.py +7 -0
  115. brigade_cli-0.11.0/tests/test_run_cli.py +688 -0
  116. brigade_cli-0.11.0/tests/test_runguard.py +141 -0
  117. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_selection.py +19 -1
  118. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_skills_cmd.py +3 -0
  119. brigade_cli-0.11.0/tests/test_work_cmd_backup.py +448 -0
  120. brigade_cli-0.11.0/tests/test_work_cmd_imports.py +3170 -0
  121. brigade_cli-0.11.0/tests/test_work_cmd_ledger.py +1055 -0
  122. brigade_cli-0.11.0/tests/test_work_cmd_reviews.py +727 -0
  123. brigade_cli-0.11.0/tests/test_work_cmd_scanners.py +719 -0
  124. brigade_cli-0.11.0/tests/test_work_cmd_services.py +2699 -0
  125. brigade_cli-0.11.0/tests/test_work_cmd_session.py +1699 -0
  126. brigade_cli-0.11.0/tests/test_work_cmd_sweeps.py +699 -0
  127. brigade_cli-0.11.0/tests/test_work_cmd_verification.py +259 -0
  128. brigade_cli-0.10.3/src/brigade/cli.py +0 -7063
  129. brigade_cli-0.10.3/src/brigade/operator_cmd.py +0 -3317
  130. brigade_cli-0.10.3/src/brigade/work_cmd/services.py +0 -6127
  131. brigade_cli-0.10.3/tests/test_run_cli.py +0 -244
  132. brigade_cli-0.10.3/tests/test_work_cmd.py +0 -11652
  133. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/LICENSE +0 -0
  134. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/MANIFEST.in +0 -0
  135. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/setup.cfg +0 -0
  136. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/__main__.py +0 -0
  137. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/actionqueue.py +0 -0
  138. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/add.py +0 -0
  139. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/budgets.py +0 -0
  140. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/budgets_cmd.py +0 -0
  141. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/chat_cmd.py +0 -0
  142. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/config.py +0 -0
  143. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/context_cmd.py +0 -0
  144. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/daily_cmd.py +0 -0
  145. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/fragments.py +0 -0
  146. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/handoff.py +0 -0
  147. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/handoff_cmd.py +0 -0
  148. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/hermes_adapter.py +0 -0
  149. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/ingest.py +0 -0
  150. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/managed.py +0 -0
  151. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/notifications_cmd.py +0 -0
  152. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/pantry_cmd.py +0 -0
  153. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/phases_cmd.py +0 -0
  154. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/projects_cmd.py +0 -0
  155. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/py.typed +0 -0
  156. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/reconfigure.py +0 -0
  157. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/registry.py +0 -0
  158. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/release_cmd.py +0 -0
  159. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/reportstore.py +0 -0
  160. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/repos_cmd.py +0 -0
  161. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/__init__.py +0 -0
  162. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/config.py +0 -0
  163. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/engine.py +0 -0
  164. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/extract.py +0 -0
  165. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/handoff.py +0 -0
  166. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/registry.py +0 -0
  167. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/report.py +0 -0
  168. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/sources/__init__.py +0 -0
  169. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/sources/cli.py +0 -0
  170. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/sources/local.py +0 -0
  171. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/sources/web.py +0 -0
  172. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/research/types.py +0 -0
  173. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/roadmap_cmd.py +0 -0
  174. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/runbook_cmd.py +0 -0
  175. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/runs_cmd.py +0 -0
  176. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/scrub.py +0 -0
  177. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/station.py +0 -0
  178. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/status.py +0 -0
  179. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/adal/memory-handoffs/TEMPLATE.md +0 -0
  180. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/aider/memory-handoffs/TEMPLATE.md +0 -0
  181. {brigade_cli-0.10.3/src/brigade/templates/antigravity → brigade_cli-0.11.0/src/brigade/templates/amp}/memory-handoffs/TEMPLATE.md +0 -0
  182. {brigade_cli-0.10.3/src/brigade/templates/claude → brigade_cli-0.11.0/src/brigade/templates/antigravity}/memory-handoffs/TEMPLATE.md +0 -0
  183. {brigade_cli-0.10.3/src/brigade/templates/codex → brigade_cli-0.11.0/src/brigade/templates/claude}/memory-handoffs/TEMPLATE.md +0 -0
  184. {brigade_cli-0.10.3/src/brigade/templates/continue → brigade_cli-0.11.0/src/brigade/templates/codex}/memory-handoffs/TEMPLATE.md +0 -0
  185. {brigade_cli-0.10.3/src/brigade/templates/copilot → brigade_cli-0.11.0/src/brigade/templates/continue}/memory-handoffs/TEMPLATE.md +0 -0
  186. {brigade_cli-0.10.3/src/brigade/templates/cursor → brigade_cli-0.11.0/src/brigade/templates/copilot}/memory-handoffs/TEMPLATE.md +0 -0
  187. {brigade_cli-0.10.3/src/brigade/templates/goose → brigade_cli-0.11.0/src/brigade/templates/crush}/memory-handoffs/TEMPLATE.md +0 -0
  188. {brigade_cli-0.10.3/src/brigade/templates/hermes → brigade_cli-0.11.0/src/brigade/templates/cursor}/memory-handoffs/TEMPLATE.md +0 -0
  189. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/depth/repo.json +0 -0
  190. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/depth/workspace.json +0 -0
  191. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/generic/harness-adapter-checklist.md +0 -0
  192. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/generic/memory-contract.md +0 -0
  193. {brigade_cli-0.10.3/src/brigade/templates/kimi → brigade_cli-0.11.0/src/brigade/templates/goose}/memory-handoffs/TEMPLATE.md +0 -0
  194. {brigade_cli-0.10.3/src/brigade/templates/opencode → brigade_cli-0.11.0/src/brigade/templates/grok}/memory-handoffs/TEMPLATE.md +0 -0
  195. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/handoff/handoff-sources.example.json +0 -0
  196. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/handoff/openclaw-ingest-receipt.example.json +0 -0
  197. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/adal.json +0 -0
  198. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/aider.json +0 -0
  199. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/antigravity.json +0 -0
  200. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/claude.json +0 -0
  201. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/codex.json +0 -0
  202. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/continue.json +0 -0
  203. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/copilot.json +0 -0
  204. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/cursor.json +0 -0
  205. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/goose.json +0 -0
  206. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/hermes.json +0 -0
  207. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/kimi.json +0 -0
  208. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/openclaw.json +0 -0
  209. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/opencode.json +0 -0
  210. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/openhands.json +0 -0
  211. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/pi.json +0 -0
  212. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/harnesses/qwen.json +0 -0
  213. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/hermes/README.md +0 -0
  214. {brigade_cli-0.10.3/src/brigade/templates/openhands → brigade_cli-0.11.0/src/brigade/templates/hermes}/memory-handoffs/TEMPLATE.md +0 -0
  215. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/hooks/pre-push +0 -0
  216. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/includes/publisher.json +0 -0
  217. {brigade_cli-0.10.3/src/brigade/templates/pi → brigade_cli-0.11.0/src/brigade/templates/kimi}/memory-handoffs/TEMPLATE.md +0 -0
  218. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/backup-restic.md +0 -0
  219. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/chat-surface-crawlers.md +0 -0
  220. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/content-safety.md +0 -0
  221. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/handoff-flow.md +0 -0
  222. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/memory-architecture.md +0 -0
  223. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/memory-care-staleness.md +0 -0
  224. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/memory-scanner.md +0 -0
  225. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/multi-workspace-handoff-admin.md +0 -0
  226. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/obsidian-notes.md +0 -0
  227. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/pipeline-standups.md +0 -0
  228. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/memory/cards/tokenjuice-output-compaction.md +0 -0
  229. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/README.md +0 -0
  230. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/acp-escalation.openclaw.json +0 -0
  231. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/memory-sweep-cron.openclaw.json +0 -0
  232. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/model-aliases.openclaw.json +0 -0
  233. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/openclaw/ollama-memory-search.openclaw.json +0 -0
  234. {brigade_cli-0.10.3/src/brigade/templates/qwen → brigade_cli-0.11.0/src/brigade/templates/opencode}/memory-handoffs/TEMPLATE.md +0 -0
  235. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/scripts/backup-restic.sh +0 -0
  236. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/skills/note/SKILL.md +0 -0
  237. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/AGENTS.md +0 -0
  238. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/CLAUDE.md +0 -0
  239. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/HEARTBEAT.md +0 -0
  240. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/IDENTITY.md +0 -0
  241. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/INSTALL_FOR_AGENTS.md +0 -0
  242. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/MEMORY.md +0 -0
  243. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/SAFETY_RULES.md +0 -0
  244. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/SOUL.md +0 -0
  245. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/TOOLS.md +0 -0
  246. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/USER.md +0 -0
  247. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/rules/acceptance-driven-work.md +0 -0
  248. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates/workspace/rules/issue-tdd-loop.md +0 -0
  249. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/templates.py +0 -0
  250. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/untrusted.py +0 -0
  251. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/untrusted_cmd.py +0 -0
  252. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/work_cmd/constants.py +0 -0
  253. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade/work_cmd/ledger.py +0 -0
  254. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/dependency_links.txt +0 -0
  255. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/entry_points.txt +0 -0
  256. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/requires.txt +0 -0
  257. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/src/brigade_cli.egg-info/top_level.txt +0 -0
  258. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_add.py +0 -0
  259. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_budgets.py +0 -0
  260. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_cli_alias.py +0 -0
  261. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_cli_help.py +0 -0
  262. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_config.py +0 -0
  263. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_doctor.py +0 -0
  264. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_fragments.py +0 -0
  265. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_gitignore.py +0 -0
  266. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_handoff.py +0 -0
  267. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_handoff_cmd.py +0 -0
  268. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_init.py +0 -0
  269. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_managed.py +0 -0
  270. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_memory_cmd.py +0 -0
  271. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_neutrality.py +0 -0
  272. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_notifications_cmd.py +0 -0
  273. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_pantry_cmd.py +0 -0
  274. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase100_cmd.py +0 -0
  275. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase101_cmd.py +0 -0
  276. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase165_cmd.py +0 -0
  277. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase36_cmd.py +0 -0
  278. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase37_cmd.py +0 -0
  279. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase38_cmd.py +0 -0
  280. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase39_cmd.py +0 -0
  281. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase40_cmd.py +0 -0
  282. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase41_cmd.py +0 -0
  283. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase42_cmd.py +0 -0
  284. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase43_cmd.py +0 -0
  285. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase44_cmd.py +0 -0
  286. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase45_cmd.py +0 -0
  287. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase46_50_cmd.py +0 -0
  288. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase51_55_cmd.py +0 -0
  289. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase56_60_cmd.py +0 -0
  290. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase96_cmd.py +0 -0
  291. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase97_cmd.py +0 -0
  292. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase98_cmd.py +0 -0
  293. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_phase99_cmd.py +0 -0
  294. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_privacy_regression.py +0 -0
  295. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_proc.py +0 -0
  296. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_reconfigure.py +0 -0
  297. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_registry.py +0 -0
  298. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_release_cmd.py +0 -0
  299. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_repos_cmd.py +0 -0
  300. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_cli_sources.py +0 -0
  301. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_config.py +0 -0
  302. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_engine.py +0 -0
  303. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_extract.py +0 -0
  304. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_handoff.py +0 -0
  305. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_local_sources.py +0 -0
  306. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_registry.py +0 -0
  307. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_report.py +0 -0
  308. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_types.py +0 -0
  309. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_research_web.py +0 -0
  310. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_roadmap_cmd.py +0 -0
  311. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_runbook_cmd.py +0 -0
  312. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_runs_cmd.py +0 -0
  313. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_scrub.py +0 -0
  314. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_security_cmd.py +0 -0
  315. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_station.py +0 -0
  316. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_status.py +0 -0
  317. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_toml_compat.py +0 -0
  318. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_untrusted.py +0 -0
  319. {brigade_cli-0.10.3 → brigade_cli-0.11.0}/tests/test_untrusted_cmd.py +0 -0
  320. {brigade_cli-0.10.3 → 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.10.3
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 <srneas@gmail.com>
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
@@ -35,7 +35,7 @@ Dynamic: license-file
35
35
  <h1 align="center">Brigade CLI</h1>
36
36
 
37
37
  <p align="center">
38
- <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>
39
39
  </p>
40
40
 
41
41
  <p align="center">
@@ -47,6 +47,16 @@ Dynamic: license-file
47
47
 
48
48
  Your agents run loops. Brigade keeps the receipts.
49
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
+
50
60
  ## Why I built this
51
61
 
52
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.
@@ -140,6 +150,9 @@ Each writer gets its own local inbox; one canonical owner ingests. Brigade keeps
140
150
  | Kimi Code | `kimi` | `.kimi/memory-handoffs/` |
141
151
  | AdaL | `adal` | `.adal/memory-handoffs/` |
142
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/` |
143
156
  | Hermes | `hermes` | `.hermes/memory-handoffs/` |
144
157
  | OpenClaw | `openclaw` | usually the memory owner, not a writer |
145
158
 
@@ -150,9 +163,11 @@ All of them get handoff templates, ingest source coverage, and projected tools/s
150
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:
151
164
 
152
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.
153
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.
154
168
  - **Tools and skills**: one reviewed catalog projected into every harness's native format, with approval gates for anything that executes.
155
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.
156
171
  - **Fleet and release**: health evidence across your local repos and release-readiness receipts, with no publish step.
157
172
 
158
173
  The full tour of every station lives in [docs/overview.md](docs/overview.md).
@@ -188,6 +203,7 @@ That pause is the point. Agent memory should be useful, not noisy.
188
203
  - [Handoff promotion](docs/handoff-promotion.md): how notes move toward memory.
189
204
  - [Repo fleet](docs/repo-fleet.md) and [Tool catalog](docs/tool-catalog.md).
190
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).
191
207
  - [Roadmap](ROADMAP.md) and [roadmap archive](docs/roadmap-archive.md).
192
208
 
193
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 clone to a working agent kitchen.
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 fastest path is to run `brigade init` with no flags and answer the prompts:
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
@@ -43,8 +55,11 @@ Which harnesses do you use? (type numbers separated by space/comma to toggle, en
43
55
  [ ] 12. Kimi Code
44
56
  [ ] 13. AdaL
45
57
  [ ] 14. OpenHands
46
- [ ] 15. OpenClaw
47
- [ ] 16. Hermes (experimental)
58
+ [ ] 15. Grok CLI
59
+ [ ] 16. Amp
60
+ [ ] 17. Crush
61
+ [ ] 18. OpenClaw
62
+ [ ] 19. Hermes (experimental)
48
63
 
49
64
  Depth? (type a number, enter for default)
50
65
  * 1. repo (handoff flow + publish guard)
@@ -58,24 +73,30 @@ Defaults are claude harness, repo depth, no includes. Enter ships the install.
58
73
 
59
74
  ## CI / scripted install
60
75
 
61
- Pass flags directly to skip the prompt:
76
+ Pass flags directly to skip the prompt. The same flags work on `operator quickstart`:
62
77
 
63
78
  ```bash
64
79
  # Claude Code + Codex + OpenClaw, full workspace
65
- brigade init --target ~/agent-kitchen \
80
+ brigade operator quickstart --target ~/agent-kitchen \
66
81
  --depth workspace \
67
82
  --harnesses claude,codex,openclaw
68
83
 
69
84
  # Codex-only project, minimal install
70
- brigade init --target ./my-project --depth repo --harnesses codex
85
+ brigade operator quickstart --target ./my-project --depth repo --harnesses codex
71
86
 
72
- # Generic layout, no harness-specific files
87
+ # Template files only, no harness-specific files
73
88
  brigade init --target ./my-project --harnesses none
74
89
  ```
75
90
 
76
91
  ## Verifying
77
92
 
78
- After install, `brigade doctor --target <path>` reports the apparent harness shape and checks every configured inbox and adapter:
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:
79
100
 
80
101
  ```
81
102
  brigade doctor: target /home/you/agent-kitchen
@@ -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.
@@ -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.10.3"
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 = "srneas@gmail.com" }] # content-guard: allow 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",
@@ -53,8 +53,8 @@ line-length = 120
53
53
  src = ["src", "tests"]
54
54
 
55
55
  [tool.ruff.lint]
56
- # Pragmatic initial gate: ruff's default rule set (pyflakes plus the
57
- # E4/E7/E9 pycodestyle errors). Deferred for later, separate passes:
58
- # import sorting (I), pyupgrade (UP), bugbear (B), the full pycodestyle
59
- # E/W set, and any type checking (mypy/pyright).
60
- select = ["E4", "E7", "E9", "F"]
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,3 +1,3 @@
1
1
  """Brigade: local operator-system CLI for agent workspaces."""
2
2
 
3
- __version__ = "0.10.3"
3
+ __version__ = "0.11.0"
@@ -20,6 +20,7 @@ from .roster import Agent, Roster, is_cli_allowed, timeout_for, workers
20
20
  class Assignment:
21
21
  worker: str
22
22
  task: str
23
+ stage: int = 1
23
24
 
24
25
 
25
26
  @dataclass(frozen=True)
@@ -46,11 +47,14 @@ def build_plan_prompt(
46
47
  return (
47
48
  "You are the Brigade aboyeur. Split the user's task across the available workers.\n"
48
49
  "Return exactly one JSON object, with no prose outside JSON:\n"
49
- '{"assignments":[{"worker":"<worker-name>","task":"<specific sub-task>"}]}\n'
50
+ '{"assignments":[{"stage":1,"worker":"<worker-name>","task":"<specific sub-task>"}]}\n'
50
51
  f"{note}\n"
51
52
  f"User task:\n{task}\n\n"
52
53
  f"Available workers, excluding you:\n{worker_lines}\n\n"
53
- 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"
54
58
  "- Assign only listed workers.\n"
55
59
  "- Use zero assignments only if no worker is useful."
56
60
  f"{policy}"
@@ -147,7 +151,10 @@ def write_run_handoff(
147
151
  or "- no workers dispatched"
148
152
  )
149
153
  assignment_summary = (
150
- "\n".join(f"- {assignment.worker}: {_one_line(assignment.task)}" for assignment in assignments)
154
+ "\n".join(
155
+ f"- stage {assignment.stage} -> {assignment.worker}: {_one_line(assignment.task)}"
156
+ for assignment in assignments
157
+ )
151
158
  or "- no worker assignments"
152
159
  )
153
160
  artifact_line = f"- artifacts: `{output_dir}`" if output_dir is not None else "- artifacts: none"
@@ -232,30 +239,38 @@ def parse_plan(text: str, roster: Roster) -> list[Assignment]:
232
239
  raw_assignments = payload.get("assignments")
233
240
  if not isinstance(raw_assignments, list):
234
241
  raise ValueError("plan JSON needs an assignments list")
235
- if len(raw_assignments) > roster.max_workers:
236
- raise ValueError(f"plan has {len(raw_assignments)} assignments, limit is {roster.max_workers}")
237
242
 
238
243
  assignments: list[Assignment] = []
239
- seen: set[tuple[str, str]] = set()
244
+ seen: set[tuple[int, str, str]] = set()
245
+ stage_counts: dict[int, int] = {}
240
246
  for item in raw_assignments:
241
247
  if not isinstance(item, dict):
242
248
  raise ValueError("each assignment must be an object")
243
- worker = item.get("worker")
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")
244
253
  subtask = item.get("task")
245
- if not isinstance(worker, str) or not worker.strip():
254
+ if not isinstance(raw_worker, str) or not raw_worker.strip():
246
255
  raise ValueError("assignment.worker must be a non-empty string")
256
+ worker = raw_worker.strip()
247
257
  if worker not in roster.agents:
248
258
  raise ValueError(f"assignment references unknown worker: {worker!r}")
249
259
  if worker == roster.orchestrator:
250
260
  raise ValueError("assignment cannot target the orchestrator")
251
261
  if not isinstance(subtask, str) or not subtask.strip():
252
262
  raise ValueError("assignment.task must be a non-empty string")
253
- assignment = Assignment(worker=worker.strip(), task=subtask.strip())
254
- key = (assignment.worker, assignment.task)
263
+ assignment = Assignment(worker=worker, task=subtask.strip(), stage=stage)
264
+ key = (assignment.stage, assignment.worker, assignment.task)
255
265
  if key not in seen:
256
266
  assignments.append(assignment)
257
267
  seen.add(key)
258
- return assignments
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)
259
274
 
260
275
 
261
276
  def _record_plan_attempt(
@@ -302,6 +317,8 @@ def _run_orchestrator(
302
317
  }
303
318
  if sandbox is not None:
304
319
  kwargs["sandbox"] = sandbox
320
+ if orchestrator.model is not None:
321
+ kwargs["model"] = orchestrator.model
305
322
  return agents.run_agent(orchestrator.cli, prompt, **kwargs)
306
323
 
307
324
 
@@ -356,13 +373,39 @@ def plan(
356
373
  raise RuntimeError(f"orchestrator returned an invalid plan: {second_exc}") from second_exc
357
374
 
358
375
 
359
- def _worker_prompt(agent: Agent, assignment: Assignment, read_only: bool = False) -> str:
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)}"
360
402
  policy = f"\n\n{_read_only_rules()}" if read_only else ""
361
403
  return (
362
404
  f"You are Brigade worker {agent.name}.\n"
363
405
  f"Role:\n{agent.role}\n\n"
364
406
  f"Sub-task:\n{assignment.task}\n\n"
365
407
  "Return a concise, complete result for the orchestrator to synthesize."
408
+ f"{prior_context}"
366
409
  f"{policy}"
367
410
  )
368
411
 
@@ -375,7 +418,7 @@ def dispatch(
375
418
  sandbox_read_only: bool | None = None,
376
419
  sandbox: str | None = None,
377
420
  ) -> list[WorkerResult]:
378
- def run_one(assignment: Assignment) -> WorkerResult:
421
+ def run_one(assignment: Assignment, prior_results: list[WorkerResult]) -> WorkerResult:
379
422
  agent = roster.agents[assignment.worker]
380
423
  if not is_cli_allowed(agent.cli, roster):
381
424
  return WorkerResult(
@@ -392,7 +435,13 @@ def dispatch(
392
435
  }
393
436
  if sandbox is not None:
394
437
  kwargs["sandbox"] = sandbox
395
- result = agents.run_agent(agent.cli, _worker_prompt(agent, assignment, read_only=read_only), **kwargs)
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
+ )
396
445
  return WorkerResult(
397
446
  worker=assignment.worker,
398
447
  task=assignment.task,
@@ -404,25 +453,34 @@ def dispatch(
404
453
  if not assignments:
405
454
  return []
406
455
 
407
- results_by_index: dict[int, WorkerResult] = {}
408
- max_workers = min(roster.max_workers, len(assignments))
409
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
410
- future_to_index = {executor.submit(run_one, assignment): index for index, assignment in enumerate(assignments)}
411
- for future in as_completed(future_to_index):
412
- index = future_to_index[future]
413
- try:
414
- results_by_index[index] = future.result()
415
- except Exception as exc: # pragma: no cover - defensive boundary
416
- assignment = assignments[index]
417
- results_by_index[index] = WorkerResult(
418
- worker=assignment.worker,
419
- task=assignment.task,
420
- text="",
421
- ok=False,
422
- detail=str(exc)[:200],
423
- )
424
-
425
- return [results_by_index[index] for index in range(len(assignments))]
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
- print(f" -> {assignment.worker}: {assignment.task}")
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,8 +539,10 @@ 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, str]]:
477
- return [{"worker": assignment.worker, "task": assignment.task} for assignment in assignments]
542
+ def _assignment_payload(assignments: list[Assignment]) -> list[dict[str, object]]:
543
+ return [
544
+ {"stage": assignment.stage, "worker": assignment.worker, "task": assignment.task} for assignment in assignments
545
+ ]
478
546
 
479
547
 
480
548
  def _worker_payload(results: list[WorkerResult]) -> list[dict[str, object]]:
@@ -504,9 +572,11 @@ def _roster_payload(roster: Roster) -> dict[str, object]:
504
572
  "max_workers": roster.max_workers,
505
573
  "timeout_seconds": roster.timeout_seconds,
506
574
  "allow_models": list(roster.allow_models),
575
+ "sandbox": roster.sandbox,
507
576
  "agents": {
508
577
  name: {
509
578
  "cli": agent.cli,
579
+ "model": agent.model,
510
580
  "role": agent.role,
511
581
  "timeout_seconds": agent.timeout_seconds,
512
582
  }
@@ -97,6 +97,21 @@ def _openhands_argv(prompt: str, read_only: bool, sandbox: str | None) -> List[s
97
97
  return ["openhands", "--headless", "-t", task]
98
98
 
99
99
 
100
+ def _grok_argv(prompt: str, read_only: bool, sandbox: str | None) -> List[str]:
101
+ task = _read_only_prompt(prompt) if read_only or sandbox == "read-only" else prompt
102
+ return ["grok", "--prompt", task]
103
+
104
+
105
+ def _amp_argv(prompt: str, read_only: bool, sandbox: str | None) -> List[str]:
106
+ task = _read_only_prompt(prompt) if read_only or sandbox == "read-only" else prompt
107
+ return ["amp", "--prompt", task]
108
+
109
+
110
+ def _crush_argv(prompt: str, read_only: bool, sandbox: str | None) -> List[str]:
111
+ task = _read_only_prompt(prompt) if read_only or sandbox == "read-only" else prompt
112
+ return ["crush", "--prompt", task]
113
+
114
+
100
115
  _ADAPTERS: dict[str, Callable[[str, bool, str | None], List[str]]] = {
101
116
  "claude": _claude_argv,
102
117
  "codex": _codex_argv,
@@ -112,6 +127,9 @@ _ADAPTERS: dict[str, Callable[[str, bool, str | None], List[str]]] = {
112
127
  "kimi": _kimi_argv,
113
128
  "adal": _adal_argv,
114
129
  "openhands": _openhands_argv,
130
+ "grok": _grok_argv,
131
+ "amp": _amp_argv,
132
+ "crush": _crush_argv,
115
133
  }
116
134
 
117
135
 
@@ -138,26 +156,42 @@ def command_for(cli_ref: str) -> str:
138
156
  return cli_ref
139
157
 
140
158
 
159
+ def _with_model(cli_ref: str, argv: List[str], model: str) -> List[str]:
160
+ if cli_ref == "claude":
161
+ # claude --model <id> -p <prompt>
162
+ return [argv[0], "--model", model, *argv[1:]]
163
+ if cli_ref == "codex":
164
+ # codex exec [--sandbox <mode>] -m <id> <prompt>
165
+ return [*argv[:-1], "-m", model, argv[-1]]
166
+ raise ValueError(f"{cli_ref!r} does not support model pinning (supported: claude, codex)")
167
+
168
+
141
169
  def build_argv(
142
170
  cli_ref: str,
143
171
  prompt: str,
144
172
  read_only: bool = False,
145
173
  sandbox: str | None = None,
174
+ model: str | None = None,
146
175
  ) -> List[str]:
147
176
  if cli_ref.startswith(_OLLAMA_PREFIX):
148
- model = cli_ref[len(_OLLAMA_PREFIX) :]
149
- if not model:
177
+ ollama_model = cli_ref[len(_OLLAMA_PREFIX) :]
178
+ if not ollama_model:
150
179
  raise ValueError(f"ollama reference needs a model: {cli_ref!r}")
151
- return ["ollama", "run", model, prompt]
180
+ if model is not None:
181
+ raise ValueError(f"{cli_ref!r} already names a model; drop the separate model setting")
182
+ return ["ollama", "run", ollama_model, prompt]
152
183
 
153
184
  builder = _ADAPTERS.get(cli_ref)
154
185
  if builder is None:
155
186
  raise ValueError(
156
187
  f"unknown agent cli: {cli_ref!r} "
157
188
  "(known: claude, codex, opencode, antigravity, pi, cursor, aider, goose, continue, "
158
- "copilot, qwen, kimi, adal, openhands, ollama:<model>)"
189
+ "copilot, qwen, kimi, adal, openhands, grok, amp, crush, ollama:<model>)"
159
190
  )
160
- return builder(prompt, read_only, sandbox)
191
+ argv = builder(prompt, read_only, sandbox)
192
+ if model is not None:
193
+ argv = _with_model(cli_ref, argv, model)
194
+ return argv
161
195
 
162
196
 
163
197
  def detect(cli_ref: str) -> bool:
@@ -171,11 +205,16 @@ def run_agent(
171
205
  cwd: Path | None = None,
172
206
  read_only: bool = False,
173
207
  sandbox: str | None = None,
208
+ model: str | None = None,
174
209
  ) -> AgentResult:
175
210
  if not detect(cli_ref):
176
211
  return AgentResult(text="", ok=False, detail=f"{command_for(cli_ref)} not installed")
177
212
 
178
- result = proc.run(build_argv(cli_ref, prompt, read_only=read_only, sandbox=sandbox), timeout=timeout, cwd=cwd)
213
+ result = proc.run(
214
+ build_argv(cli_ref, prompt, read_only=read_only, sandbox=sandbox, model=model),
215
+ timeout=timeout,
216
+ cwd=cwd,
217
+ )
179
218
  text = result.stdout.strip()
180
219
  if result.code != 0:
181
220
  detail = result.stderr.strip() or f"exit {result.code}"
@@ -34,6 +34,7 @@ from . import (
34
34
  work_cmd,
35
35
  )
36
36
  from .localio import (
37
+ parse_iso_datetime,
37
38
  read_json_dict as _read_json,
38
39
  read_jsonl_dicts as _read_jsonl,
39
40
  utc_now as _now,
@@ -2632,7 +2633,7 @@ def report_review(*, target: Path, report_id: str = "latest", json_output: bool
2632
2633
  def _receipt_newer_than_report(receipt: dict[str, Any] | None, report_created: datetime | None) -> bool:
2633
2634
  if receipt is None or report_created is None:
2634
2635
  return False
2635
- stamp = work_cmd._parse_iso_datetime(
2636
+ stamp = parse_iso_datetime(
2636
2637
  receipt.get("completed_at")
2637
2638
  or receipt.get("created_at")
2638
2639
  or receipt.get("started_at")
@@ -2850,7 +2851,7 @@ def report_compare(*, target: Path, report_id: str = "latest", json_output: bool
2850
2851
  ):
2851
2852
  if _receipt_newer_than_report(receipt if isinstance(receipt, dict) else None, report_created):
2852
2853
  issues.append({"status": "warn", "name": name, "detail": str((receipt or {}).get(key))})
2853
- security_generated = work_cmd._parse_iso_datetime(
2854
+ security_generated = parse_iso_datetime(
2854
2855
  (latest_security or {}).get("generated_at") if isinstance(latest_security, dict) else None
2855
2856
  )
2856
2857
  if report_created and security_generated and security_generated > report_created: