drydock-cli 3.1.32__tar.gz → 3.1.34__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.32/drydock_cli.egg-info → drydock_cli-3.1.34}/PKG-INFO +1 -1
  2. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/advisor.py +27 -10
  3. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/swarm.py +31 -10
  4. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tools/__init__.py +3 -8
  5. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tui/app.py +3 -0
  6. {drydock_cli-3.1.32 → drydock_cli-3.1.34/drydock_cli.egg-info}/PKG-INFO +1 -1
  7. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/pyproject.toml +1 -1
  8. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_advisor.py +26 -24
  9. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_swarm.py +44 -0
  10. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/LICENSE +0 -0
  11. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/NOTICE +0 -0
  12. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/README.md +0 -0
  13. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/__init__.py +0 -0
  14. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/__main__.py +0 -0
  15. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/agent.py +0 -0
  16. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/asr.py +0 -0
  17. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/bash_safety.py +0 -0
  18. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/bottleneck.py +0 -0
  19. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/budget.py +0 -0
  20. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/__init__.py +0 -0
  21. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/document-canvas.md +0 -0
  22. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/fiar-assess.md +0 -0
  23. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/fiar-cap.md +0 -0
  24. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/fiar-evidence.md +0 -0
  25. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/fiar-readiness.md +0 -0
  26. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/ml-data.md +0 -0
  27. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/ml-debug.md +0 -0
  28. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/ml-finetune.md +0 -0
  29. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/ml-metrics.md +0 -0
  30. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/ml-rl.md +0 -0
  31. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/ml-train.md +0 -0
  32. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/nist-ai-rmf.md +0 -0
  33. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/nist-csf.md +0 -0
  34. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/rmf-categorize.md +0 -0
  35. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/rmf-control.md +0 -0
  36. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/rmf-poam.md +0 -0
  37. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/rmf-review.md +0 -0
  38. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/stig-assess.md +0 -0
  39. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/builtin_skills/stig-remediate.md +0 -0
  40. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/capacity.py +0 -0
  41. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/cci.py +0 -0
  42. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/cli.py +0 -0
  43. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/comms/__init__.py +0 -0
  44. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/comms/attention.py +0 -0
  45. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/comms/channels.py +0 -0
  46. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/comms/events.py +0 -0
  47. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/compaction.py +0 -0
  48. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/config.py +0 -0
  49. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/detect.py +0 -0
  50. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/doccanvas.py +0 -0
  51. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/eratchet.py +0 -0
  52. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/events.py +0 -0
  53. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/extract.py +0 -0
  54. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/fiar.py +0 -0
  55. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/gittools.py +0 -0
  56. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/graphrag.py +0 -0
  57. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/groundtruth.py +0 -0
  58. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/guards.py +0 -0
  59. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/jobs.py +0 -0
  60. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/loop_detect.py +0 -0
  61. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/mcp.py +0 -0
  62. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/mission.py +0 -0
  63. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/mission_cli.py +0 -0
  64. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/mission_run.py +0 -0
  65. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/pdfredbox.py +0 -0
  66. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/phases.py +0 -0
  67. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/poam.py +0 -0
  68. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/predictions.py +0 -0
  69. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/progress.py +0 -0
  70. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/providers.py +0 -0
  71. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/ratchet.py +0 -0
  72. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/recipes.py +0 -0
  73. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/recovery.py +0 -0
  74. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/resume.py +0 -0
  75. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/rmf.py +0 -0
  76. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/rmf_graph.py +0 -0
  77. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/skills.py +0 -0
  78. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/stig.py +0 -0
  79. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/subagents.py +0 -0
  80. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/suggest.py +0 -0
  81. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/task_state.py +0 -0
  82. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tool_policy.py +0 -0
  83. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tool_registry.py +0 -0
  84. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tool_result.py +0 -0
  85. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tool_select.py +0 -0
  86. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tool_validate.py +0 -0
  87. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/trajectory.py +0 -0
  88. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tui/__init__.py +0 -0
  89. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tui/approval.py +0 -0
  90. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tui/messages.py +0 -0
  91. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tui/widgets.py +0 -0
  92. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/tuning.py +0 -0
  93. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/verification.py +0 -0
  94. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock/web.py +0 -0
  95. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock_cli.egg-info/SOURCES.txt +0 -0
  96. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock_cli.egg-info/dependency_links.txt +0 -0
  97. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock_cli.egg-info/entry_points.txt +0 -0
  98. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock_cli.egg-info/requires.txt +0 -0
  99. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/drydock_cli.egg-info/top_level.txt +0 -0
  100. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/setup.cfg +0 -0
  101. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_approval.py +0 -0
  102. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_asr.py +0 -0
  103. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_back_command.py +0 -0
  104. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_background.py +0 -0
  105. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_binary_output.py +0 -0
  106. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_crossplatform.py +0 -0
  107. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_output_bounding.py +0 -0
  108. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_process_group.py +0 -0
  109. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_safety.py +0 -0
  110. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_sanitize.py +0 -0
  111. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_shell.py +0 -0
  112. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_stdin.py +0 -0
  113. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_stop_partial.py +0 -0
  114. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_timeout_network.py +0 -0
  115. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bash_timeout_param.py +0 -0
  116. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_bottleneck.py +0 -0
  117. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_budget.py +0 -0
  118. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_capacity.py +0 -0
  119. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_cci.py +0 -0
  120. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_cli_agents.py +0 -0
  121. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_comms.py +0 -0
  122. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_compact_command.py +0 -0
  123. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_compaction.py +0 -0
  124. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_config.py +0 -0
  125. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_config_migration.py +0 -0
  126. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_context_limit_config.py +0 -0
  127. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_context_limit_issue25.py +0 -0
  128. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_criteria_coverage.py +0 -0
  129. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_cycling_detection.py +0 -0
  130. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_declared_deps.py +0 -0
  131. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_degenerate_argument.py +0 -0
  132. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_detect.py +0 -0
  133. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_dispatch.py +0 -0
  134. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_doccanvas.py +0 -0
  135. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_e2e_connected.py +0 -0
  136. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_edit_replace_all.py +0 -0
  137. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_edit_thrash_and_compaction.py +0 -0
  138. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_effort_governor.py +0 -0
  139. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_empty_response.py +0 -0
  140. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_eratchet.py +0 -0
  141. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_events.py +0 -0
  142. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_extract.py +0 -0
  143. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_failure_loop.py +0 -0
  144. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_fiar.py +0 -0
  145. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_first_run_setup.py +0 -0
  146. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_gittools.py +0 -0
  147. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_glob_edit_edges.py +0 -0
  148. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_graphify_example.py +0 -0
  149. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_graphrag.py +0 -0
  150. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_graphrag_quoted_path.py +0 -0
  151. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_graphrag_sqlite.py +0 -0
  152. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_grep_and_read_robust.py +0 -0
  153. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_groundtruth_predictions.py +0 -0
  154. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_guards_and_tools.py +0 -0
  155. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_hallucinated_tools.py +0 -0
  156. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_jobs.py +0 -0
  157. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_knowledge_tool_available.py +0 -0
  158. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_leaked_tool_call.py +0 -0
  159. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_loop_detect.py +0 -0
  160. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_main_api_key.py +0 -0
  161. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_mcp.py +0 -0
  162. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_mission.py +0 -0
  163. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_mission_cli.py +0 -0
  164. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_mission_run.py +0 -0
  165. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_model_registry.py +0 -0
  166. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_oneshot_unreachable.py +0 -0
  167. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_overthink_interrupt.py +0 -0
  168. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_pdfredbox.py +0 -0
  169. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_phases.py +0 -0
  170. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_plan_autocontinue.py +0 -0
  171. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_poam.py +0 -0
  172. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_progress.py +0 -0
  173. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_providers_unreachable.py +0 -0
  174. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_ratchet.py +0 -0
  175. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_ratchet_offer.py +0 -0
  176. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_read_index.py +0 -0
  177. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_recipes.py +0 -0
  178. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_recovery.py +0 -0
  179. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_recovery_config.py +0 -0
  180. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_recovery_integration.py +0 -0
  181. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_repeated_outcome.py +0 -0
  182. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_repetition_interrupt.py +0 -0
  183. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_resume_events.py +0 -0
  184. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_resume_restore.py +0 -0
  185. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_resume_snapshot.py +0 -0
  186. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_rmf.py +0 -0
  187. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_rmf_graph.py +0 -0
  188. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_rmf_stig_graph.py +0 -0
  189. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_rolling_plan.py +0 -0
  190. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_runaway_repetition.py +0 -0
  191. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_screenshot.py +0 -0
  192. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_server_probe.py +0 -0
  193. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_skills.py +0 -0
  194. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_sqlite_events.py +0 -0
  195. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_stall_retry.py +0 -0
  196. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_stig.py +0 -0
  197. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_stop.py +0 -0
  198. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_streaming_newlines.py +0 -0
  199. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_subagent.py +0 -0
  200. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_subagents.py +0 -0
  201. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_suggest.py +0 -0
  202. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_system_prompt_help.py +0 -0
  203. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_task_state.py +0 -0
  204. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_timeline.py +0 -0
  205. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_todo.py +0 -0
  206. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_arg_coercion.py +0 -0
  207. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_arg_coercion_more.py +0 -0
  208. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_arg_parsing.py +0 -0
  209. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_canonical.py +0 -0
  210. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_policy.py +0 -0
  211. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_result.py +0 -0
  212. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_select.py +0 -0
  213. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tool_validate.py +0 -0
  214. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tools_undo.py +0 -0
  215. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_trajectory.py +0 -0
  216. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tui.py +0 -0
  217. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_tuning.py +0 -0
  218. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_verification_gate.py +0 -0
  219. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_viewimage.py +0 -0
  220. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_vision_input.py +0 -0
  221. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_web_tools.py +0 -0
  222. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_windows_shell.py +0 -0
  223. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_worker_subagent.py +0 -0
  224. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/tests/test_write_content_coerce.py +0 -0
  225. {drydock_cli-3.1.32 → drydock_cli-3.1.34}/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.32
3
+ Version: 3.1.34
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
@@ -16,11 +16,14 @@ from __future__ import annotations
16
16
 
17
17
  _ADVISOR_SYSTEM = (
18
18
  "You are a senior engineering advisor consulted by another AI coding agent "
19
- "(Drydock) running a smaller local model. Give concise, direct, expert advice "
20
- "— the specific fix, the design trade-off, or the likely root cause. Prefer "
21
- "concrete steps and short code over prose. If the question is under-specified, "
22
- "state the key assumption and answer anyway. You cannot run tools; the calling "
23
- "agent will act on your answer."
19
+ "(Drydock) running a smaller local model. You are given a BRIEF (recent activity, "
20
+ "errors, tool output, and any context the agent attached) followed by a QUESTION — "
21
+ "treat it like a task handed to you with its background. Answer FROM the brief: give "
22
+ "concise, direct, expert advice — the specific fix, the design trade-off, or the likely "
23
+ "root cause. Prefer concrete steps and short code over prose. If the brief is thin or the "
24
+ "question is under-specified, STATE your best assumption and answer anyway — do NOT reply "
25
+ "that you have no information to review. You cannot run tools; the calling agent acts on "
26
+ "your answer."
24
27
  )
25
28
 
26
29
 
@@ -82,9 +85,8 @@ def _call(question: str, config: dict, *, context: str = "",
82
85
  key = (config.get("advisor_api_key") or "").strip() or "dummy"
83
86
  messages: list[dict] = [{"role": "system", "content": _ADVISOR_SYSTEM}]
84
87
  if context.strip():
85
- messages.append({"role": "user",
86
- "content": f"Context from the current task:\n{context.strip()}"})
87
- messages.append({"role": "user", "content": question})
88
+ messages.append({"role": "user", "content": f"=== BRIEF ===\n{context.strip()}"})
89
+ messages.append({"role": "user", "content": f"=== QUESTION ===\n{question}"})
88
90
  kwargs = {"model": model, "messages": messages, "temperature": 0.3, "max_tokens": max_tokens}
89
91
  client = OpenAI(api_key=key, base_url=base, max_retries=1,
90
92
  timeout=httpx.Timeout(timeout, connect=10.0))
@@ -92,16 +94,31 @@ def _call(question: str, config: dict, *, context: str = "",
92
94
  return (resp.choices[0].message.content or "").strip()
93
95
 
94
96
 
97
+ def build_brief(config: dict, context: str = "") -> str:
98
+ """Assemble the advisor's brief (§ Consult): the recent transcript the agent stashed in
99
+ config PLUS any context the caller passed. Centralized here so BOTH the `Consult` tool and
100
+ the `/ask` command send a self-contained, sub-agent-style brief — not a bare question."""
101
+ parts = []
102
+ recent = recent_context(config.get("_recent_messages") or [])
103
+ if recent:
104
+ parts.append("RECENT ACTIVITY (agent's reasoning, tool output, errors):\n" + recent)
105
+ if (context or "").strip():
106
+ parts.append("CONTEXT:\n" + context.strip())
107
+ return "\n\n".join(parts)
108
+
109
+
95
110
  def consult(question: str, config: dict, *, context: str = "", timeout: float = 120.0) -> str:
96
111
  """Ask the configured advisor model and return its answer (or a clean error).
97
- Never raises — a failed consult must not crash the turn."""
112
+ Never raises — a failed consult must not crash the turn. Auto-briefs the advisor with the
113
+ recent transcript so it's informed even when the caller passed no context."""
98
114
  question = (question or "").strip()
99
115
  if not question:
100
116
  return "Error: nothing to ask the advisor."
101
117
  if not is_configured(config):
102
118
  return not_configured_message()
119
+ brief = build_brief(config, context)
103
120
  try:
104
- return _call(question, config, context=context, timeout=timeout) \
121
+ return _call(question, config, context=brief, timeout=timeout) \
105
122
  or "(the advisor model returned an empty response)"
106
123
  except Exception as e: # noqa: BLE001 — never crash the caller
107
124
  return (f"Could not reach the advisor model ({config.get('advisor_model')}) at "
@@ -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
@@ -2054,14 +2054,9 @@ def tool_consult(params: dict, config: dict) -> str:
2054
2054
  question = _as_str_arg(params.get("question") or params.get("prompt")).strip()
2055
2055
  if not question:
2056
2056
  return "Error: Consult needs a `question`."
2057
- ctx = _as_str_arg(params.get("context") or "").strip()
2058
- # Auto-enrich: the advisor is useless without knowing what's going on, and the calling
2059
- # model often passes thin/no context. Attach the recent transcript (errors + reasoning)
2060
- # that the agent stashed in config so the advisor always has real information to work with.
2061
- auto = advisor.recent_context(config.get("_recent_messages") or [])
2062
- if auto:
2063
- ctx = (ctx + "\n\n" if ctx else "") + "Recent activity (auto-attached):\n" + auto
2064
- return advisor.consult(question, config, context=ctx)
2057
+ # consult() now auto-briefs the advisor with the recent transcript (from config), so both
2058
+ # this tool and /ask send a self-contained brief — just pass the model's extra context.
2059
+ return advisor.consult(question, config, context=_as_str_arg(params.get("context") or ""))
2065
2060
 
2066
2061
 
2067
2062
  # A sub-agent's whole job is to keep its investigation OUT of the main agent's
@@ -1565,6 +1565,9 @@ class DrydockApp(App):
1565
1565
 
1566
1566
  def _ask_worker(self, question: str, inject: bool = False) -> None:
1567
1567
  from drydock import advisor
1568
+ # Brief the advisor with the live transcript (same as the Consult tool) so /ask isn't
1569
+ # a context-free question — the advisor sees the recent reasoning/errors.
1570
+ self.config["_recent_messages"] = self.state.messages
1568
1571
  answer = advisor.consult(question, self.config)
1569
1572
  self.call_from_thread(self._deliver_advice, question, answer, inject)
1570
1573
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.1.32
3
+ Version: 3.1.34
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.32"
10
+ version = "3.1.34"
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"
@@ -115,31 +115,33 @@ def test_recent_context_handles_multimodal_blocks():
115
115
  assert "look at this" in advisor.recent_context(msgs)
116
116
 
117
117
 
118
- def test_consult_tool_auto_attaches_recent_transcript(monkeypatch):
119
- from drydock import tools as T
118
+ def test_build_brief_combines_transcript_and_context():
119
+ cfg = {"_recent_messages": [
120
+ {"role": "tool", "content": "FAILED test_reconnect - state lost after reconnect"},
121
+ {"role": "assistant", "content": "Trying a lock around the queue."},
122
+ ]}
123
+ brief = advisor.build_brief(cfg, "my hand-written context")
124
+ assert "FAILED test_reconnect" in brief and "lock around the queue" in brief
125
+ assert "my hand-written context" in brief
126
+ assert "RECENT ACTIVITY" in brief and "CONTEXT" in brief
127
+
128
+
129
+ def test_consult_briefs_advisor_from_transcript_even_with_no_context(monkeypatch):
130
+ # the whole point: a bare question still reaches the advisor WITH the recent transcript
120
131
  captured = {}
121
- monkeypatch.setattr(advisor, "consult",
122
- lambda q, cfg, context="": captured.update(question=q, context=context) or "ok")
123
- cfg = {
124
- "advisor_base_url": "http://b/v1", "advisor_model": "m",
125
- "_recent_messages": [
126
- {"role": "tool", "content": "FAILED test_reconnect - state lost after reconnect"},
127
- {"role": "assistant", "content": "Trying a lock around the queue."},
128
- ],
129
- }
130
- # model passed only a bare question (thin context) — the fix must attach the transcript
131
- T.tool_consult({"question": "why does reconnect lose state?"}, cfg)
132
- assert "FAILED test_reconnect" in captured["context"] # advisor now sees the error
133
- assert "lock around the queue" in captured["context"]
134
- assert "Recent activity" in captured["context"]
135
-
136
-
137
- def test_consult_tool_keeps_model_context_and_adds_recent(monkeypatch):
132
+ monkeypatch.setattr(advisor, "_call",
133
+ lambda q, cfg, context="", **k: captured.update(q=q, context=context) or "ok")
134
+ cfg = {"advisor_base_url": "http://b/v1", "advisor_model": "m",
135
+ "_recent_messages": [{"role": "tool", "content": "FAILED test_x - AssertionError"}]}
136
+ advisor.consult("why is this failing?", cfg) # no context passed
137
+ assert "FAILED test_x" in captured["context"] # transcript auto-attached
138
+ assert captured["q"] == "why is this failing?"
139
+
140
+
141
+ def test_consult_tool_delegates_to_consult(monkeypatch):
138
142
  from drydock import tools as T
139
143
  captured = {}
140
144
  monkeypatch.setattr(advisor, "consult",
141
- lambda q, cfg, context="": captured.update(context=context) or "ok")
142
- cfg = {"advisor_base_url": "http://b/v1", "advisor_model": "m",
143
- "_recent_messages": [{"role": "tool", "content": "ERR: boom"}]}
144
- T.tool_consult({"question": "help", "context": "my hand-written context"}, cfg)
145
- assert "my hand-written context" in captured["context"] and "ERR: boom" in captured["context"]
145
+ lambda q, cfg, context="": captured.update(q=q, context=context) or "ok")
146
+ T.tool_consult({"question": "help", "context": "hand-written"}, {"advisor_model": "m"})
147
+ assert captured["q"] == "help" and captured["context"] == "hand-written"
@@ -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