drydock-cli 3.1.33__tar.gz → 3.1.35__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 (225) hide show
  1. {drydock_cli-3.1.33/drydock_cli.egg-info → drydock_cli-3.1.35}/PKG-INFO +1 -1
  2. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/mission_cli.py +17 -2
  3. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/mission_run.py +30 -0
  4. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/swarm.py +31 -10
  5. {drydock_cli-3.1.33 → drydock_cli-3.1.35/drydock_cli.egg-info}/PKG-INFO +1 -1
  6. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/pyproject.toml +1 -1
  7. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_mission_cli.py +19 -0
  8. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_mission_run.py +24 -0
  9. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_swarm.py +44 -0
  10. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/LICENSE +0 -0
  11. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/NOTICE +0 -0
  12. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/README.md +0 -0
  13. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/__init__.py +0 -0
  14. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/__main__.py +0 -0
  15. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/advisor.py +0 -0
  16. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/agent.py +0 -0
  17. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/asr.py +0 -0
  18. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/bash_safety.py +0 -0
  19. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/bottleneck.py +0 -0
  20. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/budget.py +0 -0
  21. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/__init__.py +0 -0
  22. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/document-canvas.md +0 -0
  23. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/fiar-assess.md +0 -0
  24. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/fiar-cap.md +0 -0
  25. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/fiar-evidence.md +0 -0
  26. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/fiar-readiness.md +0 -0
  27. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/ml-data.md +0 -0
  28. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/ml-debug.md +0 -0
  29. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/ml-finetune.md +0 -0
  30. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/ml-metrics.md +0 -0
  31. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/ml-rl.md +0 -0
  32. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/ml-train.md +0 -0
  33. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/nist-ai-rmf.md +0 -0
  34. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/nist-csf.md +0 -0
  35. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/rmf-categorize.md +0 -0
  36. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/rmf-control.md +0 -0
  37. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/rmf-poam.md +0 -0
  38. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/rmf-review.md +0 -0
  39. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/stig-assess.md +0 -0
  40. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/builtin_skills/stig-remediate.md +0 -0
  41. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/capacity.py +0 -0
  42. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/cci.py +0 -0
  43. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/cli.py +0 -0
  44. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/comms/__init__.py +0 -0
  45. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/comms/attention.py +0 -0
  46. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/comms/channels.py +0 -0
  47. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/comms/events.py +0 -0
  48. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/compaction.py +0 -0
  49. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/config.py +0 -0
  50. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/detect.py +0 -0
  51. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/doccanvas.py +0 -0
  52. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/eratchet.py +0 -0
  53. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/events.py +0 -0
  54. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/extract.py +0 -0
  55. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/fiar.py +0 -0
  56. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/gittools.py +0 -0
  57. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/graphrag.py +0 -0
  58. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/groundtruth.py +0 -0
  59. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/guards.py +0 -0
  60. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/jobs.py +0 -0
  61. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/loop_detect.py +0 -0
  62. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/mcp.py +0 -0
  63. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/mission.py +0 -0
  64. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/pdfredbox.py +0 -0
  65. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/phases.py +0 -0
  66. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/poam.py +0 -0
  67. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/predictions.py +0 -0
  68. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/progress.py +0 -0
  69. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/providers.py +0 -0
  70. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/ratchet.py +0 -0
  71. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/recipes.py +0 -0
  72. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/recovery.py +0 -0
  73. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/resume.py +0 -0
  74. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/rmf.py +0 -0
  75. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/rmf_graph.py +0 -0
  76. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/skills.py +0 -0
  77. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/stig.py +0 -0
  78. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/subagents.py +0 -0
  79. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/suggest.py +0 -0
  80. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/task_state.py +0 -0
  81. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tool_policy.py +0 -0
  82. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tool_registry.py +0 -0
  83. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tool_result.py +0 -0
  84. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tool_select.py +0 -0
  85. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tool_validate.py +0 -0
  86. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tools/__init__.py +0 -0
  87. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/trajectory.py +0 -0
  88. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tui/__init__.py +0 -0
  89. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tui/app.py +0 -0
  90. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tui/approval.py +0 -0
  91. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tui/messages.py +0 -0
  92. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tui/widgets.py +0 -0
  93. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/tuning.py +0 -0
  94. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/verification.py +0 -0
  95. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock/web.py +0 -0
  96. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock_cli.egg-info/SOURCES.txt +0 -0
  97. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock_cli.egg-info/dependency_links.txt +0 -0
  98. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock_cli.egg-info/entry_points.txt +0 -0
  99. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock_cli.egg-info/requires.txt +0 -0
  100. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/drydock_cli.egg-info/top_level.txt +0 -0
  101. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/setup.cfg +0 -0
  102. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_advisor.py +0 -0
  103. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_approval.py +0 -0
  104. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_asr.py +0 -0
  105. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_back_command.py +0 -0
  106. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_background.py +0 -0
  107. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_binary_output.py +0 -0
  108. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_crossplatform.py +0 -0
  109. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_output_bounding.py +0 -0
  110. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_process_group.py +0 -0
  111. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_safety.py +0 -0
  112. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_sanitize.py +0 -0
  113. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_shell.py +0 -0
  114. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_stdin.py +0 -0
  115. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_stop_partial.py +0 -0
  116. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_timeout_network.py +0 -0
  117. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bash_timeout_param.py +0 -0
  118. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_bottleneck.py +0 -0
  119. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_budget.py +0 -0
  120. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_capacity.py +0 -0
  121. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_cci.py +0 -0
  122. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_cli_agents.py +0 -0
  123. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_comms.py +0 -0
  124. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_compact_command.py +0 -0
  125. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_compaction.py +0 -0
  126. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_config.py +0 -0
  127. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_config_migration.py +0 -0
  128. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_context_limit_config.py +0 -0
  129. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_context_limit_issue25.py +0 -0
  130. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_criteria_coverage.py +0 -0
  131. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_cycling_detection.py +0 -0
  132. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_declared_deps.py +0 -0
  133. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_degenerate_argument.py +0 -0
  134. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_detect.py +0 -0
  135. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_dispatch.py +0 -0
  136. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_doccanvas.py +0 -0
  137. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_e2e_connected.py +0 -0
  138. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_edit_replace_all.py +0 -0
  139. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_edit_thrash_and_compaction.py +0 -0
  140. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_effort_governor.py +0 -0
  141. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_empty_response.py +0 -0
  142. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_eratchet.py +0 -0
  143. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_events.py +0 -0
  144. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_extract.py +0 -0
  145. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_failure_loop.py +0 -0
  146. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_fiar.py +0 -0
  147. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_first_run_setup.py +0 -0
  148. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_gittools.py +0 -0
  149. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_glob_edit_edges.py +0 -0
  150. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_graphify_example.py +0 -0
  151. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_graphrag.py +0 -0
  152. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_graphrag_quoted_path.py +0 -0
  153. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_graphrag_sqlite.py +0 -0
  154. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_grep_and_read_robust.py +0 -0
  155. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_groundtruth_predictions.py +0 -0
  156. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_guards_and_tools.py +0 -0
  157. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_hallucinated_tools.py +0 -0
  158. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_jobs.py +0 -0
  159. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_knowledge_tool_available.py +0 -0
  160. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_leaked_tool_call.py +0 -0
  161. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_loop_detect.py +0 -0
  162. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_main_api_key.py +0 -0
  163. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_mcp.py +0 -0
  164. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_mission.py +0 -0
  165. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_model_registry.py +0 -0
  166. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_oneshot_unreachable.py +0 -0
  167. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_overthink_interrupt.py +0 -0
  168. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_pdfredbox.py +0 -0
  169. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_phases.py +0 -0
  170. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_plan_autocontinue.py +0 -0
  171. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_poam.py +0 -0
  172. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_progress.py +0 -0
  173. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_providers_unreachable.py +0 -0
  174. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_ratchet.py +0 -0
  175. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_ratchet_offer.py +0 -0
  176. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_read_index.py +0 -0
  177. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_recipes.py +0 -0
  178. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_recovery.py +0 -0
  179. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_recovery_config.py +0 -0
  180. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_recovery_integration.py +0 -0
  181. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_repeated_outcome.py +0 -0
  182. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_repetition_interrupt.py +0 -0
  183. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_resume_events.py +0 -0
  184. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_resume_restore.py +0 -0
  185. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_resume_snapshot.py +0 -0
  186. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_rmf.py +0 -0
  187. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_rmf_graph.py +0 -0
  188. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_rmf_stig_graph.py +0 -0
  189. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_rolling_plan.py +0 -0
  190. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_runaway_repetition.py +0 -0
  191. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_screenshot.py +0 -0
  192. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_server_probe.py +0 -0
  193. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_skills.py +0 -0
  194. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_sqlite_events.py +0 -0
  195. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_stall_retry.py +0 -0
  196. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_stig.py +0 -0
  197. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_stop.py +0 -0
  198. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_streaming_newlines.py +0 -0
  199. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_subagent.py +0 -0
  200. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_subagents.py +0 -0
  201. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_suggest.py +0 -0
  202. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_system_prompt_help.py +0 -0
  203. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_task_state.py +0 -0
  204. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_timeline.py +0 -0
  205. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_todo.py +0 -0
  206. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_arg_coercion.py +0 -0
  207. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_arg_coercion_more.py +0 -0
  208. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_arg_parsing.py +0 -0
  209. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_canonical.py +0 -0
  210. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_policy.py +0 -0
  211. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_result.py +0 -0
  212. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_select.py +0 -0
  213. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tool_validate.py +0 -0
  214. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tools_undo.py +0 -0
  215. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_trajectory.py +0 -0
  216. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tui.py +0 -0
  217. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_tuning.py +0 -0
  218. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_verification_gate.py +0 -0
  219. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_viewimage.py +0 -0
  220. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_vision_input.py +0 -0
  221. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_web_tools.py +0 -0
  222. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_windows_shell.py +0 -0
  223. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_worker_subagent.py +0 -0
  224. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_write_content_coerce.py +0 -0
  225. {drydock_cli-3.1.33 → drydock_cli-3.1.35}/tests/test_xccdf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.1.33
3
+ Version: 3.1.35
4
4
  Summary: Drydock — a local, provider-agnostic terminal coding agent for local LLMs
5
5
  Author: Frank Bobe III
6
6
  License-Expression: Apache-2.0
@@ -215,6 +215,13 @@ def _parse_budget(argv: list[str]) -> tuple[dict, dict, str, list[str], dict, li
215
215
  elif a == "--max-escalations" and nxt: # ladder climbs before BLOCKED/AWAITING_HUMAN (§23)
216
216
  eval_cfg["max_escalations"] = int(nxt) if nxt.isdigit() else 3
217
217
  i += 2
218
+ elif a == "--swarm": # solve each task with a SWARM, not one agent
219
+ eval_cfg["swarm"] = True
220
+ i += 1
221
+ elif a == "--swarm-agents" and nxt: # agents per task's swarm (implies --swarm)
222
+ eval_cfg["swarm"] = True
223
+ eval_cfg["swarm_agents"] = int(nxt) if nxt.isdigit() else 4
224
+ i += 2
218
225
  elif a == "--model" and nxt: # mission carries its own model/endpoint (§33)
219
226
  eval_cfg["model"] = nxt
220
227
  i += 2
@@ -248,7 +255,7 @@ def run_cli(argv: list, config: dict | None = None) -> int:
248
255
  if not objective:
249
256
  print('usage: drydock mission create "<objective>" [--target ">=70"] '
250
257
  '[--verify CMD] [--protect GLOB]... [--samples N] [--noise-band F] '
251
- '[--time-budget 48h] [--max-experiments N]')
258
+ '[--swarm | --swarm-agents N] [--time-budget 48h] [--max-experiments N]')
252
259
  return 1
253
260
  mconf = {"verify_cmd": verify, "model": config.get("model", ""),
254
261
  "base_url": config.get("base_url", ""), "provider": config.get("provider", "vllm"),
@@ -337,6 +344,14 @@ def _run(store: M.MissionStore, mid: str, cwd: str, config: dict, *, resume: boo
337
344
  if mconf.get(key):
338
345
  base_config[key] = mconf[key]
339
346
  print(f" model: {base_config.get('model')} @ {base_config.get('base_url')}")
347
+ # Worker: one bounded agent per task (default), OR a swarm per task (--swarm) — parallel
348
+ # diversified agents + blackboard cross-pollination, KEEP/REVERT on the swarm winner (§Phase-4).
349
+ if mconf.get("swarm"):
350
+ n = int(mconf.get("swarm_agents") or 4)
351
+ worker = R.make_swarm_worker(agents=n, share=True, waves=2)
352
+ print(f" worker: SWARM ({n} agents/task, shared blackboard)")
353
+ else:
354
+ worker = R.default_worker
340
355
  if evaluator is not None:
341
356
  b = R.establish_baseline(store, mid, cwd, evaluator)
342
357
  if b is not None:
@@ -352,7 +367,7 @@ def _run(store: M.MissionStore, mid: str, cwd: str, config: dict, *, resume: boo
352
367
  print(f" ⚑ {kind}: {d}")
353
368
  write_views(store, mid, cwd)
354
369
 
355
- R.run_mission(store, mid, cwd=cwd, repo=cwd, evaluator=evaluator,
370
+ R.run_mission(store, mid, cwd=cwd, repo=cwd, worker=worker, evaluator=evaluator,
356
371
  planner=decomposing_planner(m["objective"], verify, cwd), base_config=base_config,
357
372
  stagnation_limit=int(mconf.get("stagnation_limit") or 5),
358
373
  max_escalations=int(mconf.get("max_escalations") or 3),
@@ -191,6 +191,36 @@ def default_worker(task: dict, mission: dict, cwd: str, base_config: dict) -> Wo
191
191
  out_tokens=int(getattr(state, "total_output_tokens", 0) or 0))
192
192
 
193
193
 
194
+ def make_swarm_worker(agents: "int | str" = 4, share: bool = True, waves: int = 2,
195
+ max_turns: int = 30, runner=None) -> WorkerFn:
196
+ """A WorkerFn that solves each mission task with a SWARM (parallel diversified agents +
197
+ blackboard cross-pollination) instead of a single agent — the long-horizon mission dispatches
198
+ a swarm per task (ASR Phase-4: durable depth × parallel breadth). The winning candidate's
199
+ commit is checked out into the mission's working tree (no commit of its own — the mission's
200
+ KEEP/REVERT handles that), so the mission evaluator scores the swarm's best result."""
201
+ def worker(task: dict, mission: dict, cwd: str, base_config: dict) -> WorkerResult:
202
+ from drydock import swarm as S
203
+ objective = task.get("objective", "")
204
+ verify_cmd = (mission.get("config") or {}).get("verify_cmd") or None
205
+ kw: dict = dict(agents=agents, base_config=base_config or {}, base_ref="HEAD",
206
+ verify_cmd=verify_cmd, share=share, waves=waves, max_turns=max_turns)
207
+ if runner is not None:
208
+ kw["runner"] = runner
209
+ try:
210
+ res = S.run_swarm(cwd, objective, **kw)
211
+ except Exception as e: # noqa: BLE001 — a swarm crash is contained like any worker (§32)
212
+ return WorkerResult(ok=False, error=f"swarm failed: {type(e).__name__}: {e}")
213
+ w = res.winner
214
+ if w is not None and w.commit:
215
+ # bring the winner's tree into the mission's working dir; the mission's checkpoint
216
+ # (KEEP) or restore (REVERT) commits/discards it, so no extra commit here.
217
+ _git(["checkout", w.commit, "--", "."], cwd)
218
+ return WorkerResult(ok=True, summary=(f"swarm winner {w.agent} "
219
+ f"{w.tests_passed}/{w.tests_total}: {(w.summary or '')[:200]}"))
220
+ return WorkerResult(ok=True, summary="swarm produced no winning candidate")
221
+ return worker
222
+
223
+
194
224
  def _median(xs: list[float]) -> float:
195
225
  s = sorted(xs)
196
226
  n = len(s)
@@ -693,17 +693,18 @@ def _peer_notes(candidates: list[Candidate], limit: int = 8) -> str:
693
693
  each attempt's verdict + verified score + one-line summary, so a reader builds on the
694
694
  partials and avoids repeating the failures. Only summaries cross between agents, never
695
695
  full transcripts (§8 — preserve independence while still comparing notes)."""
696
+ # Rank strongest-first so the reader sees the best partial at the top, and explicitly flag
697
+ # it as the base to EXTEND — the actionable core of cross-pollination (§14): don't restart
698
+ # from scratch, continue the leading partial (and skip the failed approaches below it).
699
+ ranked = sorted(candidates, key=lambda c: (c.tests_passed, c.tests_total), reverse=True)[:limit]
696
700
  lines = []
697
- for c in candidates[:limit]:
701
+ for i, c in enumerate(ranked):
698
702
  score = f"{c.tests_passed}/{c.tests_total}" if c.tests_total else "unverified"
699
- if c.tests_total and c.tests_passed >= c.tests_total:
700
- verdict = "SOLVED"
701
- elif c.tests_passed:
702
- verdict = "partial"
703
- else:
704
- verdict = "failed"
703
+ solved = bool(c.tests_total and c.tests_passed >= c.tests_total)
704
+ verdict = "SOLVED" if solved else ("partial" if c.tests_passed else "failed")
705
705
  summ = (c.summary or "").strip().splitlines()[0][:160] if c.summary else "(no summary)"
706
- lines.append(f"- {c.agent} [{verdict} {score}]: {summ}")
706
+ tag = " ⟵ STRONGEST partial — EXTEND this approach" if (i == 0 and c.tests_passed and not solved) else ""
707
+ lines.append(f"- {c.agent} [{verdict} {score}]{tag}: {summ}")
707
708
  return "\n".join(lines)
708
709
 
709
710
 
@@ -971,6 +972,11 @@ def run_cli(argv: list, config: dict | None = None) -> int:
971
972
  p.add_argument("--max-turns", type=int, default=40)
972
973
  p.add_argument("--max-tool-calls", type=int, default=40)
973
974
  p.add_argument("--max-workers", type=int, default=None, help="max concurrent workers")
975
+ p.add_argument("--share", action="store_true",
976
+ help="blackboard cross-pollination: run in waves; later agents read peers' "
977
+ "verified attempts and build on partials instead of repeating failures")
978
+ p.add_argument("--waves", type=int, default=2,
979
+ help="number of waves when --share is set (wave 0 explores blind; default 2)")
974
980
  try:
975
981
  args = p.parse_args(argv)
976
982
  except SystemExit as e:
@@ -986,12 +992,27 @@ def run_cli(argv: list, config: dict | None = None) -> int:
986
992
  return 1
987
993
 
988
994
  print(f"⚓ Drydock swarm — {args.agents} agents on: {objective}")
989
- print(" (isolated git worktrees, independent verification, evidence-based judging)\n")
995
+ print(" (isolated git worktrees, independent verification, evidence-based judging)"
996
+ + (f"\n 🔗 shared communication ON — {args.waves} waves, peers' notes cross the "
997
+ "blackboard\n" if args.share else "\n"))
998
+
999
+ def _on(kind: str, d: dict) -> None:
1000
+ if kind == "share":
1001
+ print(f" 🔗 wave {d.get('wave')}: {d.get('agents')} agent(s) reading peers' notes")
1002
+ elif kind == "worker_done":
1003
+ print(f" · {d.get('agent')}: {'ok' if d.get('ok') else 'failed'}"
1004
+ + (f" ({d.get('files')} files)" if d.get('files') else ""))
1005
+ elif kind == "verified":
1006
+ print(f" ✓ {d.get('candidate')}: {d.get('passed')}/{d.get('total')} ({d.get('status')})")
1007
+ elif kind == "judge":
1008
+ print(f" ⚖ winner={d.get('agent') or '—'} converged={d.get('converged')}")
1009
+
990
1010
  try:
991
1011
  res = run_swarm(cwd, objective, agents=args.agents, base_config=config,
992
1012
  base_ref=args.base_ref, verify_cmd=args.verify, fitness=args.fitness,
993
1013
  max_turns=args.max_turns, max_tool_calls=args.max_tool_calls,
994
- max_workers=args.max_workers)
1014
+ max_workers=args.max_workers, share=args.share, waves=args.waves,
1015
+ on_event=_on)
995
1016
  except ValueError as e:
996
1017
  print(f"Error: {e}")
997
1018
  return 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.1.33
3
+ Version: 3.1.35
4
4
  Summary: Drydock — a local, provider-agnostic terminal coding agent for local LLMs
5
5
  Author: Frank Bobe III
6
6
  License-Expression: Apache-2.0
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
  # PyPI distribution name is drydock-cli (the established install name, continued
8
8
  # from the retired v2 fork); the import package + CLI command stay `drydock`.
9
9
  name = "drydock-cli"
10
- version = "3.1.33"
10
+ version = "3.1.35"
11
11
  description = "Drydock — a local, provider-agnostic terminal coding agent for local LLMs"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.11"
@@ -107,6 +107,25 @@ def test_cli_create_persists_integrity_and_noise_flags(tmp_path):
107
107
  assert cfg["eval_samples"] == 3 and cfg["noise_band"] == 1.5
108
108
 
109
109
 
110
+ def test_cli_swarm_flag_persists_and_selects_swarm_worker(tmp_path, monkeypatch):
111
+ rc = C.run_cli(["create", "fix", "it", "--swarm-agents", "5", "--verify", "true"],
112
+ {"cwd": str(tmp_path)})
113
+ assert rc == 0
114
+ mid = M.list_missions(tmp_path)[0]
115
+ cfg = M.open_store(tmp_path, mid).get_mission(mid)["config"]
116
+ assert cfg["swarm"] is True and cfg["swarm_agents"] == 5 # persisted to mission config
117
+
118
+ # _run must pick make_swarm_worker (not default_worker) when swarm is set
119
+ captured = {}
120
+ monkeypatch.setattr(R, "make_swarm_worker",
121
+ lambda **kw: captured.update(kw) or (lambda *a: R.WorkerResult(ok=True)))
122
+ monkeypatch.setattr(R, "run_mission", lambda *a, **kw: captured.update(worker_set=True) or
123
+ R.RunSummary(mission_id=mid, status="COMPLETED", cycles=0, final_metric=100.0))
124
+ monkeypatch.setattr(R, "establish_baseline", lambda *a, **k: None)
125
+ C.run_cli(["run", mid], {"cwd": str(tmp_path)})
126
+ assert captured.get("agents") == 5 and captured.get("share") is True # swarm worker built with N=5
127
+
128
+
110
129
  def test_cli_knowledge_view(tmp_path, capsys):
111
130
  C.run_cli(["create", "obj here"], {"cwd": str(tmp_path)})
112
131
  mid = M.list_missions(tmp_path)[0]
@@ -275,6 +275,30 @@ def test_negative_knowledge_is_discriminating_by_target(tmp_path):
275
275
  assert hit and "test_roman" in hit[0]["statement"] and "roman.py" in hit[0]["statement"]
276
276
 
277
277
 
278
+ def test_swarm_worker_applies_winner_to_mission_tree(tmp_path):
279
+ """ASR Phase-4: a mission task solved by a SWARM; the winning candidate lands in the
280
+ mission's working tree so KEEP/REVERT scores it. Injected runner — no model."""
281
+ from drydock import swarm as S
282
+ repo = tmp_path / "proj"
283
+ (repo / "tests").mkdir(parents=True)
284
+ (repo / "toolkit.py").write_text("def add(a, b):\n raise NotImplementedError\n")
285
+ (repo / "tests" / "test_it.py").write_text("from toolkit import add\ndef test_add(): assert add(2,3)==5\n")
286
+ (repo / "conftest.py").write_text("")
287
+ for c in (["git", "init", "-q"], ["git", "config", "user.email", "t@t"],
288
+ ["git", "config", "user.name", "t"], ["git", "add", "-A"], ["git", "commit", "-qm", "base"]):
289
+ subprocess.run(c, cwd=repo, check=True)
290
+
291
+ def runner(objective, cwd, base_config, system_prompt, allow, mt, mtc):
292
+ (Path(cwd) / "toolkit.py").write_text("def add(a, b):\n return a + b\n") # the fix
293
+ return S.RunResult(summary="implemented add", in_tokens=1, out_tokens=1, turns=1)
294
+
295
+ worker = R.make_swarm_worker(agents=2, share=False, runner=runner)
296
+ wr = worker({"objective": "make add() work"},
297
+ {"config": {"verify_cmd": "python -m pytest -q"}}, str(repo), {})
298
+ assert wr.ok and "winner" in wr.summary # swarm found + reported a winner
299
+ assert "return a + b" in (repo / "toolkit.py").read_text() # winner applied to the mission tree
300
+
301
+
278
302
  def test_run_mission_stops_on_success(tmp_path):
279
303
  repo = _repo(tmp_path)
280
304
  s = M.MissionStore(tmp_path / "s.db")
@@ -313,6 +313,26 @@ def test_share_injects_peer_notes_into_later_waves(tmp_path):
313
313
  assert any("agent-1" in s and "SOLVED" in s for s in seen)
314
314
 
315
315
 
316
+ def test_peer_notes_rank_and_flag_strongest_partial():
317
+ from drydock.swarm import Candidate, _peer_notes
318
+ cands = [
319
+ Candidate(id="c1", agent="agent-1", summary="tried A", tests_passed=1, tests_total=6),
320
+ Candidate(id="c2", agent="agent-2", summary="tried B", tests_passed=4, tests_total=6),
321
+ Candidate(id="c3", agent="agent-3", summary="tried C", tests_passed=0, tests_total=6),
322
+ ]
323
+ notes = _peer_notes(cands)
324
+ first_line = notes.splitlines()[0]
325
+ assert "agent-2" in first_line and "EXTEND this approach" in first_line # strongest partial, ranked first + flagged
326
+ assert notes.index("agent-2") < notes.index("agent-1") < notes.index("agent-3") # ranked by score
327
+ assert "EXTEND" not in notes.split("agent-1")[1].split("\n")[0] # only the leader is flagged
328
+
329
+
330
+ def test_peer_notes_no_flag_when_already_solved():
331
+ from drydock.swarm import Candidate, _peer_notes
332
+ notes = _peer_notes([Candidate(id="c1", agent="a1", summary="done", tests_passed=6, tests_total=6)])
333
+ assert "SOLVED" in notes and "EXTEND" not in notes # nothing to extend if it's fully solved
334
+
335
+
316
336
  def test_no_share_means_no_peer_notes(tmp_path):
317
337
  import threading
318
338
  repo = _init_repo(tmp_path / "repo")
@@ -398,6 +418,30 @@ def test_cli_solve_requires_git_repo(tmp_path, capsys):
398
418
  assert "git" in capsys.readouterr().out.lower()
399
419
 
400
420
 
421
+ def test_cli_share_flag_reaches_run_swarm(tmp_path, monkeypatch):
422
+ repo = _init_repo(tmp_path / "repo")
423
+ captured = {}
424
+
425
+ def fake_run_swarm(cwd, objective, **kw):
426
+ captured.update(objective=objective, **kw)
427
+ return swarm.SwarmResult(swarm_id="s", objective=objective, root="r",
428
+ converged=False, winner=None, candidates=[])
429
+ monkeypatch.setattr(swarm, "run_swarm", fake_run_swarm)
430
+ rc = swarm.run_cli(["fix the bug", "--agents", "4", "--share", "--waves", "3"], {"cwd": repo})
431
+ assert rc == 0
432
+ assert captured["share"] is True and captured["waves"] == 3 # cross-pollination reachable via CLI
433
+
434
+
435
+ def test_cli_default_is_blind_parallel(tmp_path, monkeypatch):
436
+ repo = _init_repo(tmp_path / "repo")
437
+ captured = {}
438
+ monkeypatch.setattr(swarm, "run_swarm",
439
+ lambda cwd, objective, **kw: captured.update(kw) or
440
+ swarm.SwarmResult("s", objective, "r", False, None, []))
441
+ swarm.run_cli(["fix it"], {"cwd": repo})
442
+ assert captured["share"] is False # default unchanged (blind parallel)
443
+
444
+
401
445
  def test_cli_solve_empty_objective(tmp_path):
402
446
  repo = _init_repo(tmp_path / "repo")
403
447
  assert swarm.run_cli([], {"cwd": repo}) == 1
File without changes
File without changes
File without changes
File without changes