strands-compose 0.1.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 (242) hide show
  1. strands_compose-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +124 -0
  2. strands_compose-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. strands_compose-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
  4. strands_compose-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  5. strands_compose-0.1.0/.github/agents/developer.md +38 -0
  6. strands_compose-0.1.0/.github/agents/docs-writer.md +48 -0
  7. strands_compose-0.1.0/.github/agents/reviewer.md +46 -0
  8. strands_compose-0.1.0/.github/agents/tester.md +37 -0
  9. strands_compose-0.1.0/.github/copilot-instructions.md +176 -0
  10. strands_compose-0.1.0/.github/dependabot.yml +20 -0
  11. strands_compose-0.1.0/.github/workflows/ci.yml +76 -0
  12. strands_compose-0.1.0/.github/workflows/publish.yml +126 -0
  13. strands_compose-0.1.0/.gitignore +57 -0
  14. strands_compose-0.1.0/.pre-commit-config.yaml +48 -0
  15. strands_compose-0.1.0/.secrets.baseline +111 -0
  16. strands_compose-0.1.0/AGENTS.md +176 -0
  17. strands_compose-0.1.0/CHANGELOG.md +26 -0
  18. strands_compose-0.1.0/CLAUDE.md +3 -0
  19. strands_compose-0.1.0/CONTRIBUTING.md +115 -0
  20. strands_compose-0.1.0/GEMINI.md +3 -0
  21. strands_compose-0.1.0/LICENSE +174 -0
  22. strands_compose-0.1.0/PKG-INFO +445 -0
  23. strands_compose-0.1.0/README.md +408 -0
  24. strands_compose-0.1.0/RELEASING.md +50 -0
  25. strands_compose-0.1.0/SECURITY.md +31 -0
  26. strands_compose-0.1.0/SUPPORT.md +34 -0
  27. strands_compose-0.1.0/docs/configuration/Chapter_01.md +71 -0
  28. strands_compose-0.1.0/docs/configuration/Chapter_02.md +106 -0
  29. strands_compose-0.1.0/docs/configuration/Chapter_03.md +117 -0
  30. strands_compose-0.1.0/docs/configuration/Chapter_04.md +115 -0
  31. strands_compose-0.1.0/docs/configuration/Chapter_05.md +107 -0
  32. strands_compose-0.1.0/docs/configuration/Chapter_06.md +180 -0
  33. strands_compose-0.1.0/docs/configuration/Chapter_07.md +162 -0
  34. strands_compose-0.1.0/docs/configuration/Chapter_08.md +71 -0
  35. strands_compose-0.1.0/docs/configuration/Chapter_09.md +195 -0
  36. strands_compose-0.1.0/docs/configuration/Chapter_10.md +170 -0
  37. strands_compose-0.1.0/docs/configuration/Chapter_11.md +67 -0
  38. strands_compose-0.1.0/docs/configuration/Chapter_12.md +118 -0
  39. strands_compose-0.1.0/docs/configuration/Chapter_13.md +110 -0
  40. strands_compose-0.1.0/docs/configuration/Chapter_14.md +97 -0
  41. strands_compose-0.1.0/docs/configuration/Chapter_15.md +76 -0
  42. strands_compose-0.1.0/docs/configuration/Chapter_16.md +70 -0
  43. strands_compose-0.1.0/docs/configuration/Chapter_17.md +199 -0
  44. strands_compose-0.1.0/docs/configuration/Chapter_18.md +155 -0
  45. strands_compose-0.1.0/docs/configuration/Quick_Recipes.md +113 -0
  46. strands_compose-0.1.0/docs/configuration/README.md +38 -0
  47. strands_compose-0.1.0/docs/img/logo-black.svg +213 -0
  48. strands_compose-0.1.0/docs/img/logo-white.svg +213 -0
  49. strands_compose-0.1.0/docs/img/logo.png +0 -0
  50. strands_compose-0.1.0/docs/index.html +67 -0
  51. strands_compose-0.1.0/examples/01_minimal/README.md +67 -0
  52. strands_compose-0.1.0/examples/01_minimal/config.yaml +22 -0
  53. strands_compose-0.1.0/examples/01_minimal/main.py +48 -0
  54. strands_compose-0.1.0/examples/02_vars_and_anchors/README.md +104 -0
  55. strands_compose-0.1.0/examples/02_vars_and_anchors/config.yaml +48 -0
  56. strands_compose-0.1.0/examples/02_vars_and_anchors/main.py +60 -0
  57. strands_compose-0.1.0/examples/03_tools/README.md +78 -0
  58. strands_compose-0.1.0/examples/03_tools/config.yaml +35 -0
  59. strands_compose-0.1.0/examples/03_tools/main.py +49 -0
  60. strands_compose-0.1.0/examples/03_tools/tools.py +48 -0
  61. strands_compose-0.1.0/examples/04_session/README.md +144 -0
  62. strands_compose-0.1.0/examples/04_session/config.yaml +32 -0
  63. strands_compose-0.1.0/examples/04_session/main.py +48 -0
  64. strands_compose-0.1.0/examples/05_hooks/README.md +84 -0
  65. strands_compose-0.1.0/examples/05_hooks/config.yaml +40 -0
  66. strands_compose-0.1.0/examples/05_hooks/custom_tools.py +50 -0
  67. strands_compose-0.1.0/examples/05_hooks/hooks.py +36 -0
  68. strands_compose-0.1.0/examples/05_hooks/main.py +51 -0
  69. strands_compose-0.1.0/examples/06_mcp/README.md +126 -0
  70. strands_compose-0.1.0/examples/06_mcp/config.yaml +68 -0
  71. strands_compose-0.1.0/examples/06_mcp/main.py +50 -0
  72. strands_compose-0.1.0/examples/06_mcp/server.py +75 -0
  73. strands_compose-0.1.0/examples/07_delegate/README.md +86 -0
  74. strands_compose-0.1.0/examples/07_delegate/config.yaml +57 -0
  75. strands_compose-0.1.0/examples/07_delegate/main.py +48 -0
  76. strands_compose-0.1.0/examples/08_swarm/README.md +75 -0
  77. strands_compose-0.1.0/examples/08_swarm/config.yaml +52 -0
  78. strands_compose-0.1.0/examples/08_swarm/main.py +48 -0
  79. strands_compose-0.1.0/examples/09_graph/README.md +76 -0
  80. strands_compose-0.1.0/examples/09_graph/config.yaml +55 -0
  81. strands_compose-0.1.0/examples/09_graph/main.py +48 -0
  82. strands_compose-0.1.0/examples/10_nested/README.md +85 -0
  83. strands_compose-0.1.0/examples/10_nested/config.yaml +105 -0
  84. strands_compose-0.1.0/examples/10_nested/main.py +49 -0
  85. strands_compose-0.1.0/examples/11_multi_file_config/README.md +88 -0
  86. strands_compose-0.1.0/examples/11_multi_file_config/agents.yaml +18 -0
  87. strands_compose-0.1.0/examples/11_multi_file_config/base.yaml +19 -0
  88. strands_compose-0.1.0/examples/11_multi_file_config/main.py +51 -0
  89. strands_compose-0.1.0/examples/12_streaming/README.md +84 -0
  90. strands_compose-0.1.0/examples/12_streaming/config.yaml +59 -0
  91. strands_compose-0.1.0/examples/12_streaming/main.py +74 -0
  92. strands_compose-0.1.0/examples/13_graph_conditions/README.md +90 -0
  93. strands_compose-0.1.0/examples/13_graph_conditions/conditions.py +33 -0
  94. strands_compose-0.1.0/examples/13_graph_conditions/config.yaml +55 -0
  95. strands_compose-0.1.0/examples/13_graph_conditions/main.py +49 -0
  96. strands_compose-0.1.0/examples/14_agent_factory/README.md +75 -0
  97. strands_compose-0.1.0/examples/14_agent_factory/config.yaml +23 -0
  98. strands_compose-0.1.0/examples/14_agent_factory/factory.py +53 -0
  99. strands_compose-0.1.0/examples/14_agent_factory/main.py +48 -0
  100. strands_compose-0.1.0/examples/README.md +36 -0
  101. strands_compose-0.1.0/examples/TEMPLATE_EXAMPLE.md +62 -0
  102. strands_compose-0.1.0/justfile +29 -0
  103. strands_compose-0.1.0/pyproject.toml +130 -0
  104. strands_compose-0.1.0/src/strands_compose/__init__.py +64 -0
  105. strands_compose-0.1.0/src/strands_compose/cli.py +427 -0
  106. strands_compose-0.1.0/src/strands_compose/config/__init__.py +53 -0
  107. strands_compose-0.1.0/src/strands_compose/config/interpolation.py +196 -0
  108. strands_compose-0.1.0/src/strands_compose/config/loaders/__init__.py +12 -0
  109. strands_compose-0.1.0/src/strands_compose/config/loaders/helpers.py +406 -0
  110. strands_compose-0.1.0/src/strands_compose/config/loaders/loaders.py +270 -0
  111. strands_compose-0.1.0/src/strands_compose/config/loaders/validators.py +124 -0
  112. strands_compose-0.1.0/src/strands_compose/config/resolvers/__init__.py +28 -0
  113. strands_compose-0.1.0/src/strands_compose/config/resolvers/agents.py +203 -0
  114. strands_compose-0.1.0/src/strands_compose/config/resolvers/config.py +166 -0
  115. strands_compose-0.1.0/src/strands_compose/config/resolvers/conversation_manager.py +54 -0
  116. strands_compose-0.1.0/src/strands_compose/config/resolvers/hooks.py +68 -0
  117. strands_compose-0.1.0/src/strands_compose/config/resolvers/mcp.py +106 -0
  118. strands_compose-0.1.0/src/strands_compose/config/resolvers/models.py +47 -0
  119. strands_compose-0.1.0/src/strands_compose/config/resolvers/orchestrations/__init__.py +80 -0
  120. strands_compose-0.1.0/src/strands_compose/config/resolvers/orchestrations/builders.py +354 -0
  121. strands_compose-0.1.0/src/strands_compose/config/resolvers/orchestrations/planner.py +102 -0
  122. strands_compose-0.1.0/src/strands_compose/config/resolvers/session_manager.py +149 -0
  123. strands_compose-0.1.0/src/strands_compose/config/schema.py +337 -0
  124. strands_compose-0.1.0/src/strands_compose/converters/__init__.py +13 -0
  125. strands_compose-0.1.0/src/strands_compose/converters/base.py +54 -0
  126. strands_compose-0.1.0/src/strands_compose/converters/openai.py +221 -0
  127. strands_compose-0.1.0/src/strands_compose/converters/raw.py +31 -0
  128. strands_compose-0.1.0/src/strands_compose/exceptions.py +40 -0
  129. strands_compose-0.1.0/src/strands_compose/hooks/__init__.py +17 -0
  130. strands_compose-0.1.0/src/strands_compose/hooks/event_publisher.py +378 -0
  131. strands_compose-0.1.0/src/strands_compose/hooks/max_calls_guard.py +90 -0
  132. strands_compose-0.1.0/src/strands_compose/hooks/stop_guard.py +113 -0
  133. strands_compose-0.1.0/src/strands_compose/hooks/tool_name_sanitizer.py +184 -0
  134. strands_compose-0.1.0/src/strands_compose/mcp/README.md +105 -0
  135. strands_compose-0.1.0/src/strands_compose/mcp/__init__.py +27 -0
  136. strands_compose-0.1.0/src/strands_compose/mcp/client.py +170 -0
  137. strands_compose-0.1.0/src/strands_compose/mcp/lifecycle.py +233 -0
  138. strands_compose-0.1.0/src/strands_compose/mcp/server.py +327 -0
  139. strands_compose-0.1.0/src/strands_compose/mcp/transports.py +188 -0
  140. strands_compose-0.1.0/src/strands_compose/models.py +69 -0
  141. strands_compose-0.1.0/src/strands_compose/py.typed +0 -0
  142. strands_compose-0.1.0/src/strands_compose/renderers/__init__.py +9 -0
  143. strands_compose-0.1.0/src/strands_compose/renderers/ansi.py +237 -0
  144. strands_compose-0.1.0/src/strands_compose/renderers/base.py +36 -0
  145. strands_compose-0.1.0/src/strands_compose/startup/__init__.py +24 -0
  146. strands_compose-0.1.0/src/strands_compose/startup/report.py +174 -0
  147. strands_compose-0.1.0/src/strands_compose/startup/validator.py +167 -0
  148. strands_compose-0.1.0/src/strands_compose/tools/__init__.py +33 -0
  149. strands_compose-0.1.0/src/strands_compose/tools/extractors.py +182 -0
  150. strands_compose-0.1.0/src/strands_compose/tools/loaders.py +238 -0
  151. strands_compose-0.1.0/src/strands_compose/tools/wrappers.py +100 -0
  152. strands_compose-0.1.0/src/strands_compose/types.py +110 -0
  153. strands_compose-0.1.0/src/strands_compose/utils.py +210 -0
  154. strands_compose-0.1.0/src/strands_compose/wire.py +196 -0
  155. strands_compose-0.1.0/tasks/README.md +152 -0
  156. strands_compose-0.1.0/tasks/check.just +30 -0
  157. strands_compose-0.1.0/tasks/clean.just +55 -0
  158. strands_compose-0.1.0/tasks/commit.just +19 -0
  159. strands_compose-0.1.0/tasks/format.just +13 -0
  160. strands_compose-0.1.0/tasks/install.just +15 -0
  161. strands_compose-0.1.0/tasks/release.just +39 -0
  162. strands_compose-0.1.0/tasks/test.just +13 -0
  163. strands_compose-0.1.0/tests/__init__.py +1 -0
  164. strands_compose-0.1.0/tests/conftest.py +10 -0
  165. strands_compose-0.1.0/tests/examples/test_examples_smoke.py +103 -0
  166. strands_compose-0.1.0/tests/integration/__init__.py +0 -0
  167. strands_compose-0.1.0/tests/integration/conftest.py +28 -0
  168. strands_compose-0.1.0/tests/integration/fixtures/complex_full.yaml +50 -0
  169. strands_compose-0.1.0/tests/integration/fixtures/graph.yaml +17 -0
  170. strands_compose-0.1.0/tests/integration/fixtures/minimal.yaml +4 -0
  171. strands_compose-0.1.0/tests/integration/fixtures/multi_agent_delegate.yaml +13 -0
  172. strands_compose-0.1.0/tests/integration/fixtures/nested_orchestration.yaml +23 -0
  173. strands_compose-0.1.0/tests/integration/fixtures/swarm.yaml +12 -0
  174. strands_compose-0.1.0/tests/integration/fixtures/with_hooks.yaml +8 -0
  175. strands_compose-0.1.0/tests/integration/fixtures/with_model.yaml +9 -0
  176. strands_compose-0.1.0/tests/integration/fixtures/with_session_manager.yaml +8 -0
  177. strands_compose-0.1.0/tests/integration/fixtures/with_vars.yaml +12 -0
  178. strands_compose-0.1.0/tests/integration/test_full_pipeline.py +90 -0
  179. strands_compose-0.1.0/tests/integration/test_load_config.py +301 -0
  180. strands_compose-0.1.0/tests/unit/__init__.py +1 -0
  181. strands_compose-0.1.0/tests/unit/config/__init__.py +0 -0
  182. strands_compose-0.1.0/tests/unit/config/loaders/__init__.py +0 -0
  183. strands_compose-0.1.0/tests/unit/config/loaders/conftest.py +131 -0
  184. strands_compose-0.1.0/tests/unit/config/loaders/test_helpers.py +219 -0
  185. strands_compose-0.1.0/tests/unit/config/loaders/test_helpers_extended.py +348 -0
  186. strands_compose-0.1.0/tests/unit/config/loaders/test_load_session.py +174 -0
  187. strands_compose-0.1.0/tests/unit/config/loaders/test_loaders.py +633 -0
  188. strands_compose-0.1.0/tests/unit/config/loaders/test_validators.py +227 -0
  189. strands_compose-0.1.0/tests/unit/config/resolvers/__init__.py +0 -0
  190. strands_compose-0.1.0/tests/unit/config/resolvers/orchestrations/__init__.py +0 -0
  191. strands_compose-0.1.0/tests/unit/config/resolvers/orchestrations/test_builders.py +328 -0
  192. strands_compose-0.1.0/tests/unit/config/resolvers/orchestrations/test_planner.py +115 -0
  193. strands_compose-0.1.0/tests/unit/config/resolvers/orchestrations/test_tools.py +581 -0
  194. strands_compose-0.1.0/tests/unit/config/resolvers/test_agents.py +270 -0
  195. strands_compose-0.1.0/tests/unit/config/resolvers/test_config.py +145 -0
  196. strands_compose-0.1.0/tests/unit/config/resolvers/test_conversation_manager.py +119 -0
  197. strands_compose-0.1.0/tests/unit/config/resolvers/test_hooks.py +82 -0
  198. strands_compose-0.1.0/tests/unit/config/resolvers/test_mcp.py +118 -0
  199. strands_compose-0.1.0/tests/unit/config/resolvers/test_models.py +64 -0
  200. strands_compose-0.1.0/tests/unit/config/resolvers/test_session_manager.py +234 -0
  201. strands_compose-0.1.0/tests/unit/config/resolvers/test_wire_event_queue.py +78 -0
  202. strands_compose-0.1.0/tests/unit/config/test_edge_cases.py +64 -0
  203. strands_compose-0.1.0/tests/unit/config/test_interpolation.py +92 -0
  204. strands_compose-0.1.0/tests/unit/config/test_schema.py +191 -0
  205. strands_compose-0.1.0/tests/unit/conftest.py +250 -0
  206. strands_compose-0.1.0/tests/unit/converters/__init__.py +0 -0
  207. strands_compose-0.1.0/tests/unit/converters/test_base.py +47 -0
  208. strands_compose-0.1.0/tests/unit/converters/test_openai.py +440 -0
  209. strands_compose-0.1.0/tests/unit/converters/test_raw.py +129 -0
  210. strands_compose-0.1.0/tests/unit/hooks/__init__.py +0 -0
  211. strands_compose-0.1.0/tests/unit/hooks/test_edge_cases.py +59 -0
  212. strands_compose-0.1.0/tests/unit/hooks/test_event_publisher.py +506 -0
  213. strands_compose-0.1.0/tests/unit/hooks/test_max_calls_guard.py +46 -0
  214. strands_compose-0.1.0/tests/unit/hooks/test_stop_guard.py +119 -0
  215. strands_compose-0.1.0/tests/unit/hooks/test_tool_name_sanitizer.py +133 -0
  216. strands_compose-0.1.0/tests/unit/mcp/__init__.py +0 -0
  217. strands_compose-0.1.0/tests/unit/mcp/test_client.py +193 -0
  218. strands_compose-0.1.0/tests/unit/mcp/test_init.py +32 -0
  219. strands_compose-0.1.0/tests/unit/mcp/test_lifecycle.py +152 -0
  220. strands_compose-0.1.0/tests/unit/mcp/test_server.py +203 -0
  221. strands_compose-0.1.0/tests/unit/mcp/test_transports.py +301 -0
  222. strands_compose-0.1.0/tests/unit/models/__init__.py +0 -0
  223. strands_compose-0.1.0/tests/unit/models/test_models.py +61 -0
  224. strands_compose-0.1.0/tests/unit/renderers/__init__.py +0 -0
  225. strands_compose-0.1.0/tests/unit/renderers/test_ansi.py +173 -0
  226. strands_compose-0.1.0/tests/unit/renderers/test_base.py +15 -0
  227. strands_compose-0.1.0/tests/unit/startup/__init__.py +0 -0
  228. strands_compose-0.1.0/tests/unit/startup/test_report.py +102 -0
  229. strands_compose-0.1.0/tests/unit/startup/test_validator.py +133 -0
  230. strands_compose-0.1.0/tests/unit/test_cli.py +501 -0
  231. strands_compose-0.1.0/tests/unit/test_concurrency.py +176 -0
  232. strands_compose-0.1.0/tests/unit/test_event_queue.py +118 -0
  233. strands_compose-0.1.0/tests/unit/test_exception_usage.py +74 -0
  234. strands_compose-0.1.0/tests/unit/test_exceptions.py +84 -0
  235. strands_compose-0.1.0/tests/unit/test_exports.py +25 -0
  236. strands_compose-0.1.0/tests/unit/test_golden_outputs.py +335 -0
  237. strands_compose-0.1.0/tests/unit/test_tools.py +173 -0
  238. strands_compose-0.1.0/tests/unit/test_tools_module.py +92 -0
  239. strands_compose-0.1.0/tests/unit/test_types.py +58 -0
  240. strands_compose-0.1.0/tests/unit/test_utils.py +169 -0
  241. strands_compose-0.1.0/tests/unit/test_wire.py +51 -0
  242. strands_compose-0.1.0/uv.lock +2177 -0
