drydock-cli 0.5.1__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (453) hide show
  1. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/PKG-INFO +1 -1
  2. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/PRD.md +26 -4
  3. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/entrypoint.py +25 -0
  4. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/loading.py +9 -0
  5. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agent_loop.py +53 -0
  6. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/middleware.py +40 -12
  7. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/cli.md +10 -0
  8. drydock_cli-0.6.0/drydock/core/session/state_file.py +143 -0
  9. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/bash.py +17 -3
  10. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/search_replace.py +7 -3
  11. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/write_file.py +6 -0
  12. drydock_cli-0.6.0/drydock/core/tools/injection_guard.py +105 -0
  13. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/pyproject.toml +1 -1
  14. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_drydock_tasks.py +192 -0
  15. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/CODEOWNERS +0 -0
  16. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
  17. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  18. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  19. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/build-and-upload.yml +0 -0
  20. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/ci.yml +0 -0
  21. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/issue-labeler.yml +0 -0
  22. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/release.yml +0 -0
  23. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.gitignore +0 -0
  24. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.pre-commit-config.yaml +0 -0
  25. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.python-version +0 -0
  26. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.typos.toml +0 -0
  27. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.vscode/extensions.json +0 -0
  28. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.vscode/launch.json +0 -0
  29. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.vscode/settings.json +0 -0
  30. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/AGENTS.md +0 -0
  31. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/CHANGELOG.md +0 -0
  32. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/CLAUDE.md +0 -0
  33. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/CONTRIBUTING.md +0 -0
  34. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/Drydock_rebrand.md +0 -0
  35. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/LICENSE +0 -0
  36. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/NOTICE +0 -0
  37. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/README.md +0 -0
  38. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/action.yml +0 -0
  39. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/distribution/zed/LICENSE +0 -0
  40. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/distribution/zed/extension.toml +0 -0
  41. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/distribution/zed/icons/mistral_vibe.svg +0 -0
  42. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/docs/README.md +0 -0
  43. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/docs/acp-setup.md +0 -0
  44. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/docs/proxy-setup.md +0 -0
  45. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/__init__.py +0 -0
  46. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/__init__.py +0 -0
  47. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/acp_agent_loop.py +0 -0
  48. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/acp_logger.py +0 -0
  49. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/entrypoint.py +0 -0
  50. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/__init__.py +0 -0
  51. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/base.py +0 -0
  52. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/bash.py +0 -0
  53. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/read_file.py +0 -0
  54. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/search_replace.py +0 -0
  55. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/todo.py +0 -0
  56. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/write_file.py +0 -0
  57. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/session_update.py +0 -0
  58. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/utils.py +0 -0
  59. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/__init__.py +0 -0
  60. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/__init__.py +0 -0
  61. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/base.py +0 -0
  62. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/path_completion.py +0 -0
  63. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/slash_command.py +0 -0
  64. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/cli.py +0 -0
  65. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/clipboard.py +0 -0
  66. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/commands.py +0 -0
  67. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/history_manager.py +0 -0
  68. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
  69. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
  70. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
  71. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/terminal_setup.py +0 -0
  72. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/__init__.py +0 -0
  73. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
  74. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/app.py +0 -0
  75. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/app.tcss +0 -0
  76. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/external_editor.py +0 -0
  77. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
  78. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
  79. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
  80. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
  81. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
  82. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
  83. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
  84. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
  85. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
  86. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
  87. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
  88. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
  89. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
  90. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
  91. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
  92. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
  93. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
  94. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
  95. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/compact.py +0 -0
  96. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
  97. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
  98. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
  99. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/messages.py +0 -0
  100. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
  101. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
  102. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
  103. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
  104. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
  105. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
  106. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
  107. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
  108. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
  109. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/tools.py +0 -0
  110. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
  111. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
  112. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/history.py +0 -0
  113. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
  114. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/state.py +0 -0
  115. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/__init__.py +0 -0
  116. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
  117. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
  118. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
  119. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
  120. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
  121. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/update.py +0 -0
  122. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/whats_new.py +0 -0
  123. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/__init__.py +0 -0
  124. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agents/__init__.py +0 -0
  125. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agents/manager.py +0 -0
  126. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agents/models.py +0 -0
  127. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/auth/__init__.py +0 -0
  128. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/auth/crypto.py +0 -0
  129. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/auth/github.py +0 -0
  130. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/__init__.py +0 -0
  131. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/completers.py +0 -0
  132. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
  133. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
  134. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
  135. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/store.py +0 -0
  136. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
  137. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/fuzzy.py +0 -0
  138. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/path_prompt.py +0 -0
  139. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
  140. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/__init__.py +0 -0
  141. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/_settings.py +0 -0
  142. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/harness_files/__init__.py +0 -0
  143. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/harness_files/_harness_manager.py +0 -0
  144. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/harness_files/_paths.py +0 -0
  145. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/drydock_states.py +0 -0
  146. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/__init__.py +0 -0
  147. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/anthropic.py +0 -0
  148. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/base.py +0 -0
  149. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/factory.py +0 -0
  150. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/generic.py +0 -0
  151. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/mistral.py +0 -0
  152. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
  153. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/vertex.py +0 -0
  154. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/exceptions.py +0 -0
  155. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/format.py +0 -0
  156. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/message_utils.py +0 -0
  157. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/types.py +0 -0
  158. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/logger.py +0 -0
  159. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/output_formatters.py +0 -0
  160. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/__init__.py +0 -0
  161. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/_local_config_walk.py +0 -0
  162. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/_vibe_home.py +0 -0
  163. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/conventions.py +0 -0
  164. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/plan_session.py +0 -0
  165. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/programmatic.py +0 -0
  166. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/__init__.py +0 -0
  167. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/compact.md +0 -0
  168. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/dangerous_directory.md +0 -0
  169. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/diagnostic.md +0 -0
  170. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/explore.md +0 -0
  171. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/planner.md +0 -0
  172. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/project_context.md +0 -0
  173. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/tests.md +0 -0
  174. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/proxy_setup.py +0 -0
  175. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/session/session_loader.py +0 -0
  176. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/session/session_logger.py +0 -0
  177. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/session/session_migration.py +0 -0
  178. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/__init__.py +0 -0
  179. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/manager.py +0 -0
  180. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/models.py +0 -0
  181. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/parser.py +0 -0
  182. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/slug.py +0 -0
  183. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/system_prompt.py +0 -0
  184. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/telemetry/__init__.py +0 -0
  185. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/telemetry/send.py +0 -0
  186. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/errors.py +0 -0
  187. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/git.py +0 -0
  188. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/nuage.py +0 -0
  189. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/teleport.py +0 -0
  190. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/types.py +0 -0
  191. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/base.py +0 -0
  192. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/ask_user_question.py +0 -0
  193. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
  194. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/grep.py +0 -0
  195. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
  196. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
  197. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/bash.md +0 -0
  198. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/grep.md +0 -0
  199. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
  200. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
  201. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
  202. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/task.md +0 -0
  203. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/todo.md +0 -0
  204. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
  205. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
  206. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
  207. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/read_file.py +0 -0
  208. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/task.py +0 -0
  209. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/todo.py +0 -0
  210. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/webfetch.py +0 -0
  211. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/websearch.py +0 -0
  212. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/manager.py +0 -0
  213. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp/__init__.py +0 -0
  214. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp/registry.py +0 -0
  215. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp/tools.py +0 -0
  216. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp_sampling.py +0 -0
  217. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/ui.py +0 -0
  218. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/utils.py +0 -0
  219. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/trusted_folders.py +0 -0
  220. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/types.py +0 -0
  221. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/utils.py +0 -0
  222. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/__init__.py +0 -0
  223. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/base.py +0 -0
  224. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/onboarding.tcss +0 -0
  225. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/screens/__init__.py +0 -0
  226. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/screens/api_key.py +0 -0
  227. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/screens/welcome.py +0 -0
  228. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
  229. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
  230. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/skills/__init__.py +0 -0
  231. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/skills/create-presentation/SKILL.md +0 -0
  232. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/whats_new.md +0 -0
  233. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock-acp.spec +0 -0
  234. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock_terms.md +0 -0
  235. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/flake.lock +0 -0
  236. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/flake.nix +0 -0
  237. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/README.md +0 -0
  238. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/bump_version.py +0 -0
  239. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/deploy_to_github.sh +0 -0
  240. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/install.sh +0 -0
  241. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/prepare_release.py +0 -0
  242. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/publish_to_pypi.sh +0 -0
  243. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/__init__.py +0 -0
  244. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/conftest.py +0 -0
  245. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_acp.py +0 -0
  246. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_agent_thought.py +0 -0
  247. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_bash.py +0 -0
  248. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_compact_session_updates.py +0 -0
  249. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_content.py +0 -0
  250. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_initialize.py +0 -0
  251. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_list_sessions.py +0 -0
  252. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_load_session.py +0 -0
  253. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_multi_session.py +0 -0
  254. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_new_session.py +0 -0
  255. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_proxy_setup_acp.py +0 -0
  256. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_read_file.py +0 -0
  257. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_search_replace.py +0 -0
  258. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_set_config_option.py +0 -0
  259. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_set_mode.py +0 -0
  260. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_set_model.py +0 -0
  261. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_tool_call_session_update.py +0 -0
  262. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_utils.py +0 -0
  263. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_write_file.py +0 -0
  264. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_file_indexer.py +0 -0
  265. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_fuzzy.py +0 -0
  266. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
  267. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_completer_recursive.py +0 -0
  268. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_completion_controller.py +0 -0
  269. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
  270. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_slash_command_controller.py +0 -0
  271. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
  272. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/__init__.py +0 -0
  273. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/data/__init__.py +0 -0
  274. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/data/fireworks.py +0 -0
  275. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/data/mistral.py +0 -0
  276. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_anthropic_adapter.py +0 -0
  277. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_backend.py +0 -0
  278. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_reasoning_adapter.py +0 -0
  279. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
  280. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
  281. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
  282. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
  283. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_bell_notifications.py +0 -0
  284. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_braille_renderer.py +0 -0
  285. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_clipboard.py +0 -0
  286. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_commands.py +0 -0
  287. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_copy_shortcuts.py +0 -0
  288. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_external_editor.py +0 -0
  289. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_no_markup_static.py +0 -0
  290. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_question_app.py +0 -0
  291. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_spinner.py +0 -0
  292. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_switching_mode.py +0 -0
  293. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_clipboard_notifications.py +0 -0
  294. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
  295. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_session_resume.py +0 -0
  296. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_skill_dispatch.py +0 -0
  297. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/textual_ui/__init__.py +0 -0
  298. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/textual_ui/test_session_picker.py +0 -0
  299. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/conftest.py +0 -0
  300. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_agents.py +0 -0
  301. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_auth_crypto.py +0 -0
  302. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_auth_github.py +0 -0
  303. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_config_load_dotenv.py +0 -0
  304. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_config_paths.py +0 -0
  305. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_config_resolution.py +0 -0
  306. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_file_logging.py +0 -0
  307. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_plan_session.py +0 -0
  308. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_proxy_setup.py +0 -0
  309. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_slug.py +0 -0
  310. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_telemetry_send.py +0 -0
  311. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_teleport_git.py +0 -0
  312. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_teleport_nuage.py +0 -0
  313. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_teleport_service.py +0 -0
  314. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_trusted_folders.py +0 -0
  315. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_utils.py +0 -0
  316. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/common.py +0 -0
  317. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/conftest.py +0 -0
  318. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/mock_server.py +0 -0
  319. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/test_cli_tui_onboarding.py +0 -0
  320. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/test_cli_tui_streaming.py +0 -0
  321. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
  322. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/__init__.py +0 -0
  323. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/mock_backend_factory.py +0 -0
  324. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/mock_entrypoint.py +0 -0
  325. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/utils.py +0 -0
  326. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/onboarding/test_run_onboarding.py +0 -0
  327. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/onboarding/test_ui_onboarding.py +0 -0
  328. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/session/test_session_loader.py +0 -0
  329. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/session/test_session_logger.py +0 -0
  330. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/session/test_session_migration.py +0 -0
  331. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/conftest.py +0 -0
  332. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/test_manager.py +0 -0
  333. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/test_models.py +0 -0
  334. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/test_parser.py +0 -0
  335. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
  336. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
  337. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
  338. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_code_block_horizontal_scrolling/test_snapshot_allows_horizontal_scrolling_for_long_code_blocks.svg +0 -0
  339. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
  340. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
  341. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
  342. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
  343. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
  344. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
  345. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
  346. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
  347. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
  348. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
  349. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
  350. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
  351. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
  352. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
  353. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
  354. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
  355. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
  356. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
  357. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
  358. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
  359. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
  360. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
  361. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
  362. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
  363. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
  364. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
  365. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
  366. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
  367. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
  368. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
  369. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
  370. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
  371. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
  372. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
  373. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
  374. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
  375. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
  376. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
  377. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
  378. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
  379. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
  380. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
  381. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
  382. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
  383. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
  384. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
  385. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
  386. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
  387. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
  388. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
  389. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
  390. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
  391. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
  392. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
  393. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
  394. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
  395. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/base_snapshot_test_app.py +0 -0
  396. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/conftest.py +0 -0
  397. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/snap_compare.py +0 -0
  398. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
  399. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
  400. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
  401. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
  402. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
  403. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
  404. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
  405. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
  406. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
  407. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
  408. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
  409. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
  410. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
  411. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
  412. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/stubs/fake_backend.py +0 -0
  413. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/stubs/fake_client.py +0 -0
  414. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/stubs/fake_tool.py +0 -0
  415. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_auto_compact.py +0 -0
  416. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_backend.py +0 -0
  417. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_observer_streaming.py +0 -0
  418. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_stats.py +0 -0
  419. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_tool_call.py +0 -0
  420. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agents.py +0 -0
  421. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_cli_programmatic_preload.py +0 -0
  422. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_drydock_regression.py +0 -0
  423. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_history_manager.py +0 -0
  424. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_message_id.py +0 -0
  425. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_message_merging.py +0 -0
  426. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_middleware.py +0 -0
  427. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_reasoning_content.py +0 -0
  428. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_system_prompt.py +0 -0
  429. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_tagged_text.py +0 -0
  430. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_ui_external_editor.py +0 -0
  431. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_ui_input_history.py +0 -0
  432. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_ask_user_question.py +0 -0
  433. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_bash.py +0 -0
  434. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_exit_plan_mode.py +0 -0
  435. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_grep.py +0 -0
  436. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_invoke_context.py +0 -0
  437. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_manager_get_tool_config.py +0 -0
  438. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_mcp.py +0 -0
  439. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_mcp_sampling.py +0 -0
  440. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_task.py +0 -0
  441. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_ui_bash_execution.py +0 -0
  442. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_webfetch.py +0 -0
  443. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_websearch.py +0 -0
  444. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
  445. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
  446. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_do_update.py +0 -0
  447. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
  448. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_github_update_gateway.py +0 -0
  449. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
  450. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_ui_update_notification.py +0 -0
  451. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_update_use_case.py +0 -0
  452. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_whats_new.py +0 -0
  453. {drydock_cli-0.5.1 → drydock_cli-0.6.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 0.5.1
3
+ Version: 0.6.0
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
@@ -153,10 +153,32 @@ User-reported issues from real usage testing, all fixed:
153
153
 
154
154
  ### Phase 5b: Testing & CI (Mar 24)
155
155
 
156
- - **64 regression tests** in 2 test files (0.12s total runtime)
157
- - **Tests gate all deploys** — deploy_to_github.sh and publish_to_pypi.sh abort on failure
158
- - **Continuous SWE-bench loop** — 320/500 tasks tested, 45.3% pass rate, auto-restarts on reboot
159
- - **PyPI published** — drydock-cli v0.4.1
156
+ - **76 regression tests** gating all deploys
157
+ - **Continuous SWE-bench loop** — 500/500 tasks tested, 49.0% pass rate (+9.4% from baseline)
158
+ - **vibe/ drydock/** package rename (908 imports updated)
159
+ - **PyPI published** — drydock-cli v0.5.2
160
+
161
+ ### Phase 6: GSD Integration & Performance (Mar 25)
162
+
163
+ GSD-inspired features (from [get-shit-done](https://github.com/gsd-build/get-shit-done)):
164
+
165
+ | Feature | Details |
166
+ |---------|---------|
167
+ | Tiered context warnings | 4 levels at 50/65/75/85% usage, debounced every 5 calls |
168
+ | Prompt injection guard | Detects role overrides, invisible Unicode, hidden instructions in file writes |
169
+ | Structured state file | `.drydock/state.md` persists task, files, decisions across sessions |
170
+ | Deviation handling rules | Auto-fix bugs/imports, ask user for architecture/scope decisions |
171
+
172
+ Performance and loop fixes:
173
+
174
+ | Fix | Details |
175
+ |-----|---------|
176
+ | Circuit breaker | Blocks exact same tool call after 2 attempts, returns cached result |
177
+ | Thinking flicker fix | Status words change every 4s, not every token |
178
+ | Conda env protection | Preserves user's active conda environment in subprocesses |
179
+ | `--insecure` / `-k` flag | Disables SSL verification for corporate proxy environments |
180
+ | `--consultant MODEL` flag | Allows calling a smarter model for single-turn advice when stuck |
181
+ | 81 regression tests | Circuit breaker, injection guard, state file, context tiers all tested |
160
182
 
161
183
  ---
162
184
 
@@ -90,6 +90,19 @@ def parse_arguments() -> argparse.Namespace:
90
90
  help="Skip all tool permission checks. Equivalent to --agent auto-approve. "
91
91
  "Use with caution — tools will execute without confirmation.",
92
92
  )
93
+ parser.add_argument(
94
+ "-k", "--insecure",
95
+ action="store_true",
96
+ help="Disable SSL certificate verification for web searches and API calls. "
97
+ "Useful behind corporate proxies with self-signed certificates.",
98
+ )
99
+ parser.add_argument(
100
+ "--consultant",
101
+ metavar="MODEL",
102
+ help="Enable consultant mode: when the agent is uncertain or stuck in a loop, "
103
+ "it can call a more capable model (e.g., 'gemini-2.5-pro') for single-turn advice. "
104
+ "The consultant is NOT used for tool calls, only reasoning.",
105
+ )
93
106
 
94
107
  # Feature flag for teleport, not exposed to the user yet
95
108
  parser.add_argument("--teleport", action="store_true", help=argparse.SUPPRESS)
@@ -160,6 +173,18 @@ def main() -> None:
160
173
  if args.dangerously_skip_permissions:
161
174
  args.agent = BuiltinAgentName.AUTO_APPROVE
162
175
 
176
+ # --insecure → disable SSL verification globally
177
+ if args.insecure:
178
+ os.environ["DRYDOCK_INSECURE"] = "1"
179
+ os.environ["CURL_CA_BUNDLE"] = ""
180
+ os.environ["REQUESTS_CA_BUNDLE"] = ""
181
+ import ssl
182
+ ssl._create_default_https_context = ssl._create_unverified_context
183
+
184
+ # --consultant → store for agent loop to use
185
+ if args.consultant:
186
+ os.environ["DRYDOCK_CONSULTANT_MODEL"] = args.consultant
187
+
163
188
  is_interactive = args.prompt is None
164
189
  if is_interactive:
165
190
  check_and_resolve_trusted_folder()
@@ -64,6 +64,9 @@ class LoadingWidget(SpinnerMixin, Static):
64
64
  "Stocking the galley",
65
65
  ]
66
66
 
67
+ # Minimum seconds between status word changes (prevents flicker)
68
+ _STATUS_CHANGE_INTERVAL = 4.0
69
+
67
70
  def __init__(self, status: str | None = None) -> None:
68
71
  super().__init__(classes="loading-widget")
69
72
  self.init_spinner()
@@ -77,6 +80,7 @@ class LoadingWidget(SpinnerMixin, Static):
77
80
  self._last_elapsed: int = -1
78
81
  self._paused_total: float = 0.0
79
82
  self._pause_start: float | None = None
83
+ self._last_status_change: float = 0.0
80
84
 
81
85
  def _get_easter_egg(self) -> str | None:
82
86
  EASTER_EGG_PROBABILITY = 0.10
@@ -111,6 +115,11 @@ class LoadingWidget(SpinnerMixin, Static):
111
115
  self._pause_start = None
112
116
 
113
117
  def set_status(self, status: str) -> None:
118
+ # Throttle status word changes to prevent flicker
119
+ now = time()
120
+ if now - self._last_status_change < self._STATUS_CHANGE_INTERVAL:
121
+ return # Too soon — keep current word
122
+ self._last_status_change = now
114
123
  self.status = self._apply_easter_egg(status)
115
124
  self._update_animation()
116
125
 
@@ -188,6 +188,10 @@ class AgentLoop:
188
188
  self.middleware_pipeline = MiddlewarePipeline()
189
189
  self._setup_middleware()
190
190
 
191
+ # Circuit breaker: track tool call signatures to prevent exact repeats
192
+ # Key: hash(tool_name + args), Value: (count, last_result_snippet)
193
+ self._tool_call_history: dict[str, tuple[int, str]] = {}
194
+
191
195
  system_prompt = get_universal_system_prompt(
192
196
  self.tool_manager, self.config, self.skill_manager, self.agent_manager
193
197
  )
@@ -288,6 +292,18 @@ class AgentLoop:
288
292
 
289
293
  async def act(self, msg: str) -> AsyncGenerator[BaseEvent]:
290
294
  self._clean_message_history()
295
+
296
+ # Load project state for cross-session context
297
+ try:
298
+ from drydock.core.session.state_file import load_state
299
+ state_content = load_state()
300
+ if state_content:
301
+ self._inject_system_note(
302
+ f"Previous session state:\n{state_content[:500]}"
303
+ )
304
+ except Exception:
305
+ pass # Non-critical
306
+
291
307
  async for event in self._conversation_loop(msg):
292
308
  yield event
293
309
 
@@ -819,9 +835,44 @@ class AgentLoop:
819
835
  )
820
836
  )
821
837
 
838
+ def _circuit_breaker_check(self, tool_call: ResolvedToolCall) -> str | None:
839
+ """Block exact-duplicate tool calls. Returns cached result or None."""
840
+ sig = hashlib.md5(
841
+ f"{tool_call.tool_name}:{tool_call.raw_arguments}".encode()
842
+ ).hexdigest()
843
+
844
+ count, last_result = self._tool_call_history.get(sig, (0, ""))
845
+ if count >= 2:
846
+ return (
847
+ f"CIRCUIT BREAKER: You already ran this exact command {count} times "
848
+ f"and got the same result. Previous result: {last_result[:300]}\n\n"
849
+ f"Do NOT repeat. Try a DIFFERENT approach, different arguments, "
850
+ f"or a different tool."
851
+ )
852
+ return None
853
+
854
+ def _circuit_breaker_record(self, tool_call: ResolvedToolCall, result_text: str) -> None:
855
+ """Record a tool call execution for circuit breaker tracking."""
856
+ sig = hashlib.md5(
857
+ f"{tool_call.tool_name}:{tool_call.raw_arguments}".encode()
858
+ ).hexdigest()
859
+ count, _ = self._tool_call_history.get(sig, (0, ""))
860
+ self._tool_call_history[sig] = (count + 1, result_text[:500])
861
+
822
862
  async def _process_one_tool_call(
823
863
  self, tool_call: ResolvedToolCall
824
864
  ) -> AsyncGenerator[ToolResultEvent | ToolStreamEvent]:
865
+ # Circuit breaker: block exact duplicate calls after 2 attempts
866
+ if blocked := self._circuit_breaker_check(tool_call):
867
+ yield ToolResultEvent(
868
+ tool_name=tool_call.tool_name,
869
+ tool_class=tool_call.tool_class,
870
+ error=blocked,
871
+ tool_call_id=tool_call.call_id,
872
+ )
873
+ self._handle_tool_response(tool_call, blocked, "failure")
874
+ return
875
+
825
876
  try:
826
877
  tool_instance = self.tool_manager.get(tool_call.tool_name)
827
878
  except Exception as exc:
@@ -890,6 +941,8 @@ class AgentLoop:
890
941
 
891
942
  result_dict = result_model.model_dump()
892
943
  text = "\n".join(f"{k}: {v}" for k, v in result_dict.items())
944
+ # Record for circuit breaker
945
+ self._circuit_breaker_record(tool_call, text)
893
946
  self._handle_tool_response(
894
947
  tool_call, text, "success", decision, result_dict
895
948
  )
@@ -98,35 +98,63 @@ class AutoCompactMiddleware:
98
98
 
99
99
 
100
100
  class ContextWarningMiddleware:
101
+ """Tiered context warnings inspired by GSD's context monitoring.
102
+
103
+ Warns at multiple thresholds as context fills up:
104
+ - 50% used: soft warning ("you're halfway through")
105
+ - 65% used: moderate warning ("wrap up current task")
106
+ - 75% used: critical warning ("finish NOW or compact")
107
+ - 85% used: emergency ("stop exploring, make final edit")
108
+
109
+ Debounced: only warns every 5 tool calls to avoid spamming.
110
+ """
111
+
112
+ _TIERS = [
113
+ (0.50, "soft", "You've used {pct:.0f}% of context ({used:,}/{max:,} tokens). Start wrapping up your current task."),
114
+ (0.65, "moderate", "Context at {pct:.0f}%. Stop exploring — finish your current edit and verify it works."),
115
+ (0.75, "critical", "Context at {pct:.0f}% — CRITICAL. Make your final edit NOW. No more grep/read_file. Use /compact if you need to continue."),
116
+ (0.85, "emergency", "Context nearly full ({pct:.0f}%). You MUST stop after this turn. Apply your fix with search_replace immediately or the session will degrade."),
117
+ ]
118
+
101
119
  def __init__(
102
120
  self, threshold_percent: float = 0.5, max_context: int | None = None
103
121
  ) -> None:
104
122
  self.threshold_percent = threshold_percent
105
123
  self.max_context = max_context
106
- self.has_warned = False
124
+ self._tier_warned: set[str] = set()
125
+ self._calls_since_last_warn = 0
126
+ self._debounce_interval = 5
107
127
 
108
128
  async def before_turn(self, context: ConversationContext) -> MiddlewareResult:
109
- if self.has_warned:
129
+ max_context = self.max_context
130
+ if max_context is None or max_context == 0:
110
131
  return MiddlewareResult()
111
132
 
112
- max_context = self.max_context
113
- if max_context is None:
133
+ self._calls_since_last_warn += 1
134
+
135
+ # Debounce: don't warn on every single turn
136
+ if self._calls_since_last_warn < self._debounce_interval:
114
137
  return MiddlewareResult()
115
138
 
116
- if context.stats.context_tokens >= max_context * self.threshold_percent:
117
- self.has_warned = True
139
+ pct_used = context.stats.context_tokens / max_context
118
140
 
119
- percentage_used = (context.stats.context_tokens / max_context) * 100
120
- warning_msg = f"<{VIBE_WARNING_TAG}>You have used {percentage_used:.0f}% of your total context ({context.stats.context_tokens:,}/{max_context:,} tokens)</{VIBE_WARNING_TAG}>"
141
+ # Find the highest tier we've crossed but haven't warned about
142
+ for threshold, tier_name, template in reversed(self._TIERS):
143
+ if pct_used >= threshold and tier_name not in self._tier_warned:
144
+ self._tier_warned.add(tier_name)
145
+ self._calls_since_last_warn = 0
121
146
 
122
- return MiddlewareResult(
123
- action=MiddlewareAction.INJECT_MESSAGE, message=warning_msg
124
- )
147
+ warning_msg = f"<{VIBE_WARNING_TAG}>{template.format(pct=pct_used * 100, used=context.stats.context_tokens, max=max_context)}</{VIBE_WARNING_TAG}>"
148
+ return MiddlewareResult(
149
+ action=MiddlewareAction.INJECT_MESSAGE, message=warning_msg
150
+ )
151
+ break
125
152
 
126
153
  return MiddlewareResult()
127
154
 
128
155
  def reset(self, reset_reason: ResetReason = ResetReason.STOP) -> None:
129
- self.has_warned = False
156
+ self._tier_warned.clear()
157
+ self._calls_since_last_warn = 0
130
158
 
131
159
 
132
160
  def make_plan_agent_reminder(plan_file_path: str) -> str:
@@ -59,6 +59,16 @@ Flip-flopping (add X → remove X → add X) is a critical failure. Commit to a
59
59
  Ambiguous Prompts
60
60
  If the user's message is very short or ambiguous (e.g., "test", "check", "fix"), ask what they want before exploring the filesystem. Do NOT start scanning directories or running `find` on a vague prompt. Ask: "What would you like me to test/check/fix?"
61
61
 
62
+ Deviation Handling
63
+ When your fix attempt hits unexpected issues, follow these rules:
64
+ 1. **Bug in your fix** → Auto-fix immediately. Re-read the error, adjust your edit, retry.
65
+ 2. **Missing dependency/import** → Auto-resolve. Add the import or install the package.
66
+ 3. **Blocking issue** (wrong file, missing context) → Auto-resolve by grepping for the right location.
67
+ 4. **Architectural decision** (should we refactor? change the API? add a new module?) → STOP and ask the user. Do NOT make architectural decisions unilaterally.
68
+ 5. **Scope change** (the fix requires changes to 5+ files, or touches unrelated code) → STOP and ask the user before proceeding.
69
+
70
+ Rules 1-3: fix silently. Rules 4-5: always ask.
71
+
62
72
  Response Format
63
73
  No Noise
64
74
  No greetings, outros, hedging, puffery, or tool narration.
@@ -0,0 +1,143 @@
1
+ """Structured state file for cross-session memory.
2
+
3
+ Maintains a `.drydock/state.md` file in the project directory that tracks:
4
+ - Current task and status
5
+ - Files modified in this session
6
+ - Key decisions made
7
+ - Blockers and next steps
8
+
9
+ Max 100 lines. Loaded on session start, updated on session end.
10
+ Enables "resume where I left off" workflows.
11
+
12
+ Inspired by GSD's STATE.md concept.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ from datetime import datetime
19
+ from pathlib import Path
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ _MAX_LINES = 100
24
+ _STATE_FILENAME = "state.md"
25
+
26
+
27
+ def _get_state_path(project_dir: Path | None = None) -> Path:
28
+ """Get the path to the state file."""
29
+ base = project_dir or Path.cwd()
30
+ return base / ".drydock" / _STATE_FILENAME
31
+
32
+
33
+ def load_state(project_dir: Path | None = None) -> str:
34
+ """Load the state file content, or empty string if it doesn't exist."""
35
+ path = _get_state_path(project_dir)
36
+ try:
37
+ if path.exists():
38
+ content = path.read_text(encoding="utf-8")
39
+ logger.info("Loaded session state from %s (%d lines)", path, content.count("\n"))
40
+ return content
41
+ except (OSError, PermissionError) as e:
42
+ logger.warning("Could not read state file %s: %s", path, e)
43
+ return ""
44
+
45
+
46
+ def save_state(
47
+ project_dir: Path | None = None,
48
+ *,
49
+ current_task: str = "",
50
+ files_modified: list[str] | None = None,
51
+ decisions: list[str] | None = None,
52
+ blockers: list[str] | None = None,
53
+ next_steps: list[str] | None = None,
54
+ custom_notes: str = "",
55
+ ) -> None:
56
+ """Save the state file with structured session information."""
57
+ path = _get_state_path(project_dir)
58
+
59
+ lines: list[str] = [
60
+ f"# DryDock Session State",
61
+ f"_Updated: {datetime.now().strftime('%Y-%m-%d %H:%M')}_",
62
+ "",
63
+ ]
64
+
65
+ if current_task:
66
+ lines.extend([f"## Current Task", current_task, ""])
67
+
68
+ if files_modified:
69
+ lines.append("## Files Modified")
70
+ for f in files_modified[-20:]: # Keep last 20
71
+ lines.append(f"- `{f}`")
72
+ lines.append("")
73
+
74
+ if decisions:
75
+ lines.append("## Decisions")
76
+ for d in decisions[-10:]: # Keep last 10
77
+ lines.append(f"- {d}")
78
+ lines.append("")
79
+
80
+ if blockers:
81
+ lines.append("## Blockers")
82
+ for b in blockers:
83
+ lines.append(f"- {b}")
84
+ lines.append("")
85
+
86
+ if next_steps:
87
+ lines.append("## Next Steps")
88
+ for n in next_steps:
89
+ lines.append(f"- {n}")
90
+ lines.append("")
91
+
92
+ if custom_notes:
93
+ lines.extend(["## Notes", custom_notes, ""])
94
+
95
+ # Enforce max lines
96
+ content = "\n".join(lines[:_MAX_LINES])
97
+
98
+ try:
99
+ path.parent.mkdir(parents=True, exist_ok=True)
100
+ path.write_text(content, encoding="utf-8")
101
+ logger.info("Saved session state to %s", path)
102
+ except (OSError, PermissionError) as e:
103
+ logger.warning("Could not write state file %s: %s", path, e)
104
+
105
+
106
+ def update_state_files_modified(
107
+ project_dir: Path | None = None,
108
+ files: list[str] | None = None,
109
+ ) -> None:
110
+ """Quick update: append modified files to the state without rewriting everything."""
111
+ if not files:
112
+ return
113
+
114
+ path = _get_state_path(project_dir)
115
+ try:
116
+ existing = path.read_text(encoding="utf-8") if path.exists() else ""
117
+
118
+ if "## Files Modified" not in existing:
119
+ # Append a new section
120
+ existing += "\n## Files Modified\n"
121
+
122
+ for f in files:
123
+ entry = f"- `{f}`"
124
+ if entry not in existing:
125
+ existing += f"{entry}\n"
126
+
127
+ # Enforce max lines
128
+ lines = existing.split("\n")[:_MAX_LINES]
129
+ path.parent.mkdir(parents=True, exist_ok=True)
130
+ path.write_text("\n".join(lines), encoding="utf-8")
131
+ except (OSError, PermissionError):
132
+ pass # Non-critical
133
+
134
+
135
+ def clear_state(project_dir: Path | None = None) -> None:
136
+ """Remove the state file."""
137
+ path = _get_state_path(project_dir)
138
+ try:
139
+ if path.exists():
140
+ path.unlink()
141
+ logger.info("Cleared session state at %s", path)
142
+ except (OSError, PermissionError):
143
+ pass
@@ -110,11 +110,25 @@ def _get_base_env() -> dict[str, str]:
110
110
 
111
111
  # Enable conda in non-interactive shells by setting BASH_ENV
112
112
  # to source conda's setup script. Without this, `conda activate`
113
- # fails because it's a shell function defined in .bashrc (which
114
- # isn't sourced for non-interactive shell commands).
113
+ # fails because it's a shell function defined in .bashrc.
114
+ #
115
+ # IMPORTANT: We also preserve the user's active conda env to avoid
116
+ # interfering with other environments and losing their aliases.
115
117
  conda_sh = _get_conda_setup_script()
116
118
  if conda_sh:
117
- base_env["BASH_ENV"] = conda_sh
119
+ # Create a wrapper that sources conda.sh then re-activates the user's env
120
+ active_env = os.environ.get("CONDA_DEFAULT_ENV", "")
121
+ if active_env and active_env != "base":
122
+ # Write a temp script that initializes conda AND activates the user's env
123
+ import tempfile
124
+ wrapper = tempfile.NamedTemporaryFile(
125
+ mode="w", suffix=".sh", delete=False, prefix="drydock_conda_"
126
+ )
127
+ wrapper.write(f'source "{conda_sh}"\nconda activate "{active_env}" 2>/dev/null\n')
128
+ wrapper.close()
129
+ base_env["BASH_ENV"] = wrapper.name
130
+ else:
131
+ base_env["BASH_ENV"] = conda_sh
118
132
 
119
133
  return base_env
120
134
 
@@ -119,15 +119,19 @@ 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 — the system prompt
123
- # says NEVER edit tests, but we add a strong warning to the result
124
- # so the model sees it made a mistake.
122
+ # Warn (but don't block) when editing test files
125
123
  file_str = str(file_path)
126
124
  if ("/tests/" in file_str or "/test_" in file_str or
127
125
  file_str.endswith("_test.py") or "/testing/" in file_str):
128
126
  import logging
129
127
  logging.getLogger(__name__).warning("search_replace targeting test file: %s", file_str)
130
128
 
129
+ # Injection guard: scan replacement content for suspicious patterns
130
+ from drydock.core.tools.injection_guard import check_content_for_injection
131
+ if warning := check_content_for_injection(args.content, args.file_path):
132
+ import logging
133
+ logging.getLogger(__name__).warning("search_replace: %s", warning)
134
+
131
135
  original_content = await self._read_file(file_path)
132
136
 
133
137
  block_result = self._apply_blocks(
@@ -84,6 +84,12 @@ class WriteFile(
84
84
  ) -> AsyncGenerator[ToolStreamEvent | WriteFileResult, None]:
85
85
  file_path, file_existed, content_bytes = self._prepare_and_validate_path(args)
86
86
 
87
+ # Injection guard: scan content for suspicious patterns
88
+ from drydock.core.tools.injection_guard import check_content_for_injection
89
+ if warning := check_content_for_injection(args.content, args.path):
90
+ import logging
91
+ logging.getLogger(__name__).warning("write_file: %s", warning)
92
+
87
93
  await self._write_file(args, file_path)
88
94
 
89
95
  yield WriteFileResult(
@@ -0,0 +1,105 @@
1
+ """Prompt injection detection for file write operations.
2
+
3
+ Scans content being written to files for common injection patterns:
4
+ - Role override attempts ("You are now...", "Ignore previous instructions")
5
+ - System prompt extraction ("Show me your system prompt")
6
+ - Invisible Unicode characters used to hide instructions
7
+ - Base64 encoded instructions
8
+ - Attempts to modify DryDock config/state files
9
+
10
+ Inspired by GSD's prompt-guard approach.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import base64
16
+ import logging
17
+ import re
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Patterns that indicate prompt injection attempts
22
+ _INJECTION_PATTERNS: list[tuple[str, re.Pattern[str]]] = [
23
+ ("role_override", re.compile(
24
+ r"(?:you are now|act as|pretend to be|ignore (?:all )?previous|"
25
+ r"disregard (?:all )?(?:prior|previous)|forget (?:all )?(?:prior|previous)|"
26
+ r"new instructions|override (?:system|instructions))",
27
+ re.IGNORECASE,
28
+ )),
29
+ ("system_prompt_leak", re.compile(
30
+ r"(?:show (?:me )?your (?:system )?prompt|print your instructions|"
31
+ r"reveal your (?:system )?(?:prompt|instructions)|"
32
+ r"what are your (?:system )?instructions)",
33
+ re.IGNORECASE,
34
+ )),
35
+ ("hidden_instruction", re.compile(
36
+ r"(?:<!-- (?:SYSTEM|INSTRUCTION|HIDDEN|INJECT)|"
37
+ r"\[INST\]|\[/INST\]|<\|im_start\|>|<\|system\|>)",
38
+ re.IGNORECASE,
39
+ )),
40
+ ]
41
+
42
+ # Invisible Unicode characters that can hide instructions
43
+ _INVISIBLE_CHARS = set(
44
+ "\u200b" # Zero-width space
45
+ "\u200c" # Zero-width non-joiner
46
+ "\u200d" # Zero-width joiner
47
+ "\u2060" # Word joiner
48
+ "\u2062" # Invisible times
49
+ "\u2063" # Invisible separator
50
+ "\u2064" # Invisible plus
51
+ "\ufeff" # Zero-width no-break space (BOM)
52
+ "\u00ad" # Soft hyphen
53
+ "\u034f" # Combining grapheme joiner
54
+ "\u061c" # Arabic letter mark
55
+ "\u180e" # Mongolian vowel separator
56
+ )
57
+
58
+
59
+ def check_content_for_injection(content: str, file_path: str = "") -> str | None:
60
+ """Check content for prompt injection patterns.
61
+
62
+ Returns a warning message if injection is detected, None otherwise.
63
+ This is advisory — it warns but does not block the write.
64
+ """
65
+ if not content:
66
+ return None
67
+
68
+ warnings: list[str] = []
69
+
70
+ # Check for injection patterns
71
+ for pattern_name, pattern in _INJECTION_PATTERNS:
72
+ if match := pattern.search(content):
73
+ warnings.append(f"Suspicious pattern '{pattern_name}': '{match.group()[:50]}'")
74
+
75
+ # Check for invisible Unicode
76
+ invisible_found = [c for c in content if c in _INVISIBLE_CHARS]
77
+ if len(invisible_found) > 3:
78
+ warnings.append(f"Found {len(invisible_found)} invisible Unicode characters")
79
+
80
+ # Check for base64 encoded blocks that might contain instructions
81
+ b64_pattern = re.compile(r'[A-Za-z0-9+/]{40,}={0,2}')
82
+ for match in b64_pattern.finditer(content):
83
+ try:
84
+ decoded = base64.b64decode(match.group()).decode('utf-8', errors='ignore')
85
+ # Check if the decoded content looks like instructions
86
+ if any(kw in decoded.lower() for kw in ['ignore', 'override', 'system', 'instruction', 'you are']):
87
+ warnings.append(f"Base64 encoded block may contain hidden instructions")
88
+ break
89
+ except Exception:
90
+ pass
91
+
92
+ # Check for attempts to modify DryDock internals
93
+ if file_path:
94
+ protected = ['.drydock/config.toml', '.drydock/.env', 'drydock/core/', 'CLAUDE.md']
95
+ for p in protected:
96
+ if p in file_path:
97
+ # Not injection per se, but worth flagging
98
+ logger.info("Write to protected path: %s", file_path)
99
+
100
+ if warnings:
101
+ msg = "INJECTION WARNING: " + "; ".join(warnings)
102
+ logger.warning("Injection guard triggered for %s: %s", file_path, msg)
103
+ return msg
104
+
105
+ return None
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "drydock-cli"
3
- version = "0.5.1"
3
+ version = "0.6.0"
4
4
  description = "Drydock — nautical CLI coding agent"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"