drydock-cli 0.7.2__tar.gz → 0.7.4__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 (462) hide show
  1. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/PKG-INFO +1 -1
  2. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/PRD.md +14 -0
  3. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/agent_loop.py +42 -2
  4. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/search_replace.py +6 -3
  5. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/pyproject.toml +1 -1
  6. drydock_cli-0.7.4/scripts/backup.sh +84 -0
  7. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/scripts/deploy_to_github.sh +1 -1
  8. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/scripts/publish_to_pypi.sh +1 -1
  9. drydock_cli-0.7.4/tests/test_real_failures.py +95 -0
  10. drydock_cli-0.7.4/tests/test_real_issues.py +252 -0
  11. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/CODEOWNERS +0 -0
  12. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
  13. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  14. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  15. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/workflows/build-and-upload.yml +0 -0
  16. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/workflows/ci.yml +0 -0
  17. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/workflows/issue-labeler.yml +0 -0
  18. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.github/workflows/release.yml +0 -0
  19. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.gitignore +0 -0
  20. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.pre-commit-config.yaml +0 -0
  21. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.python-version +0 -0
  22. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.typos.toml +0 -0
  23. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.vscode/extensions.json +0 -0
  24. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.vscode/launch.json +0 -0
  25. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/.vscode/settings.json +0 -0
  26. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/AGENTS.md +0 -0
  27. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/CHANGELOG.md +0 -0
  28. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/CLAUDE.md +0 -0
  29. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/CONTRIBUTING.md +0 -0
  30. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/Drydock_rebrand.md +0 -0
  31. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/LICENSE +0 -0
  32. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/NOTICE +0 -0
  33. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/README.md +0 -0
  34. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/action.yml +0 -0
  35. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/distribution/zed/LICENSE +0 -0
  36. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/distribution/zed/extension.toml +0 -0
  37. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/distribution/zed/icons/mistral_vibe.svg +0 -0
  38. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/docs/README.md +0 -0
  39. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/docs/acp-setup.md +0 -0
  40. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/docs/proxy-setup.md +0 -0
  41. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/__init__.py +0 -0
  42. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/__init__.py +0 -0
  43. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/acp_agent_loop.py +0 -0
  44. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/acp_logger.py +0 -0
  45. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/entrypoint.py +0 -0
  46. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/__init__.py +0 -0
  47. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/base.py +0 -0
  48. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/builtins/bash.py +0 -0
  49. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/builtins/read_file.py +0 -0
  50. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/builtins/search_replace.py +0 -0
  51. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/builtins/todo.py +0 -0
  52. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/builtins/write_file.py +0 -0
  53. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/tools/session_update.py +0 -0
  54. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/acp/utils.py +0 -0
  55. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/__init__.py +0 -0
  56. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/autocompletion/__init__.py +0 -0
  57. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/autocompletion/base.py +0 -0
  58. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/autocompletion/path_completion.py +0 -0
  59. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/autocompletion/slash_command.py +0 -0
  60. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/cli.py +0 -0
  61. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/clipboard.py +0 -0
  62. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/commands.py +0 -0
  63. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/entrypoint.py +0 -0
  64. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/history_manager.py +0 -0
  65. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
  66. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
  67. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
  68. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/terminal_setup.py +0 -0
  69. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/__init__.py +0 -0
  70. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
  71. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/app.py +0 -0
  72. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/app.tcss +0 -0
  73. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/external_editor.py +0 -0
  74. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
  75. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
  76. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
  77. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
  78. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
  79. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
  80. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
  81. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
  82. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
  83. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
  84. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
  85. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
  86. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
  87. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
  88. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
  89. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
  90. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
  91. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
  92. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/compact.py +0 -0
  93. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
  94. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
  95. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
  96. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/loading.py +0 -0
  97. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/messages.py +0 -0
  98. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
  99. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
  100. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
  101. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
  102. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
  103. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
  104. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
  105. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
  106. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
  107. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/tools.py +0 -0
  108. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
  109. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
  110. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/windowing/history.py +0 -0
  111. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
  112. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/textual_ui/windowing/state.py +0 -0
  113. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/__init__.py +0 -0
  114. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
  115. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
  116. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
  117. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
  118. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
  119. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/update.py +0 -0
  120. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/cli/update_notifier/whats_new.py +0 -0
  121. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/__init__.py +0 -0
  122. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/agents/__init__.py +0 -0
  123. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/agents/manager.py +0 -0
  124. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/agents/models.py +0 -0
  125. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/auth/__init__.py +0 -0
  126. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/auth/crypto.py +0 -0
  127. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/auth/github.py +0 -0
  128. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/__init__.py +0 -0
  129. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/completers.py +0 -0
  130. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
  131. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
  132. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
  133. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/file_indexer/store.py +0 -0
  134. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
  135. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/fuzzy.py +0 -0
  136. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/path_prompt.py +0 -0
  137. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
  138. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/config/__init__.py +0 -0
  139. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/config/_settings.py +0 -0
  140. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/config/harness_files/__init__.py +0 -0
  141. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/config/harness_files/_harness_manager.py +0 -0
  142. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/config/harness_files/_paths.py +0 -0
  143. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/consultant.py +0 -0
  144. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/drydock_states.py +0 -0
  145. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/__init__.py +0 -0
  146. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/backend/anthropic.py +0 -0
  147. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/backend/base.py +0 -0
  148. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/backend/factory.py +0 -0
  149. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/backend/generic.py +0 -0
  150. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/backend/mistral.py +0 -0
  151. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
  152. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/backend/vertex.py +0 -0
  153. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/exceptions.py +0 -0
  154. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/format.py +0 -0
  155. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/message_utils.py +0 -0
  156. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/llm/types.py +0 -0
  157. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/logger.py +0 -0
  158. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/middleware.py +0 -0
  159. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/output_formatters.py +0 -0
  160. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/paths/__init__.py +0 -0
  161. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/paths/_local_config_walk.py +0 -0
  162. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/paths/_vibe_home.py +0 -0
  163. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/paths/conventions.py +0 -0
  164. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/plan_session.py +0 -0
  165. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/programmatic.py +0 -0
  166. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/__init__.py +0 -0
  167. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/cli.md +0 -0
  168. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/compact.md +0 -0
  169. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/dangerous_directory.md +0 -0
  170. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/diagnostic.md +0 -0
  171. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/explore.md +0 -0
  172. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/planner.md +0 -0
  173. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/project_context.md +0 -0
  174. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/prompts/tests.md +0 -0
  175. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/proxy_setup.py +0 -0
  176. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/session/session_loader.py +0 -0
  177. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/session/session_logger.py +0 -0
  178. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/session/session_migration.py +0 -0
  179. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/session/state_file.py +0 -0
  180. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/skills/__init__.py +0 -0
  181. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/skills/manager.py +0 -0
  182. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/skills/models.py +0 -0
  183. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/skills/parser.py +0 -0
  184. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/slug.py +0 -0
  185. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/system_prompt.py +0 -0
  186. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/telemetry/__init__.py +0 -0
  187. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/telemetry/send.py +0 -0
  188. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/teleport/errors.py +0 -0
  189. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/teleport/git.py +0 -0
  190. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/teleport/nuage.py +0 -0
  191. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/teleport/teleport.py +0 -0
  192. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/teleport/types.py +0 -0
  193. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/base.py +0 -0
  194. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/ask_user_question.py +0 -0
  195. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/bash.py +0 -0
  196. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
  197. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/grep.py +0 -0
  198. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
  199. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
  200. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/bash.md +0 -0
  201. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/grep.md +0 -0
  202. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
  203. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
  204. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
  205. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/task.md +0 -0
  206. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/todo.md +0 -0
  207. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
  208. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
  209. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
  210. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/read_file.py +0 -0
  211. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/task.py +0 -0
  212. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/todo.py +0 -0
  213. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/webfetch.py +0 -0
  214. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/websearch.py +0 -0
  215. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/builtins/write_file.py +0 -0
  216. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/injection_guard.py +0 -0
  217. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/manager.py +0 -0
  218. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/mcp/__init__.py +0 -0
  219. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/mcp/registry.py +0 -0
  220. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/mcp/tools.py +0 -0
  221. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/mcp_sampling.py +0 -0
  222. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/ui.py +0 -0
  223. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/tools/utils.py +0 -0
  224. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/trusted_folders.py +0 -0
  225. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/types.py +0 -0
  226. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/core/utils.py +0 -0
  227. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/onboarding/__init__.py +0 -0
  228. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/onboarding/base.py +0 -0
  229. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/onboarding/onboarding.tcss +0 -0
  230. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/onboarding/screens/__init__.py +0 -0
  231. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/onboarding/screens/api_key.py +0 -0
  232. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/onboarding/screens/welcome.py +0 -0
  233. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
  234. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
  235. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/skills/__init__.py +0 -0
  236. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/skills/create-presentation/SKILL.md +0 -0
  237. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/skills/deep-research/SKILL.md +0 -0
  238. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock/whats_new.md +0 -0
  239. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock-acp.spec +0 -0
  240. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/drydock_terms.md +0 -0
  241. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/flake.lock +0 -0
  242. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/flake.nix +0 -0
  243. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/scripts/README.md +0 -0
  244. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/scripts/bump_version.py +0 -0
  245. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/scripts/install.sh +0 -0
  246. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/scripts/prepare_release.py +0 -0
  247. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/__init__.py +0 -0
  248. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/conftest.py +0 -0
  249. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_acp.py +0 -0
  250. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_agent_thought.py +0 -0
  251. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_bash.py +0 -0
  252. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_compact_session_updates.py +0 -0
  253. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_content.py +0 -0
  254. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_initialize.py +0 -0
  255. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_list_sessions.py +0 -0
  256. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_load_session.py +0 -0
  257. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_multi_session.py +0 -0
  258. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_new_session.py +0 -0
  259. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_proxy_setup_acp.py +0 -0
  260. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_read_file.py +0 -0
  261. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_search_replace.py +0 -0
  262. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_set_config_option.py +0 -0
  263. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_set_mode.py +0 -0
  264. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_set_model.py +0 -0
  265. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_tool_call_session_update.py +0 -0
  266. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_utils.py +0 -0
  267. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/acp/test_write_file.py +0 -0
  268. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_file_indexer.py +0 -0
  269. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_fuzzy.py +0 -0
  270. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
  271. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_path_completer_recursive.py +0 -0
  272. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_path_completion_controller.py +0 -0
  273. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
  274. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_slash_command_controller.py +0 -0
  275. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
  276. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/__init__.py +0 -0
  277. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/data/__init__.py +0 -0
  278. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/data/fireworks.py +0 -0
  279. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/data/mistral.py +0 -0
  280. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/test_anthropic_adapter.py +0 -0
  281. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/test_backend.py +0 -0
  282. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/test_reasoning_adapter.py +0 -0
  283. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
  284. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
  285. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
  286. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
  287. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_bell_notifications.py +0 -0
  288. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_braille_renderer.py +0 -0
  289. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_clipboard.py +0 -0
  290. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_commands.py +0 -0
  291. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_copy_shortcuts.py +0 -0
  292. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_external_editor.py +0 -0
  293. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_no_markup_static.py +0 -0
  294. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_question_app.py +0 -0
  295. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_spinner.py +0 -0
  296. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_switching_mode.py +0 -0
  297. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_ui_clipboard_notifications.py +0 -0
  298. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
  299. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_ui_session_resume.py +0 -0
  300. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/test_ui_skill_dispatch.py +0 -0
  301. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/textual_ui/__init__.py +0 -0
  302. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/cli/textual_ui/test_session_picker.py +0 -0
  303. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/conftest.py +0 -0
  304. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_agents.py +0 -0
  305. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_auth_crypto.py +0 -0
  306. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_auth_github.py +0 -0
  307. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_config_load_dotenv.py +0 -0
  308. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_config_paths.py +0 -0
  309. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_config_resolution.py +0 -0
  310. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_file_logging.py +0 -0
  311. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_plan_session.py +0 -0
  312. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_proxy_setup.py +0 -0
  313. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_slug.py +0 -0
  314. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_telemetry_send.py +0 -0
  315. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_teleport_git.py +0 -0
  316. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_teleport_nuage.py +0 -0
  317. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_teleport_service.py +0 -0
  318. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_trusted_folders.py +0 -0
  319. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/core/test_utils.py +0 -0
  320. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/e2e/common.py +0 -0
  321. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/e2e/conftest.py +0 -0
  322. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/e2e/mock_server.py +0 -0
  323. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/e2e/test_cli_tui_onboarding.py +0 -0
  324. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/e2e/test_cli_tui_streaming.py +0 -0
  325. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
  326. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/mock/__init__.py +0 -0
  327. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/mock/mock_backend_factory.py +0 -0
  328. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/mock/mock_entrypoint.py +0 -0
  329. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/mock/utils.py +0 -0
  330. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/onboarding/test_run_onboarding.py +0 -0
  331. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/onboarding/test_ui_onboarding.py +0 -0
  332. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/session/test_session_loader.py +0 -0
  333. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/session/test_session_logger.py +0 -0
  334. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/session/test_session_migration.py +0 -0
  335. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/skills/conftest.py +0 -0
  336. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/skills/test_manager.py +0 -0
  337. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/skills/test_models.py +0 -0
  338. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/skills/test_parser.py +0 -0
  339. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
  340. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
  341. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
  342. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_code_block_horizontal_scrolling/test_snapshot_allows_horizontal_scrolling_for_long_code_blocks.svg +0 -0
  343. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
  344. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
  345. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
  346. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
  347. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
  348. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
  349. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
  350. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
  351. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
  352. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
  353. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
  354. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
  355. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
  356. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
  357. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
  358. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
  359. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
  360. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
  361. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
  362. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
  363. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
  364. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
  365. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
  366. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
  367. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
  368. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
  369. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
  370. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
  371. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
  372. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
  373. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
  374. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
  375. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
  376. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
  377. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
  378. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
  379. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
  380. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
  381. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
  382. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
  383. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
  384. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
  385. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
  386. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
  387. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
  388. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
  389. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
  390. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
  391. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
  392. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
  393. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
  394. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
  395. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
  396. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
  397. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
  398. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
  399. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/base_snapshot_test_app.py +0 -0
  400. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/conftest.py +0 -0
  401. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/snap_compare.py +0 -0
  402. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
  403. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
  404. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
  405. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
  406. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
  407. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
  408. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
  409. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
  410. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
  411. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
  412. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
  413. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
  414. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
  415. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
  416. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/stubs/fake_backend.py +0 -0
  417. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/stubs/fake_client.py +0 -0
  418. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/stubs/fake_tool.py +0 -0
  419. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_agent_auto_compact.py +0 -0
  420. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_agent_backend.py +0 -0
  421. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_agent_observer_streaming.py +0 -0
  422. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_agent_stats.py +0 -0
  423. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_agent_tasks.py +0 -0
  424. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_agent_tool_call.py +0 -0
  425. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_agents.py +0 -0
  426. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_cli_programmatic_preload.py +0 -0
  427. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_drydock_regression.py +0 -0
  428. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_drydock_tasks.py +0 -0
  429. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_history_manager.py +0 -0
  430. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_integration.py +0 -0
  431. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_loop_detection.py +0 -0
  432. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_message_id.py +0 -0
  433. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_message_merging.py +0 -0
  434. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_middleware.py +0 -0
  435. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_reasoning_content.py +0 -0
  436. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_system_prompt.py +0 -0
  437. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_tagged_text.py +0 -0
  438. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_ui_external_editor.py +0 -0
  439. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_ui_input_history.py +0 -0
  440. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/test_user_issues.py +0 -0
  441. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_ask_user_question.py +0 -0
  442. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_bash.py +0 -0
  443. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_exit_plan_mode.py +0 -0
  444. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_grep.py +0 -0
  445. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_invoke_context.py +0 -0
  446. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_manager_get_tool_config.py +0 -0
  447. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_mcp.py +0 -0
  448. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_mcp_sampling.py +0 -0
  449. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_task.py +0 -0
  450. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_ui_bash_execution.py +0 -0
  451. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_webfetch.py +0 -0
  452. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/tools/test_websearch.py +0 -0
  453. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
  454. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
  455. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/test_do_update.py +0 -0
  456. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
  457. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/test_github_update_gateway.py +0 -0
  458. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
  459. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/test_ui_update_notification.py +0 -0
  460. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/test_update_use_case.py +0 -0
  461. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/tests/update_notifier/test_whats_new.py +0 -0
  462. {drydock_cli-0.7.2 → drydock_cli-0.7.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 0.7.2
3
+ Version: 0.7.4
4
4
  Summary: Drydock — nautical CLI coding agent
5
5
  Project-URL: Homepage, https://github.com/fbobe321/drydock
6
6
  Project-URL: Repository, https://github.com/fbobe321/drydock
@@ -178,3 +178,17 @@ Show the user what the agent has planned in its execution pipeline.
178
178
 
179
179
  ### P4: Support more LLM backends
180
180
  Test with Claude, GPT-4, Gemini to understand which improvements are model-specific.
181
+
182
+ ### Phase 7: Real Test-Driven Fixes (Mar 26)
183
+
184
+ Shifted to test-driven development with real vLLM backend — no more mocks for critical bugs.
185
+
186
+ | Issue | Test Result (before fix) | Fix | Test Result (after fix) |
187
+ |-------|------------------------|-----|----------------------|
188
+ | Circuit breaker fires but model keeps calling | FAILED: 20 bash calls, 17 breaker fires ignored | Force-stop conversation after 3 consecutive breaker fires + break tool loop | PASSED: stops within 3 calls |
189
+
190
+ **Testing methodology changed:**
191
+ - All critical tests run against real vLLM at localhost:8000
192
+ - Tests must FAIL first (proving the bug exists)
193
+ - Then fix code, re-run until PASS
194
+ - 166 total tests (155 mock + 11 real backend)
@@ -191,6 +191,7 @@ class AgentLoop:
191
191
  # Circuit breaker: track tool call signatures to prevent exact repeats
192
192
  # Key: hash(tool_name + args), Value: (count, last_result_snippet)
193
193
  self._tool_call_history: dict[str, tuple[int, str]] = {}
194
+ self._consecutive_circuit_breaker_fires: int = 0
194
195
 
195
196
  system_prompt = get_universal_system_prompt(
196
197
  self.tool_manager, self.config, self.skill_manager, self.agent_manager
@@ -512,11 +513,16 @@ class AgentLoop:
512
513
  self.stats.steps += 1
513
514
  user_cancelled = False
514
515
  try:
516
+ force_stopped = False
515
517
  async for event in self._perform_llm_turn():
516
518
  if is_user_cancellation_event(event):
517
519
  user_cancelled = True
520
+ if isinstance(event, AssistantEvent) and event.stopped_by_middleware:
521
+ force_stopped = True
518
522
  yield event
519
523
  await self._save_messages()
524
+ if force_stopped:
525
+ return
520
526
  # Reset API error count on successful turn
521
527
  api_error_count = 0
522
528
  except (RuntimeError, AgentLoopLLMResponseError) as e:
@@ -641,7 +647,11 @@ class AgentLoop:
641
647
  should_break_loop = True
642
648
  nudge_text = None
643
649
  if nudge_text:
644
- self._inject_system_note(nudge_text)
650
+ # Inject as user message so the model actually sees it
651
+ # (inject_system_note buries it in old tool results)
652
+ self.messages.append(
653
+ LLMMessage(role=Role.user, content=nudge_text)
654
+ )
645
655
  logger.info("Model gave text without editing — nudging (attempt %d)", text_without_action)
646
656
 
647
657
  # If model has been investigating for too long without making an edit,
@@ -896,6 +906,29 @@ class AgentLoop:
896
906
  ) -> AsyncGenerator[ToolResultEvent | ToolStreamEvent]:
897
907
  # Circuit breaker: block exact duplicate calls after 2 attempts
898
908
  if blocked := self._circuit_breaker_check(tool_call):
909
+ self._consecutive_circuit_breaker_fires += 1
910
+
911
+ if self._consecutive_circuit_breaker_fires >= 3:
912
+ # Model is ignoring the circuit breaker — force stop
913
+ force_msg = (
914
+ f"FORCED STOP: You ignored the circuit breaker {self._consecutive_circuit_breaker_fires} times. "
915
+ f"The session is being terminated because you keep running the same command. "
916
+ f"You MUST try a completely different approach."
917
+ )
918
+ yield ToolResultEvent(
919
+ tool_name=tool_call.tool_name,
920
+ tool_class=tool_call.tool_class,
921
+ error=force_msg,
922
+ tool_call_id=tool_call.call_id,
923
+ )
924
+ self._handle_tool_response(tool_call, force_msg, "failure")
925
+ # Inject stop signal into messages
926
+ yield AssistantEvent(
927
+ content=f"\n\n[{force_msg}]\n",
928
+ stopped_by_middleware=True,
929
+ )
930
+ return
931
+
899
932
  yield ToolResultEvent(
900
933
  tool_name=tool_call.tool_name,
901
934
  tool_class=tool_call.tool_class,
@@ -904,6 +937,9 @@ class AgentLoop:
904
937
  )
905
938
  self._handle_tool_response(tool_call, blocked, "failure")
906
939
  return
940
+ else:
941
+ # Reset consecutive fires when a non-blocked call happens
942
+ self._consecutive_circuit_breaker_fires = 0
907
943
 
908
944
  try:
909
945
  tool_instance = self.tool_manager.get(tool_call.tool_name)
@@ -1060,10 +1096,14 @@ class AgentLoop:
1060
1096
 
1061
1097
  async def _handle_tool_calls(
1062
1098
  self, resolved: ResolvedMessage
1063
- ) -> AsyncGenerator[ToolCallEvent | ToolResultEvent | ToolStreamEvent]:
1099
+ ) -> AsyncGenerator[ToolCallEvent | ToolResultEvent | ToolStreamEvent | AssistantEvent]:
1064
1100
  async for event in self._emit_failed_tool_events(resolved.failed_calls):