@@ -0,0 +1,124 @@
1
+ name: Bug Report
2
+ description: Report a bug in strands-compose
3
+ title: "[BUG] "
4
+ labels: ["bug", "triage"]
5
+ assignees: []
6
+ body:
7
+ - type: markdown
8
+ attributes:
9
+ value: |
10
+ Thanks for taking the time to fill out this bug report for strands-compose!
11
+
12
+ - type: checkboxes
13
+ id: checks
14
+ attributes:
15
+ label: Checks
16
+ options:
17
+ - label: I have updated to the latest version of strands-compose
18
+ required: true
19
+ - label: I have checked the documentation and this is not expected behavior
20
+ required: true
21
+ - label: I have searched [./issues](./issues?q=) and there are no duplicates of my issue
22
+ required: true
23
+
24
+ - type: input
25
+ id: compose-version
26
+ attributes:
27
+ label: strands-compose Version
28
+ description: Which version of strands-compose are you using?
29
+ placeholder: e.g., 0.1.0
30
+ validations:
31
+ required: true
32
+
33
+ - type: input
34
+ id: strands-version
35
+ attributes:
36
+ label: strands-agents Version
37
+ description: Which version of strands-agents are you using?
38
+ placeholder: e.g., 1.32.0
39
+ validations:
40
+ required: true
41
+
42
+ - type: input
43
+ id: python-version
44
+ attributes:
45
+ label: Python Version
46
+ description: Which version of Python are you using?
47
+ placeholder: e.g., 3.11.5
48
+ validations:
49
+ required: true
50
+
51
+ - type: input
52
+ id: os
53
+ attributes:
54
+ label: Operating System
55
+ description: Which operating system are you using?
56
+ placeholder: e.g., Ubuntu 22.04 / Windows 11 / macOS 14
57
+ validations:
58
+ required: true
59
+
60
+ - type: dropdown
61
+ id: installation-method
62
+ attributes:
63
+ label: Installation Method
64
+ description: How did you install strands-compose?
65
+ options:
66
+ - pip
67
+ - uv
68
+ - git clone
69
+ - other
70
+ validations:
71
+ required: true
72
+
73
+ - type: textarea
74
+ id: config-yaml
75
+ attributes:
76
+ label: Relevant YAML Config
77
+ description: Paste the relevant portion of your `config.yaml` (remove any secrets)
78
+ render: yaml
79
+
80
+ - type: textarea
81
+ id: steps-to-reproduce
82
+ attributes:
83
+ label: Steps to Reproduce
84
+ description: Detailed steps to reproduce the behavior
85
+ placeholder: |
86
+ 1. config.yaml and code snippet (minimal reproducible example)
87
+ 2. Run the command...
88
+ 3. See error...
89
+ validations:
90
+ required: true
91
+
92
+ - type: textarea
93
+ id: expected-behavior
94
+ attributes:
95
+ label: Expected Behavior
96
+ description: A clear description of what you expected to happen
97
+ validations:
98
+ required: true
99
+
100
+ - type: textarea
101
+ id: actual-behavior
102
+ attributes:
103
+ label: Actual Behavior
104
+ description: What actually happened — include full traceback if applicable
105
+ validations:
106
+ required: true
107
+
108
+ - type: textarea
109
+ id: additional-context
110
+ attributes:
111
+ label: Additional Context
112
+ description: Any other relevant information, logs, screenshots, etc.
113
+
114
+ - type: textarea
115
+ id: possible-solution
116
+ attributes:
117
+ label: Possible Solution
118
+ description: Optional — if you have suggestions on how to fix the bug
119
+
120
+ - type: input
121
+ id: related-issues
122
+ attributes:
123
+ label: Related Issues
124
+ description: Optional — link to related issues if applicable
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: strands-compose Discussions
4
+ url: https://github.com/strands-compose/sdk-python/discussions
5
+ about: Please ask and answer questions here
6
+ - name: strands-compose Documentation
7
+ url: https://github.com/strands-compose/sdk-python/tree/main/docs
8
+ about: Visit the documentation for help
@@ -0,0 +1,46 @@
1
+ name: Feature Request
2
+ description: Suggest a new feature or enhancement for strands-compose
3
+ title: "[FEATURE] "
4
+ labels: ["enhancement", "triage"]
5
+ assignees: []
6
+ body:
7
+ - type: markdown
8
+ attributes:
9
+ value: |
10
+ Thanks for suggesting a new feature for strands-compose!
11
+
12
+ - type: textarea
13
+ id: problem-statement
14
+ attributes:
15
+ label: Problem Statement
16
+ description: Describe the problem you're trying to solve. What is currently difficult or impossible to do?
17
+ placeholder: I would like strands-compose to...
18
+ validations:
19
+ required: true
20
+
21
+ - type: textarea
22
+ id: proposed-solution
23
+ attributes:
24
+ label: Proposed Solution
25
+ description: Optional — describe your proposed solution. How would this feature work? Include example YAML or Python if helpful.
26
+
27
+ - type: textarea
28
+ id: use-case
29
+ attributes:
30
+ label: Use Case
31
+ description: Provide specific use cases for the feature. How would people use it?
32
+ placeholder: This would help with...
33
+ validations:
34
+ required: true
35
+
36
+ - type: textarea
37
+ id: alternatives
38
+ attributes:
39
+ label: Alternatives Considered
40
+ description: Optional — have you considered alternative approaches? What are their pros and cons?
41
+
42
+ - type: textarea
43
+ id: additional-context
44
+ attributes:
45
+ label: Additional Context
46
+ description: Any other context, screenshots, code examples, or references that might help understand the request.
@@ -0,0 +1,42 @@
1
+ ## Description
2
+
3
+ <!-- Provide a detailed description of the changes in this PR -->
4
+
5
+ ## Related Issues
6
+
7
+ <!-- Link to related issues using #issue-number format -->
8
+
9
+ ## Type of Change
10
+
11
+ <!-- Choose one and delete the rest -->
12
+
13
+ - Bug fix
14
+ - New feature
15
+ - Breaking change
16
+ - Documentation update
17
+ - Other (please describe):
18
+
19
+ ## YAML / API Impact
20
+
21
+ <!-- Does this change affect the config schema or the public Python API?
22
+ If yes, describe what changes and whether it's backwards-compatible. -->
23
+
24
+ ## Testing
25
+
26
+ How have you tested the change?
27
+
28
+ - [ ] I ran `uv run just check` (lint + type check)
29
+ - [ ] I ran `uv run just test` for overall testing
30
+ - [ ] I added or updated tests that prove my fix is effective or my feature works
31
+ - [ ] I verified existing examples in `examples/` still work
32
+
33
+ ## Checklist
34
+
35
+ - [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) document
36
+ - [ ] I have updated the documentation accordingly
37
+ - [ ] My changes generate no new warnings
38
+ - [ ] Any dependent changes have been merged and published
39
+
40
+ ---
41
+
42
+ By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: developer
3
+ description: Implements features and fixes bugs in strands-compose following all project architecture and coding conventions
4
+ ---
5
+
6
+ You are an expert contributor to strands-compose. Your job is to implement features and fix bugs while strictly following the project's architecture.
7
+
8
+ ## Workflow
9
+
10
+ 1. Read the issue carefully. Identify the minimal change needed.
11
+ 2. Check `.venv/lib/python*/site-packages/strands/` — if strands already provides what is needed, use it directly.
12
+ 3. Identify which module(s) should change using the Directory Structure in the repo instructions.
13
+ 4. Implement the change with full type annotations, Google-style docstrings, and structured logging.
14
+ 5. Write or update unit tests in `tests/unit/` mirroring the changed module path.
15
+ 6. Run `uv run just check` — fix all lint, type, and security issues before proceeding.
16
+ 7. Run `uv run just test` — all tests must pass.
17
+ 8. Open a draft PR with a clear description of what changed and why.
18
+
19
+ ## Where New Code Goes
20
+
21
+ - New YAML config key → `src/strands_compose/models.py` (Pydantic model) + `src/strands_compose/config/schema.py` (JSON schema)
22
+ - New resolver → `src/strands_compose/config/resolvers/`
23
+ - New built-in hook → `src/strands_compose/hooks/`
24
+ - New MCP transport or lifecycle change → `src/strands_compose/mcp/`
25
+ - New converter → `src/strands_compose/converters/`
26
+ - New tool helper → `src/strands_compose/tools/`
27
+ - New renderer → `src/strands_compose/renderers/`
28
+ - Public API changes → `src/strands_compose/__init__.py`
29
+
30
+ ## Hard Rules
31
+
32
+ - Never modify files outside the scope of the issue.
33
+ - Never reimplement what strands already provides.
34
+ - Every new public function, method, and class needs a docstring and full type hints.
35
+ - No `Optional`, `Union`, `List`, `Dict` — use `X | None`, `list`, `dict`.
36
+ - No f-strings in `logger.*` calls — use `%s` with field-value pairs.
37
+ - Raise specific exceptions with context; never swallow with bare `except:`.
38
+ - `from __future__ import annotations` at the top of every module you create or edit.
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: docs-writer
3
+ description: Writes and updates documentation for strands-compose — README, examples, and configuration reference chapters
4
+ ---
5
+
6
+ You are a documentation specialist for strands-compose. Your job is to write and improve documentation so that users can understand and use the library effectively.
7
+
8
+ ## Workflow
9
+
10
+ 1. Identify what needs documenting from the issue or PR.
11
+ 2. Determine the correct location for the change (see below).
12
+ 3. Write clear, concise, accurate documentation. Test any YAML or Python examples by running them.
13
+ 4. Run `uv run just check` to ensure no markdown lint issues.
14
+ 5. Open a PR scoped only to documentation changes.
15
+
16
+ ## Where Documentation Lives
17
+
18
+ | Content | Location |
19
+ |---------|----------|
20
+ | Project overview, installation, quick-start | `README.md` |
21
+ | YAML configuration reference (per-feature) | `docs/configuration/Chapter_XX.md` |
22
+ | Quick recipes / how-tos | `docs/configuration/Quick_Recipes.md` |
23
+ | Example projects | `examples/NN_name/` — each needs `config.yaml`, `main.py`, `README.md` |
24
+ | Release history | `CHANGELOG.md` — follows Keep a Changelog format |
25
+
26
+ ## Writing Rules
27
+
28
+ - Use plain English. Short sentences. Active voice.
29
+ - Every documented feature needs a minimal working YAML example.
30
+ - YAML examples must use valid strands-compose syntax — verify against the JSON schema in `src/strands_compose/config/schema.py`.
31
+ - Python examples must be runnable as-is.
32
+ - Do not document internal implementation details — only the public API and YAML config surface.
33
+ - Use relative links (never absolute URLs) for files within the repository.
34
+ - Keep `README.md` concise — link out to `docs/` for detail rather than expanding inline.
35
+
36
+ ## Examples
37
+
38
+ When adding a new example under `examples/`:
39
+ - Follow the naming pattern: `NN_short_name/` (next available number)
40
+ - The `README.md` must explain what the example demonstrates and how to run it.
41
+ - Use `TEMPLATE_EXAMPLE.md` in `examples/` as a structural guide.
42
+ - Run `uv run just test` — there is a smoke test suite in `tests/examples/` that runs all examples.
43
+
44
+ ## What Not to Change
45
+
46
+ - Do not modify source code.
47
+ - Do not edit `docs/configuration/` chapters without understanding the full feature — ask for clarification in the issue if unsure.
48
+ - Do not remove existing examples without an explicit request.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: reviewer
3
+ description: Reviews code in pull requests for correctness, style, architecture compliance, and security in strands-compose
4
+ ---
5
+
6
+ You are a senior code reviewer for strands-compose. Your job is to review pull requests and leave precise, actionable feedback. You enforce the project rules strictly but fairly.
7
+
8
+ ## Review Workflow
9
+
10
+ 1. Read the PR description and linked issue to understand the intended change.
11
+ 2. Check that the change is minimal — flag any refactoring of unrelated code.
12
+ 3. Run `uv run just check` — report any lint, type, or security failures.
13
+ 4. Run `uv run just test` — report any test failures or coverage regressions.
14
+ 5. Leave inline comments on specific lines. Request changes for rule violations; suggest (not require) improvements for style.
15
+
16
+ ## What to Check
17
+
18
+ ### Architecture
19
+ - [ ] Change is placed in the correct module (see Directory Structure in repo instructions)
20
+ - [ ] No strands functionality reimplemented — check `.venv/lib/python*/site-packages/strands/`
21
+ - [ ] No global state, singletons, or auto-registration introduced
22
+ - [ ] Public API changes are reflected in `src/strands_compose/__init__.py`
23
+
24
+ ### Python rules
25
+ - [ ] `from __future__ import annotations` present in every modified module
26
+ - [ ] All functions/methods fully typed (parameters + return type)
27
+ - [ ] No `Optional`, `Union`, `List`, `Dict` — only `X | None`, `list`, `dict`
28
+ - [ ] Google-style docstring on every new public class, function, and method
29
+ - [ ] Class docstrings on `__init__`, not the class body
30
+ - [ ] No f-strings in `logger.*` calls — `%s` field-value pairs only
31
+ - [ ] No bare `except:` — specific exception types with context messages
32
+ - [ ] Properties returning mutable state return copies: `return list(self._items)`
33
+ - [ ] No hardcoded secrets, no `eval()`, `exec()`, `subprocess(shell=True)`
34
+
35
+ ### Tests
36
+ - [ ] New public code has tests in `tests/unit/` mirroring the source path
37
+ - [ ] Error paths are tested with `pytest.raises`
38
+ - [ ] Tests are named descriptively
39
+
40
+ ### Commits
41
+ - [ ] Commit messages follow conventional commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`)
42
+ - [ ] No "WIP" commits in the final PR
43
+
44
+ ## Tone
45
+
46
+ Be direct and specific. Quote the problematic line. Explain why it violates a rule and what the fix should be. Don't leave vague comments like "consider refactoring this".
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: tester
3
+ description: Writes and improves tests for strands-compose — unit, integration, and example smoke tests
4
+ ---
5
+
6
+ You are a testing specialist for strands-compose. Your job is to add missing tests, improve coverage, and ensure all test behaviour is correct and well-structured.
7
+
8
+ ## Workflow
9
+
10
+ 1. Identify what is under-tested: missing unit tests, edge cases, or error paths.
11
+ 2. Place new test files in `tests/unit/` mirroring the `src/strands_compose/` path (e.g. `src/strands_compose/hooks/stop_guard.py` → `tests/unit/hooks/test_stop_guard.py`).
12
+ 3. Write tests using `pytest` — use `fixtures`, `parametrize`, and `tmp_path`. Mock all external dependencies.
13
+ 4. Run `uv run just test` — all tests must pass and coverage must remain ≥ 70%.
14
+ 5. Run `uv run just check` — tests must also pass lint and type checks.
15
+
16
+ ## Test Structure Rules
17
+
18
+ - Test **behaviour**, not implementation details.
19
+ - Name tests descriptively: `test_<what>_<condition>_<expected_outcome>`.
20
+ - Good: `test_interpolate_missing_var_without_default_raises_value_error`
21
+ - Bad: `test_interpolate_1`
22
+ - One `assert` concept per test where practical — split into multiple tests rather than one large test.
23
+ - Use `pytest.raises` with `match=` to assert exception messages.
24
+ - Mock at the boundary: patch I/O, network, and strands internals — not internal logic you're testing.
25
+ - Use `tmp_path` for any file system interactions.
26
+
27
+ ## Coverage Targets
28
+
29
+ - Every public function and method must have at least one test.
30
+ - Error paths (`ValueError`, `KeyError`, `RuntimeError`, etc.) each need a dedicated test.
31
+ - Parametrize repetitive cases instead of copy-pasting test bodies.
32
+
33
+ ## What Not to Change
34
+
35
+ - Do not modify source code to make tests pass — fix the test or raise an issue.
36
+ - Do not add integration tests for behaviour already covered by unit tests.
37
+ - Do not remove existing tests unless they are genuinely wrong or duplicate.
@@ -0,0 +1,176 @@
1
+ # strands-compose — Copilot Instructions
2
+
3
+ This is **strands-compose**: a declarative multi-agent orchestration library for [strands-agents](https://github.com/strands-agents/sdk-python).
4
+ It reads YAML configs and returns fully wired, plain `strands` objects — no wrappers, no subclasses.
5
+
6
+ ---
7
+
8
+ ## Architecture — NON-NEGOTIABLE
9
+
10
+ 1. **Strands-first** — always check `.venv/lib/python*/site-packages/strands/` before implementing anything. If strands provides it, use it directly.
11
+ 2. **Thin wrapper** — translate YAML → Python objects, then get out of the way.
12
+ 3. **Composition over inheritance** — small, focused components that compose.
13
+ 4. **Explicit over implicit** — no auto-registration, no global singletons.
14
+ 5. **Single responsibility** — each module does one thing.
15
+ 6. **Testable in isolation** — no global state, every unit testable without other components.
16
+
17
+ ## Python Rules
18
+
19
+ - `from __future__ import annotations` at the top of every module.
20
+ - Every public function/method/class must be fully typed — parameters and return type.
21
+ - Use `X | None`, `X | Y`, `list`, `dict`, `tuple` — never `Optional`, `Union`, `List`, `Dict`.
22
+ - Google-style docstrings on every public class, function, and method.
23
+ - Class docstring goes on `__init__`, not the class body.
24
+ - Early returns always — handle edge cases first, max 3 nesting levels.
25
+ - Raise specific exceptions (`ValueError`, `KeyError`, `TypeError`, `RuntimeError`) with context.
26
+ - Never silently swallow exceptions. No bare `except:`.
27
+ - Return copies from properties: `return list(self._items)`.
28
+ - `logging.getLogger(__name__)` — never `print()` for diagnostics.
29
+ - No `eval()`, `exec()`, `pickle` for untrusted data, `subprocess(shell=True)`.
30
+ - No hardcoded secrets — use env vars.
31
+ - Import order: stdlib → third-party → local (ruff-enforced).
32
+ - `__all__` only in `__init__.py`.
33
+
34
+ ## Naming
35
+
36
+ - Classes: `PascalCase` | functions/methods: `snake_case` | constants: `UPPER_SNAKE_CASE` | private: `_prefix`
37
+ - No abbreviations in public API. Boolean params: `is_`, `has_`, `enable_` prefixes.
38
+
39
+ ## Key Strands APIs (do NOT reimplement)
40
+
41
+ | What | Import path |
42
+ |------|-------------|
43
+ | `Agent` | `strands.agent.agent` |
44
+ | Hook events | `strands.hooks.events` — `BeforeInvocationEvent`, `AfterInvocationEvent`, `BeforeModelCallEvent`, `AfterModelCallEvent`, `BeforeToolCallEvent`, `AfterToolCallEvent` |
45
+ | `HookProvider` | `strands.hooks` — implement `register_hooks(registry)` |
46
+ | `MCPClient` | `strands.tools.mcp.mcp_client` |
47
+ | `SessionManager` | `strands.session` — `FileSessionManager`, `S3SessionManager` |
48
+ | Multi-agent | `strands.multiagent` — `Swarm`, `Graph` |
49
+ | `ToolRegistry` | `strands.tools.registry` |
50
+ | `@tool` decorator | `strands.tools.decorator` |
51
+
52
+ ## Testing
53
+
54
+ - Every public function gets at least one test. Test behavior, not implementation.
55
+ - Use pytest fixtures, `parametrize`, `tmp_path`. Mock external dependencies.
56
+ - Name tests descriptively: `test_interpolate_missing_var_without_default_raises_value_error`.
57
+
58
+ ## Tooling
59
+
60
+ ```bash
61
+ uv run just install # install deps + git hooks (once after clone)
62
+ uv run just check # lint + type check + security scan
63
+ uv run just test # pytest with coverage (≥70%)
64
+ uv run just format # auto-format with ruff
65
+ ```
66
+
67
+ ## Directory Structure
68
+
69
+ ```
70
+ src/strands_compose/
71
+ ├── __init__.py # Public API — load(), ResolvedConfig
72
+ ├── models.py # Pydantic config models (AgentConfig, ModelConfig, …)
73
+ ├── types.py # Shared type aliases
74
+ ├── utils.py # Miscellaneous helpers
75
+ ├── exceptions.py # Custom exception hierarchy
76
+ ├── wire.py # Final assembly — wires all resolved objects into ResolvedConfig
77
+ ├── config/ # YAML loading, validation, interpolation
78
+ │ ├── schema.py # JSON-schema for config validation
79
+ │ ├── interpolation.py # ${VAR:-default} interpolation
80
+ │ ├── loaders/ # File/string/dict loaders, helpers, validators
81
+ │ └── resolvers/ # Per-key resolvers (agents, models, mcp, hooks, …)
82
+ │ └── orchestrations/ # Orchestration builder and planner
83
+ │ ├── builders.py # Build delegate, swarm, graph objects
84
+ │ └── planner.py # Resolve orchestration config to plan
85
+ ├── converters/ # Config dict → strands objects
86
+ │ ├── base.py # BaseConverter protocol
87
+ │ ├── openai.py # OpenAI-specific conversion
88
+ │ └── raw.py # Raw/passthrough conversion
89
+ ├── hooks/ # Built-in HookProvider implementations
90
+ │ ├── event_publisher.py # Streaming event queue publisher
91
+ │ ├── max_calls_guard.py # Max tool-call circuit breaker
92
+ │ ├── stop_guard.py # Agent stop-signal hook
93
+ │ └── tool_name_sanitizer.py # Sanitize tool names for model compatibility
94
+ ├── mcp/ # MCP server/client lifecycle
95
+ │ ├── client.py # MCPClient factory and wiring
96
+ │ ├── lifecycle.py # Server startup, readiness polling, shutdown
97
+ │ ├── server.py # Local Python server launcher
98
+ │ └── transports.py # Transport builders (stdio, streamable_http)
99
+ ├── renderers/ # Terminal output rendering
100
+ │ ├── base.py # BaseRenderer protocol
101
+ │ └── ansi.py # ANSI colour renderer
102
+ ├── startup/ # Post-load validation and reporting
103
+ │ ├── validator.py # Config correctness checks
104
+ │ └── report.py # Human-readable startup report
105
+ └── tools/ # Tool loading helpers
106
+ ├── extractors.py # Extract @tool functions from modules
107
+ ├── loaders.py # Import modules by path/name
108
+ └── wrappers.py # Wrap callables as strands tools
109
+
110
+ tests/
111
+ ├── unit/ # Unit tests (mirrors src/ structure)
112
+ │ ├── config/ # Tests for config loading, schema, interpolation, resolvers
113
+ │ ├── converters/ # Tests for converter modules
114
+ │ ├── hooks/ # Tests for hook providers
115
+ │ ├── mcp/ # Tests for MCP lifecycle
116
+ │ ├── models/ # Tests for Pydantic config models
117
+ │ ├── renderers/ # Tests for renderers
118
+ │ └── startup/ # Tests for validator and report
119
+ ├── integration/ # Integration tests (real strands objects)
120
+ └── examples/ # Smoke tests for all examples/
121
+ ```
122
+
123
+ ## Logging Style
124
+
125
+ Use `%s` interpolation with structured field-value pairs — never f-strings:
126
+
127
+ ```python
128
+ # Good
129
+ logger.debug("agent_id=<%s>, tool=<%s> | tool call started", agent_id, tool_name)
130
+ logger.warning("path=<%s>, reason=<%s> | config file not found", path, reason)
131
+
132
+ # Bad
133
+ logger.debug(f"Tool {tool_name} called on agent {agent_id}") # no f-strings
134
+ logger.info("Config loaded.") # no punctuation
135
+ ```
136
+
137
+ - Field-value pairs first: `key=<value>` separated by commas
138
+ - Human-readable message after ` | `
139
+ - `<>` around values (makes empty values visible)
140
+ - Lowercase messages, no trailing punctuation
141
+ - `%s` format strings, not f-strings (lazy evaluation)
142
+
143
+ ## Things to Do
144
+
145
+ - Check `.venv/lib/python*/site-packages/strands/` before implementing — use strands if it exists
146
+ - `from __future__ import annotations` at the top of every module
147
+ - Fully type every function signature (parameters + return type)
148
+ - Google-style docstring on every public class, function, and method
149
+ - Put class docstrings on `__init__`, not the class body
150
+ - Early returns — handle edge cases first, max 3 nesting levels
151
+ - Raise specific exceptions (`ValueError`, `KeyError`, `TypeError`, `RuntimeError`) with context
152
+ - Return copies from properties exposing mutable state: `return list(self._items)`
153
+ - Use structured logging with `%s` and field-value pairs
154
+ - Run `uv run just check` then `uv run just test` before committing
155
+
156
+ ## Things NOT to Do
157
+
158
+ - Don't reimplement what strands already provides — check first
159
+ - Don't use `Optional[X]`, `Union[X, Y]`, `List`, `Dict` — use `X | None`, `list`, `dict`
160
+ - Don't use `print()` for diagnostics — use `logging.getLogger(__name__)`
161
+ - Don't use f-strings in log calls — use `%s` interpolation
162
+ - Don't swallow exceptions silently — no bare `except:`
163
+ - Don't add `__all__` outside `__init__.py`
164
+ - Don't hardcode secrets — use env vars
165
+ - Don't use `eval()`, `exec()`, `pickle` for untrusted data, or `subprocess(shell=True)`
166
+ - Don't commit without running `uv run just check`
167
+ - Don't add comments about what changed or temporal context ("recently refactored", "moved from")
168
+
169
+ ## Agent-Specific Notes
170
+
171
+ - Make the **smallest reasonable change** to achieve the goal — don't refactor unrelated code
172
+ - Prefer simple, readable, maintainable solutions over clever ones
173
+ - When unsure where something belongs, check the Directory Structure above
174
+ - Comments should explain **what** and **why**, never **when** or **how it changed**
175
+ - If you find something broken while working, fix it — don't leave it commented out
176
+ - Never add or change files outside the scope of the task
@@ -0,0 +1,20 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "daily"
7
+ open-pull-requests-limit: 100
8
+ commit-message:
9
+ prefix: ci
10
+ groups:
11
+ dev-dependencies:
12
+ patterns:
13
+ - "pytest"
14
+ - package-ecosystem: "github-actions"
15
+ directory: "/"
16
+ schedule:
17
+ interval: "daily"
18
+ open-pull-requests-limit: 100
19
+ commit-message:
20
+ prefix: ci
@@ -0,0 +1,76 @@
1
+ # Continuous Integration — runs on every push and pull request.
2
+
3
+ name: CI
4
+
5
+ on:
6
+ push:
7
+ branches: ["main"]
8
+ pull_request:
9
+ branches: ["main"]
10
+ workflow_call:
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ # ── Quality checks (format · lint · type · security) ──────────────────────
21
+ check:
22
+ name: Quality checks
23
+ runs-on: ubuntu-latest
24
+ permissions:
25
+ contents: read
26
+ steps:
27
+ - uses: actions/checkout@v6
28
+
29
+ - uses: astral-sh/setup-uv@v7
30
+ with:
31
+ enable-cache: true
32
+
33
+ - name: Install just
34
+ uses: extractions/setup-just@v3
35
+
36
+ - name: Install dev deps
37
+ run: uv sync --all-groups --all-extras
38
+
39
+ - name: Ruff — format check
40
+ run: uv run just check-format
41
+
42
+ - name: Ruff — lint
43
+ run: uv run just check-code
44
+
45
+ - name: ty — type check
46
+ run: uv run just check-type
47
+
48
+ - name: Bandit — security scan
49
+ run: uv run just check-security
50
+
51
+ # ── Tests ──────────────────────────────────────────────────────────────────
52
+ test:
53
+ name: Tests (Python ${{ matrix.python-version }})
54
+ runs-on: ubuntu-latest
55
+ permissions:
56
+ contents: read
57
+ strategy:
58
+ fail-fast: false
59
+ matrix:
60
+ python-version: ["3.11", "3.12", "3.13"]
61
+ steps:
62
+ - uses: actions/checkout@v6
63
+
64
+ - uses: astral-sh/setup-uv@v7
65
+ with:
66
+ enable-cache: true
67
+ python-version: ${{ matrix.python-version }}
68
+
69
+ - name: Install just
70
+ uses: extractions/setup-just@v3
71
+
72
+ - name: Install dev deps
73
+ run: uv sync --all-groups --all-extras
74
+
75
+ - name: pytest with coverage
76
+ run: uv run just test