1065
1101
  yield event
1066
1102
  for tool_call in resolved.tool_calls:
1103
+ # Stop processing more tool calls if circuit breaker force-stopped
1104
+ if self._consecutive_circuit_breaker_fires >= 3:
1105
+ break
1106
+
1067
1107
  yield ToolCallEvent(
1068
1108
  tool_name=tool_call.tool_name,
1069
1109
  tool_class=tool_call.tool_class,
@@ -119,12 +119,15 @@ class SearchReplace(
119
119
  ) -> AsyncGenerator[ToolStreamEvent | SearchReplaceResult, None]:
120
120
  file_path, search_replace_blocks = self._prepare_and_validate_args(args)
121
121
 
122
- # Warn (but don't block) when editing test files
122
+ # BLOCK editing test files the bug is in source code, not tests
123
123
  file_str = str(file_path)
124
124
  if ("/tests/" in file_str or "/test_" in file_str or
125
125
  file_str.endswith("_test.py") or "/testing/" in file_str):
126
- import logging
127
- logging.getLogger(__name__).warning("search_replace targeting test file: %s", file_str)
126
+ raise ToolError(
127
+ f"BLOCKED: You are trying to edit a test file ({file_path.name}). "
128
+ f"The bug is in LIBRARY SOURCE code, not tests. "
129
+ f"Use grep to find the corresponding source file and edit that instead."
130
+ )
128
131
 
129
132
  # Injection guard: scan replacement content for suspicious patterns
130
133
  from drydock.core.tools.injection_guard import check_content_for_injection
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "drydock-cli"
3
- version = "0.7.2"
3
+ version = "0.7.4"
4
4
  description = "Drydock — nautical CLI coding agent"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -0,0 +1,84 @@
1
+ #!/bin/bash
2
+ # Backup DryDock project to NAS
3
+ # Cron: 0 3 * * * /data3/drydock/scripts/backup.sh >> /data3/drydock/logs/backup.log 2>&1
4
+
5
+ set -euo pipefail
6
+
7
+ DRYDOCK_SRC="/data3/drydock"
8
+ BENCH_DIR="/data3/swe_bench_runs"
9
+ NAS_MOUNT="/mnt/nas_backups"
10
+ BACKUP_DIR="$NAS_MOUNT/drydock"
11
+ DATE=$(date +%Y%m%d)
12
+ LOGDIR="$DRYDOCK_SRC/logs"
13
+
14
+ mkdir -p "$LOGDIR"
15
+
16
+ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
17
+
18
+ # Check NAS is mounted
19
+ if ! mountpoint -q "$NAS_MOUNT" 2>/dev/null; then
20
+ log "NAS not mounted. Attempting mount..."
21
+ sudo mount "$NAS_MOUNT" 2>/dev/null || {
22
+ log "ERROR: Cannot mount NAS at $NAS_MOUNT. Skipping backup."
23
+ exit 1
24
+ }
25
+ fi
26
+
27
+ mkdir -p "$BACKUP_DIR"
28
+
29
+ log "Starting backup..."
30
+
31
+ # Backup 1: DryDock source code (excluding __pycache__, .git objects)
32
+ rsync -rlpt --delete --no-group --no-owner \
33
+ --exclude='__pycache__/' \
34
+ --exclude='.pytest_cache/' \
35
+ --exclude='dist/' \
36
+ --exclude='*.egg-info/' \
37
+ --exclude='.git/objects/' \
38
+ --exclude='logs/' \
39
+ "$DRYDOCK_SRC/" "$BACKUP_DIR/source/"
40
+
41
+ log "Source backed up."
42
+
43
+ # Backup 2: SWE-bench results and state (not the repos — too large)
44
+ rsync -rlpt --delete --no-group --no-owner \
45
+ --exclude='repos/' \
46
+ --exclude='tasks/*/repo/' \
47
+ --exclude='__pycache__/' \
48
+ "$BENCH_DIR/continuous_bench_state.json" \
49
+ "$BENCH_DIR/auto_improve_state.json" \
50
+ "$BENCH_DIR/harness.py" \
51
+ "$BENCH_DIR/continuous_bench.sh" \
52
+ "$BENCH_DIR/analyze_batch.py" \
53
+ "$BACKUP_DIR/bench/" 2>/dev/null || true
54
+
55
+ # Backup results directories (small — just JSON)
56
+ rsync -rlpt --no-group --no-owner \
57
+ --include='*/' \
58
+ --include='results.json' \
59
+ --exclude='*' \
60
+ "$BENCH_DIR/results/" "$BACKUP_DIR/bench/results/" 2>/dev/null || true
61
+
62
+ log "Bench state backed up."
63
+
64
+ # Backup 3: Config files
65
+ mkdir -p "$BACKUP_DIR/config/drydock_config" "$BACKUP_DIR/config/drydock_home"
66
+ rsync -rlpt --no-group --no-owner "$HOME/.config/drydock/" "$BACKUP_DIR/config/drydock_config/" 2>/dev/null || true
67
+ rsync -rlpt --no-group --no-owner --exclude='logs/' "$HOME/.drydock/" "$BACKUP_DIR/config/drydock_home/" 2>/dev/null || true
68
+
69
+ log "Config backed up."
70
+
71
+ # Backup 4: Crontab
72
+ crontab -l > "$BACKUP_DIR/crontab_$(hostname).txt" 2>/dev/null || true
73
+
74
+ # Write backup manifest
75
+ cat > "$BACKUP_DIR/MANIFEST.txt" << EOF
76
+ DryDock Backup
77
+ Date: $(date)
78
+ Host: $(hostname)
79
+ Source: $DRYDOCK_SRC
80
+ Bench: $BENCH_DIR
81
+ Version: $(python3 -c "import sys; sys.path.insert(0,'$DRYDOCK_SRC'); from drydock import __version__; print(__version__)" 2>/dev/null || echo "unknown")
82
+ EOF
83
+
84
+ log "Backup complete to $BACKUP_DIR"
@@ -37,7 +37,7 @@ AUTH_URL="https://${GITHUB_TOKEN}@github.com/fbobe321/drydock.git"
37
37
 
38
38
  log "Running regression tests..."
39
39
  cd "$DRYDOCK_SRC"
40
- if ! python3 -m pytest tests/test_drydock_regression.py tests/test_drydock_tasks.py tests/test_loop_detection.py tests/test_agent_tasks.py tests/test_integration.py tests/test_user_issues.py \
40
+ if ! python3 -m pytest tests/test_drydock_regression.py tests/test_drydock_tasks.py tests/test_loop_detection.py tests/test_agent_tasks.py tests/test_integration.py tests/test_user_issues.py tests/test_real_issues.py \
41
41
  -p no:xdist -p no:cov --override-ini="addopts=" -q 2>&1; then
42
42
  log "TESTS FAILED — deploy aborted. Fix the tests before deploying."
43
43
  exit 1
@@ -25,7 +25,7 @@ PYPI_TOKEN=$(cat "$TOKEN_FILE")
25
25
 
26
26
  # --- Run tests ---
27
27
  log "Running regression tests..."
28
- if ! $PYTHON -m pytest tests/test_drydock_regression.py tests/test_drydock_tasks.py tests/test_loop_detection.py tests/test_agent_tasks.py tests/test_integration.py tests/test_user_issues.py \
28
+ if ! $PYTHON -m pytest tests/test_drydock_regression.py tests/test_drydock_tasks.py tests/test_loop_detection.py tests/test_agent_tasks.py tests/test_integration.py tests/test_user_issues.py tests/test_real_issues.py \
29
29
  -p no:xdist -p no:cov --override-ini="addopts=" -q 2>&1; then
30
30
  log "TESTS FAILED — publish aborted."
31
31
  exit 1
@@ -0,0 +1,95 @@
1
+ """Tests that FAIL — proving real bugs exist.
2
+
3
+ These tests reproduce actual user-reported failures against the real backend.
4
+ They should FAIL until the underlying code is fixed.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import os
11
+ from pathlib import Path
12
+
13
+ import httpx
14
+ import pytest
15
+
16
+ from drydock.core.config.harness_files import init_harness_files_manager
17
+
18
+ # Initialize harness before importing agent
19
+ try:
20
+ init_harness_files_manager("user", "project")
21
+ except RuntimeError:
22
+ pass
23
+
24
+ from drydock.core.agent_loop import AgentLoop
25
+ from drydock.core.agents.models import BuiltinAgentName
26
+ from drydock.core.config import Backend, ModelConfig, ProviderConfig, VibeConfig
27
+ from drydock.core.types import (
28
+ AssistantEvent,
29
+ BaseEvent,
30
+ Role,
31
+ ToolCallEvent,
32
+ ToolResultEvent,
33
+ )
34
+
35
+
36
+ def _vllm_ok() -> bool:
37
+ try:
38
+ return httpx.get("http://localhost:8000/v1/models", timeout=3).status_code == 200
39
+ except Exception:
40
+ return False
41
+
42
+ pytestmark = pytest.mark.skipif(not _vllm_ok(), reason="vLLM not running")
43
+
44
+
45
+ def _config(tmp_path: Path) -> VibeConfig:
46
+ return VibeConfig(
47
+ active_model="devstral", auto_approve=True, enable_telemetry=False,
48
+ include_project_context=False, system_prompt_id="tests",
49
+ providers=[ProviderConfig(name="local", api_base="http://localhost:8000/v1", api_key_env_var="", backend=Backend.GENERIC)],
50
+ models=[ModelConfig(name="devstral", provider="local", input_price=0, output_price=0)],
51
+ session_logging={"enabled": False, "save_dir": str(tmp_path / "logs")},
52
+ )
53
+
54
+
55
+ def _agent(tmp_path: Path, max_turns: int = 15) -> AgentLoop:
56
+ return AgentLoop(config=_config(tmp_path), agent_name=BuiltinAgentName.AUTO_APPROVE, max_turns=max_turns)
57
+
58
+
59
+ async def _run(agent, prompt, max_events=100):
60
+ events = []
61
+ async for ev in agent.act(prompt):
62
+ events.append(ev)
63
+ if len(events) >= max_events:
64
+ break
65
+ return events
66
+
67
+
68
+ # ============================================================================
69
+ # ISSUE: Circuit breaker fires but model KEEPS calling the same tool
70
+ # The breaker returns an error, but the model ignores it and calls again.
71
+ # Proven: 20 bash calls despite 17 circuit breaker fires.
72
+ # ============================================================================
73
+
74
+ @pytest.mark.asyncio
75
+ async def test_circuit_breaker_actually_stops_execution(tmp_path):
76
+ """After circuit breaker fires, model should NOT make more than 3 additional
77
+ identical calls. Currently it makes 17+."""
78
+ agent = _agent(tmp_path, max_turns=20)
79
+ events = await _run(agent, "Run the command 'echo hello' ten separate times", max_events=120)
80
+
81
+ tool_calls = [e for e in events if isinstance(e, ToolCallEvent)]
82
+ cb_fires = [e for e in events if isinstance(e, ToolResultEvent)
83
+ and e.error and "CIRCUIT" in str(e.error)]
84
+
85
+ # Circuit breaker should fire
86
+ if not cb_fires:
87
+ pytest.skip("Model didn't repeat — can't test circuit breaker")
88
+
89
+ # After first circuit breaker fire, model should stop within 3 more calls
90
+ first_cb_idx = next(i for i, e in enumerate(events) if isinstance(e, ToolResultEvent) and e.error and "CIRCUIT" in str(e.error))
91
+ calls_after_cb = [e for e in events[first_cb_idx:] if isinstance(e, ToolCallEvent)]
92
+
93
+ assert len(calls_after_cb) <= 3, \
94
+ f"Model made {len(calls_after_cb)} tool calls AFTER circuit breaker fired. " \
95
+ f"It should stop, not keep going. Total: {len(tool_calls)} calls, {len(cb_fires)} breaker fires."
@@ -0,0 +1,252 @@
1
+ """Real backend tests for user-reported issues.
2
+
3
+ Every test hits the ACTUAL vLLM backend at localhost:8000.
4
+ NO mocks, NO fakes — real DryDock execution.
5
+
6
+ Skip if vLLM is not running.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import os
13
+ from pathlib import Path
14
+
15
+ import httpx
16
+ import pytest
17
+
18
+ from drydock.core.agent_loop import AgentLoop
19
+ from drydock.core.agents.models import BuiltinAgentName
20
+ from drydock.core.config import Backend, ModelConfig, ProviderConfig, VibeConfig
21
+ from drydock.core.types import (
22
+ AssistantEvent,
23
+ BaseEvent,
24
+ Role,
25
+ ToolCallEvent,
26
+ ToolResultEvent,
27
+ )
28
+
29
+
30
+ def _vllm_available() -> bool:
31
+ try:
32
+ return httpx.get("http://localhost:8000/v1/models", timeout=3).status_code == 200
33
+ except Exception:
34
+ return False
35
+
36
+
37
+ pytestmark = pytest.mark.skipif(not _vllm_available(), reason="vLLM not running")
38
+
39
+
40
+ def _config(tmp_path: Path) -> VibeConfig:
41
+ return VibeConfig(
42
+ active_model="devstral",
43
+ auto_approve=True,
44
+ enable_telemetry=False,
45
+ include_project_context=False,
46
+ system_prompt_id="cli",
47
+ providers=[ProviderConfig(
48
+ name="local", api_base="http://localhost:8000/v1",
49
+ api_key_env_var="", backend=Backend.GENERIC,
50
+ )],
51
+ models=[ModelConfig(
52
+ name="devstral", provider="local", alias="devstral",
53
+ input_price=0.0, output_price=0.0,
54
+ )],
55
+ session_logging={"enabled": False, "save_dir": str(tmp_path / "logs")},
56
+ )
57
+
58
+
59
+ def _agent(tmp_path: Path, max_turns: int = 5) -> AgentLoop:
60
+ return AgentLoop(
61
+ config=_config(tmp_path),
62
+ agent_name=BuiltinAgentName.AUTO_APPROVE,
63
+ max_turns=max_turns,
64
+ )
65
+
66
+
67
+ async def _run(agent: AgentLoop, prompt: str, max_events: int = 100) -> list[BaseEvent]:
68
+ events = []
69
+ async for ev in agent.act(prompt):
70
+ events.append(ev)
71
+ if len(events) >= max_events:
72
+ break
73
+ return events
74
+
75
+
76
+ # ============================================================================
77
+ # Test 1: "Understood" after error — agent should NOT stop with "Understood."
78
+ # ============================================================================
79
+
80
+ @pytest.mark.asyncio
81
+ async def test_no_understood_stops_agent(tmp_path):
82
+ """Agent should never inject 'Understood.' into conversation."""
83
+ agent = _agent(tmp_path)
84
+ events = await _run(agent, "Read the file /nonexistent/path/fake.py")
85
+
86
+ # Check messages — none should contain "Understood."
87
+ for msg in agent.messages:
88
+ if msg.role == Role.assistant and msg.content:
89
+ assert "Understood." not in msg.content, \
90
+ f"Found 'Understood.' in assistant message — causes premature stops"
91
+
92
+
93
+ # ============================================================================
94
+ # Test 2: Circuit breaker actually blocks (not just warns)
95
+ # ============================================================================
96
+
97
+ @pytest.mark.asyncio
98
+ async def test_circuit_breaker_blocks_real(tmp_path):
99
+ """After 2 identical tool calls, the 3rd should be blocked with error."""
100
+ agent = _agent(tmp_path, max_turns=10)
101
+ # Ask something that will likely trigger repeated tool calls
102
+ events = await _run(agent, "Run 'echo hello' five times in a row, each as a separate bash command")
103
+
104
+ tool_results = [e for e in events if isinstance(e, ToolResultEvent)]
105
+ # Should have some blocked results if circuit breaker works
106
+ errors = [e for e in tool_results if e.error and "CIRCUIT BREAKER" in str(e.error)]
107
+
108
+ # The model might not repeat exactly, but circuit breaker state should exist
109
+ assert hasattr(agent, "_tool_call_history")
110
+ assert isinstance(agent._tool_call_history, dict)
111
+ # At least verify no AttributeError crash
112
+ for e in tool_results:
113
+ if e.error:
114
+ assert "AttributeError" not in str(e.error), f"Crash: {e.error}"
115
+
116
+
117
+ # ============================================================================
118
+ # Test 3: Tool calls don't crash with AttributeError
119
+ # ============================================================================
120
+
121
+ @pytest.mark.asyncio
122
+ async def test_no_attribute_error_on_tools(tmp_path):
123
+ """No AttributeError should occur during tool execution."""
124
+ agent = _agent(tmp_path)
125
+ events = await _run(agent, "List the files in the current directory")
126
+
127
+ for e in events:
128
+ if isinstance(e, ToolResultEvent) and e.error:
129
+ assert "AttributeError" not in str(e.error), \
130
+ f"AttributeError in tool result: {e.error}"
131
+ assert "raw_arguments" not in str(e.error), \
132
+ f"Old raw_arguments bug: {e.error}"
133
+
134
+
135
+ # ============================================================================
136
+ # Test 4: Agent doesn't loop on ambiguous prompt
137
+ # ============================================================================
138
+
139
+ @pytest.mark.asyncio
140
+ async def test_ambiguous_prompt_limited(tmp_path):
141
+ """Typing 'test' should not cause 20+ tool calls."""
142
+ agent = _agent(tmp_path, max_turns=8)
143
+ events = await _run(agent, "test", max_events=60)
144
+
145
+ tool_calls = [e for e in events if isinstance(e, ToolCallEvent)]
146
+ assert len(tool_calls) < 25, \
147
+ f"Ambiguous prompt 'test' caused {len(tool_calls)} tool calls — too many"
148
+
149
+
150
+ # ============================================================================
151
+ # Test 5: Message ordering clean after real tool calls
152
+ # ============================================================================
153
+
154
+ @pytest.mark.asyncio
155
+ async def test_no_user_after_tool(tmp_path):
156
+ """No user message should follow a tool message in the history."""
157
+ agent = _agent(tmp_path)
158
+ events = await _run(agent, "What Python version is installed? Run python3 --version")
159
+
160
+ for i in range(1, len(agent.messages)):
161
+ if agent.messages[i].role == Role.user and agent.messages[i-1].role == Role.tool:
162
+ pytest.fail(f"user after tool at position {i}: "
163
+ f"'{agent.messages[i].content[:50]}' after tool '{agent.messages[i-1].content[:50]}'")
164
+
165
+
166
+ # ============================================================================
167
+ # Test 6: Agent actually uses tools (not just text responses)
168
+ # ============================================================================
169
+
170
+ @pytest.mark.asyncio
171
+ async def test_agent_uses_tools(tmp_path):
172
+ """Agent should call bash/grep/read_file when asked to do something concrete."""
173
+ agent = _agent(tmp_path)
174
+ events = await _run(agent, "Run: echo 'drydock integration test'")
175
+
176
+ tool_calls = [e for e in events if isinstance(e, ToolCallEvent)]
177
+ # Should have at least one tool call for a concrete command request
178
+ assert len(tool_calls) >= 1, \
179
+ "Agent gave text-only response when asked to run a command"
180
+
181
+
182
+ # ============================================================================
183
+ # Test 7: DryDock branding in system prompt (real check)
184
+ # ============================================================================
185
+
186
+ @pytest.mark.asyncio
187
+ async def test_system_prompt_branding(tmp_path):
188
+ """System prompt should say 'DryDock' not 'Drydock' or 'Vibe'."""
189
+ agent = _agent(tmp_path)
190
+ # Don't even need to run — just check the system prompt
191
+ system_msg = agent.messages[0]
192
+ assert system_msg.role == Role.system
193
+ assert "DryDock" in system_msg.content, "System prompt missing 'DryDock'"
194
+ assert "Vibe" not in system_msg.content or "mistral-vibe" in system_msg.content, \
195
+ "System prompt still references 'Vibe'"
196
+
197
+
198
+ # ============================================================================
199
+ # Test 8: Write file doesn't hang (has timeout)
200
+ # ============================================================================
201
+
202
+ @pytest.mark.asyncio
203
+ async def test_write_file_timeout_exists(tmp_path):
204
+ """write_file should complete within reasonable time, not hang."""
205
+ agent = _agent(tmp_path)
206
+ test_file = tmp_path / "test_write.txt"
207
+
208
+ events = await _run(agent,
209
+ f"Write the text 'hello world' to the file {test_file}")
210
+
211
+ # Should complete (not hang) — if it hangs, the test runner will timeout
212
+ assert len(events) >= 1
213
+ # Check if file was created (model might or might not do it)
214
+ # The important thing is it didn't hang
215
+
216
+
217
+ # ============================================================================
218
+ # Test 9: Agent stops cleanly (not infinite loop)
219
+ # ============================================================================
220
+
221
+ @pytest.mark.asyncio
222
+ async def test_agent_stops_cleanly(tmp_path):
223
+ """Agent should stop after completing a task, not loop forever."""
224
+ agent = _agent(tmp_path, max_turns=5)
225
+ events = await _run(agent, "What is 2+2? Just say the number.", max_events=30)
226
+
227
+ # Should stop within reasonable events
228
+ assert len(events) < 30, f"Agent produced {len(events)} events — possible loop"
229
+
230
+
231
+ # ============================================================================
232
+ # Test 10: Consultant config field exists and works
233
+ # ============================================================================
234
+
235
+ @pytest.mark.asyncio
236
+ async def test_consultant_config_real(tmp_path):
237
+ """VibeConfig should accept consultant_model field without error."""
238
+ config = VibeConfig(
239
+ active_model="devstral",
240
+ consultant_model="gemini-2.5-pro",
241
+ auto_approve=True,
242
+ enable_telemetry=False,
243
+ providers=[ProviderConfig(
244
+ name="local", api_base="http://localhost:8000/v1",
245
+ api_key_env_var="", backend=Backend.GENERIC,
246
+ )],
247
+ models=[ModelConfig(
248
+ name="devstral", provider="local",
249
+ input_price=0.0, output_price=0.0,
250
+ )],
251
+ )
252
+ assert config.consultant_model == "gemini-2.5-pro"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes