drydock-cli 0.8.1__tar.gz → 0.9.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 (475) hide show
  1. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/PKG-INFO +1 -1
  2. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agent_loop.py +29 -9
  3. drydock_cli-0.9.0/drydock/core/hooks.py +179 -0
  4. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/cli.md +7 -0
  5. drydock_cli-0.9.0/drydock/core/tools/builtins/glob_tool.py +94 -0
  6. drydock_cli-0.9.0/drydock/core/tools/builtins/notebook_edit.py +96 -0
  7. drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/glob.md +9 -0
  8. drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/notebook_edit.md +7 -0
  9. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/swe_bench.md +7 -0
  10. drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/task_manager.md +8 -0
  11. drydock_cli-0.9.0/drydock/core/tools/builtins/task_manager.py +169 -0
  12. drydock_cli-0.9.0/drydock/skills/batch/SKILL.md +45 -0
  13. drydock_cli-0.9.0/drydock/skills/simplify/SKILL.md +39 -0
  14. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/pyproject.toml +3 -1
  15. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/deploy_to_github.sh +1 -1
  16. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/publish_to_pypi.sh +1 -1
  17. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/CODEOWNERS +0 -0
  18. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
  19. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  20. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  21. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/build-and-upload.yml +0 -0
  22. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/ci.yml +0 -0
  23. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/issue-labeler.yml +0 -0
  24. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/release.yml +0 -0
  25. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.gitignore +0 -0
  26. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.pre-commit-config.yaml +0 -0
  27. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.python-version +0 -0
  28. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.typos.toml +0 -0
  29. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.vscode/extensions.json +0 -0
  30. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.vscode/launch.json +0 -0
  31. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.vscode/settings.json +0 -0
  32. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/AGENTS.md +0 -0
  33. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/CHANGELOG.md +0 -0
  34. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/CLAUDE.md +0 -0
  35. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/CONTRIBUTING.md +0 -0
  36. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/Drydock_rebrand.md +0 -0
  37. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/LICENSE +0 -0
  38. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/NOTICE +0 -0
  39. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/PRD.md +0 -0
  40. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/README.md +0 -0
  41. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/action.yml +0 -0
  42. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/distribution/zed/LICENSE +0 -0
  43. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/distribution/zed/extension.toml +0 -0
  44. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/distribution/zed/icons/mistral_vibe.svg +0 -0
  45. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/docs/README.md +0 -0
  46. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/docs/acp-setup.md +0 -0
  47. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/docs/proxy-setup.md +0 -0
  48. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/__init__.py +0 -0
  49. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/__init__.py +0 -0
  50. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/acp_agent_loop.py +0 -0
  51. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/acp_logger.py +0 -0
  52. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/entrypoint.py +0 -0
  53. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/__init__.py +0 -0
  54. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/base.py +0 -0
  55. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/bash.py +0 -0
  56. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/read_file.py +0 -0
  57. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/search_replace.py +0 -0
  58. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/todo.py +0 -0
  59. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/write_file.py +0 -0
  60. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/session_update.py +0 -0
  61. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/utils.py +0 -0
  62. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/__init__.py +0 -0
  63. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/__init__.py +0 -0
  64. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/base.py +0 -0
  65. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/path_completion.py +0 -0
  66. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/slash_command.py +0 -0
  67. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/cli.py +0 -0
  68. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/clipboard.py +0 -0
  69. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/commands.py +0 -0
  70. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/entrypoint.py +0 -0
  71. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/history_manager.py +0 -0
  72. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
  73. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
  74. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
  75. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/terminal_setup.py +0 -0
  76. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/__init__.py +0 -0
  77. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
  78. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.py +0 -0
  79. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.tcss +0 -0
  80. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/external_editor.py +0 -0
  81. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
  82. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
  83. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
  84. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
  85. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
  86. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
  87. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
  88. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
  89. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
  90. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
  91. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
  92. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
  93. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
  94. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
  95. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
  96. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
  97. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
  98. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
  99. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/compact.py +0 -0
  100. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
  101. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
  102. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
  103. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/loading.py +0 -0
  104. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/messages.py +0 -0
  105. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
  106. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
  107. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
  108. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
  109. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
  110. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
  111. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
  112. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
  113. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
  114. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tools.py +0 -0
  115. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
  116. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
  117. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history.py +0 -0
  118. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
  119. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/state.py +0 -0
  120. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/__init__.py +0 -0
  121. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
  122. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
  123. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
  124. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
  125. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
  126. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/update.py +0 -0
  127. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/whats_new.py +0 -0
  128. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/__init__.py +0 -0
  129. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agents/__init__.py +0 -0
  130. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agents/manager.py +0 -0
  131. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agents/models.py +0 -0
  132. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/auth/__init__.py +0 -0
  133. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/auth/crypto.py +0 -0
  134. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/auth/github.py +0 -0
  135. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/__init__.py +0 -0
  136. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/completers.py +0 -0
  137. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
  138. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
  139. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
  140. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/store.py +0 -0
  141. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
  142. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/fuzzy.py +0 -0
  143. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt.py +0 -0
  144. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
  145. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/__init__.py +0 -0
  146. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/_settings.py +0 -0
  147. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/harness_files/__init__.py +0 -0
  148. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_harness_manager.py +0 -0
  149. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_paths.py +0 -0
  150. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/consultant.py +0 -0
  151. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/drydock_states.py +0 -0
  152. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/__init__.py +0 -0
  153. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/anthropic.py +0 -0
  154. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/base.py +0 -0
  155. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/factory.py +0 -0
  156. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/generic.py +0 -0
  157. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/mistral.py +0 -0
  158. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
  159. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/vertex.py +0 -0
  160. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/exceptions.py +0 -0
  161. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/format.py +0 -0
  162. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/message_utils.py +0 -0
  163. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/types.py +0 -0
  164. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/logger.py +0 -0
  165. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/middleware.py +0 -0
  166. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/output_formatters.py +0 -0
  167. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/__init__.py +0 -0
  168. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/_local_config_walk.py +0 -0
  169. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/_vibe_home.py +0 -0
  170. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/conventions.py +0 -0
  171. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/plan_session.py +0 -0
  172. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/programmatic.py +0 -0
  173. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/__init__.py +0 -0
  174. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/compact.md +0 -0
  175. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/dangerous_directory.md +0 -0
  176. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/diagnostic.md +0 -0
  177. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/explore.md +0 -0
  178. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/planner.md +0 -0
  179. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/project_context.md +0 -0
  180. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/tests.md +0 -0
  181. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/proxy_setup.py +0 -0
  182. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/session_loader.py +0 -0
  183. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/session_logger.py +0 -0
  184. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/session_migration.py +0 -0
  185. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/state_file.py +0 -0
  186. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/__init__.py +0 -0
  187. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/manager.py +0 -0
  188. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/models.py +0 -0
  189. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/parser.py +0 -0
  190. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/slug.py +0 -0
  191. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/system_prompt.py +0 -0
  192. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/telemetry/__init__.py +0 -0
  193. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/telemetry/send.py +0 -0
  194. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/errors.py +0 -0
  195. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/git.py +0 -0
  196. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/nuage.py +0 -0
  197. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/teleport.py +0 -0
  198. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/types.py +0 -0
  199. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/base.py +0 -0
  200. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/ask_user_question.py +0 -0
  201. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/bash.py +0 -0
  202. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
  203. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/grep.py +0 -0
  204. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
  205. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
  206. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/bash.md +0 -0
  207. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/grep.md +0 -0
  208. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
  209. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
  210. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/task.md +0 -0
  211. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/todo.md +0 -0
  212. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
  213. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
  214. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
  215. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/read_file.py +0 -0
  216. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/search_replace.py +0 -0
  217. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/task.py +0 -0
  218. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/todo.py +0 -0
  219. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/webfetch.py +0 -0
  220. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/websearch.py +0 -0
  221. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/write_file.py +0 -0
  222. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/injection_guard.py +0 -0
  223. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/manager.py +0 -0
  224. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp/__init__.py +0 -0
  225. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp/registry.py +0 -0
  226. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp/tools.py +0 -0
  227. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp_sampling.py +0 -0
  228. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/ui.py +0 -0
  229. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/utils.py +0 -0
  230. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/trusted_folders.py +0 -0
  231. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/types.py +0 -0
  232. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/utils.py +0 -0
  233. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/__init__.py +0 -0
  234. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/base.py +0 -0
  235. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/onboarding.tcss +0 -0
  236. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/__init__.py +0 -0
  237. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/api_key.py +0 -0
  238. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/welcome.py +0 -0
  239. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
  240. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
  241. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/__init__.py +0 -0
  242. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/create-presentation/SKILL.md +0 -0
  243. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/deep-research/SKILL.md +0 -0
  244. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/investigate/SKILL.md +0 -0
  245. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/review/SKILL.md +0 -0
  246. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/ship/SKILL.md +0 -0
  247. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/whats_new.md +0 -0
  248. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock-acp.spec +0 -0
  249. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock_terms.md +0 -0
  250. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/flake.lock +0 -0
  251. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/flake.nix +0 -0
  252. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/README.md +0 -0
  253. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/backup.sh +0 -0
  254. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/bump_version.py +0 -0
  255. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/install.sh +0 -0
  256. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/prepare_release.py +0 -0
  257. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/__init__.py +0 -0
  258. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/conftest.py +0 -0
  259. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_acp.py +0 -0
  260. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_agent_thought.py +0 -0
  261. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_bash.py +0 -0
  262. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_compact_session_updates.py +0 -0
  263. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_content.py +0 -0
  264. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_initialize.py +0 -0
  265. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_list_sessions.py +0 -0
  266. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_load_session.py +0 -0
  267. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_multi_session.py +0 -0
  268. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_new_session.py +0 -0
  269. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_proxy_setup_acp.py +0 -0
  270. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_read_file.py +0 -0
  271. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_search_replace.py +0 -0
  272. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_set_config_option.py +0 -0
  273. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_set_mode.py +0 -0
  274. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_set_model.py +0 -0
  275. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_tool_call_session_update.py +0 -0
  276. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_utils.py +0 -0
  277. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_write_file.py +0 -0
  278. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_file_indexer.py +0 -0
  279. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_fuzzy.py +0 -0
  280. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
  281. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_recursive.py +0 -0
  282. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completion_controller.py +0 -0
  283. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
  284. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_slash_command_controller.py +0 -0
  285. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
  286. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/__init__.py +0 -0
  287. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/data/__init__.py +0 -0
  288. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/data/fireworks.py +0 -0
  289. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/data/mistral.py +0 -0
  290. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_anthropic_adapter.py +0 -0
  291. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_backend.py +0 -0
  292. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_reasoning_adapter.py +0 -0
  293. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
  294. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
  295. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
  296. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
  297. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_bell_notifications.py +0 -0
  298. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_braille_renderer.py +0 -0
  299. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_clipboard.py +0 -0
  300. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_commands.py +0 -0
  301. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_copy_shortcuts.py +0 -0
  302. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_external_editor.py +0 -0
  303. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_no_markup_static.py +0 -0
  304. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_question_app.py +0 -0
  305. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_spinner.py +0 -0
  306. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_switching_mode.py +0 -0
  307. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_clipboard_notifications.py +0 -0
  308. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
  309. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_session_resume.py +0 -0
  310. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_skill_dispatch.py +0 -0
  311. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/textual_ui/__init__.py +0 -0
  312. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/textual_ui/test_session_picker.py +0 -0
  313. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/conftest.py +0 -0
  314. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_agents.py +0 -0
  315. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_auth_crypto.py +0 -0
  316. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_auth_github.py +0 -0
  317. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_config_load_dotenv.py +0 -0
  318. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_config_paths.py +0 -0
  319. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_config_resolution.py +0 -0
  320. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_file_logging.py +0 -0
  321. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_plan_session.py +0 -0
  322. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_proxy_setup.py +0 -0
  323. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_slug.py +0 -0
  324. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_telemetry_send.py +0 -0
  325. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_teleport_git.py +0 -0
  326. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_teleport_nuage.py +0 -0
  327. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_teleport_service.py +0 -0
  328. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_trusted_folders.py +0 -0
  329. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_utils.py +0 -0
  330. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/common.py +0 -0
  331. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/conftest.py +0 -0
  332. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/mock_server.py +0 -0
  333. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_onboarding.py +0 -0
  334. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_streaming.py +0 -0
  335. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
  336. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/__init__.py +0 -0
  337. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/mock_backend_factory.py +0 -0
  338. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/mock_entrypoint.py +0 -0
  339. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/utils.py +0 -0
  340. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/onboarding/test_run_onboarding.py +0 -0
  341. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/onboarding/test_ui_onboarding.py +0 -0
  342. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/session/test_session_loader.py +0 -0
  343. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/session/test_session_logger.py +0 -0
  344. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/session/test_session_migration.py +0 -0
  345. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/conftest.py +0 -0
  346. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/test_manager.py +0 -0
  347. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/test_models.py +0 -0
  348. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/test_parser.py +0 -0
  349. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
  350. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
  351. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
  352. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_code_block_horizontal_scrolling/test_snapshot_allows_horizontal_scrolling_for_long_code_blocks.svg +0 -0
  353. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
  354. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
  355. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
  356. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
  357. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
  358. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
  359. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
  360. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
  361. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
  362. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
  363. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
  364. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
  365. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
  366. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
  367. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
  368. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
  369. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
  370. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
  371. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
  372. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
  373. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
  374. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
  375. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
  376. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
  377. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
  378. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
  379. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
  380. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
  381. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
  382. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
  383. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
  384. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
  385. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
  386. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
  387. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
  388. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
  389. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
  390. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
  391. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
  392. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
  393. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
  394. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
  395. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
  396. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
  397. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
  398. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
  399. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
  400. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
  401. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
  402. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
  403. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
  404. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
  405. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
  406. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
  407. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
  408. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
  409. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/base_snapshot_test_app.py +0 -0
  410. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/conftest.py +0 -0
  411. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/snap_compare.py +0 -0
  412. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
  413. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
  414. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
  415. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
  416. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
  417. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
  418. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
  419. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
  420. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
  421. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
  422. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
  423. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
  424. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
  425. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
  426. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/stubs/fake_backend.py +0 -0
  427. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/stubs/fake_client.py +0 -0
  428. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/stubs/fake_tool.py +0 -0
  429. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_auto_compact.py +0 -0
  430. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_backend.py +0 -0
  431. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_observer_streaming.py +0 -0
  432. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_stats.py +0 -0
  433. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_tasks.py +0 -0
  434. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_tool_call.py +0 -0
  435. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agents.py +0 -0
  436. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_cli_programmatic_preload.py +0 -0
  437. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_current_bugs.py +0 -0
  438. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_drydock_regression.py +0 -0
  439. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_drydock_tasks.py +0 -0
  440. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_history_manager.py +0 -0
  441. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_integration.py +0 -0
  442. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_loop_detection.py +0 -0
  443. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_message_id.py +0 -0
  444. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_message_merging.py +0 -0
  445. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_middleware.py +0 -0
  446. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_real_failures.py +0 -0
  447. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_real_issues.py +0 -0
  448. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_reasoning_content.py +0 -0
  449. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_system_prompt.py +0 -0
  450. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_tagged_text.py +0 -0
  451. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_ui_external_editor.py +0 -0
  452. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_ui_input_history.py +0 -0
  453. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_user_issues.py +0 -0
  454. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_ask_user_question.py +0 -0
  455. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_bash.py +0 -0
  456. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_exit_plan_mode.py +0 -0
  457. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_grep.py +0 -0
  458. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_invoke_context.py +0 -0
  459. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_manager_get_tool_config.py +0 -0
  460. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_mcp.py +0 -0
  461. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_mcp_sampling.py +0 -0
  462. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_task.py +0 -0
  463. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_ui_bash_execution.py +0 -0
  464. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_webfetch.py +0 -0
  465. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_websearch.py +0 -0
  466. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
  467. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
  468. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_do_update.py +0 -0
  469. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
  470. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_github_update_gateway.py +0 -0
  471. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
  472. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_ui_update_notification.py +0 -0
  473. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_update_use_case.py +0 -0
  474. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_whats_new.py +0 -0
  475. {drydock_cli-0.8.1 → drydock_cli-0.9.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 0.8.1
3
+ Version: 0.9.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
@@ -599,15 +599,35 @@ class AgentLoop:
599
599
  f"Consider stopping to verify your approach."
600
600
  )
601
601
 
602
- # Bash abuse detection: if model keeps using bash instead of
603
- # search_replace/read_file, redirect it to the proper tools
604
- if not has_made_edit and bash_count >= 10 and bash_count % 5 == 0:
605
- self._inject_system_note(
606
- f"STOP using bash. You have run {bash_count} bash commands without making an edit. "
607
- "Use search_replace to edit files, not bash. Use read_file to read files, not cat/head. "
608
- "Use grep (the tool) to search, not bash grep. "
609
- "Call search_replace NOW with your fix."
610
- )
602
+ # Bash abuse detection: model uses bash instead of proper tools
603
+ # This is the #1 cause of no-patch failures (88% of cases)
604
+ if not has_made_edit and bash_count >= 5:
605
+ if bash_count == 5:
606
+ self.messages.append(LLMMessage(
607
+ role=Role.user,
608
+ content=(
609
+ "You have run 5 bash commands without making any edit. "
610
+ "STOP using bash for reading/searching. Use the proper tools: "
611
+ "read_file (not cat), grep (not bash grep), search_replace (not sed). "
612
+ "Call search_replace NOW to fix the bug."
613
+ )
614
+ ))
615
+ elif bash_count == 8:
616
+ self.messages.append(LLMMessage(
617
+ role=Role.user,
618
+ content=(
619
+ "WARNING: 8 bash commands, ZERO edits. You are wasting context. "
620
+ "Bash does NOT fix bugs — search_replace does. "
621
+ "Use search_replace on your NEXT response or the session ends."
622
+ )
623
+ ))
624
+ elif bash_count >= 12:
625
+ # Force stop — model is clearly stuck in bash loop
626
+ yield AssistantEvent(
627
+ content="\n\n[STOPPED: 12+ bash commands without any edit. Use search_replace.]\n",
628
+ stopped_by_middleware=True,
629
+ )
630
+ return
611
631
 
612
632
  # 3-Strike Rule: search_replace keeps failing
613
633
  if search_replace_failures >= 3:
@@ -0,0 +1,179 @@
1
+ """Hook system for DryDock lifecycle events.
2
+
3
+ Hooks run shell scripts or Python callables at specific points
4
+ in the agent's workflow. They are deterministic — they always run,
5
+ unlike CLAUDE.md which is advisory.
6
+
7
+ Hook events:
8
+ - PreToolUse: Before any tool executes (can block with "deny")
9
+ - PostToolUse: After a tool completes
10
+ - SessionStart: When a new session begins
11
+ - SessionEnd: When session ends
12
+ - PreEdit: Before file write/edit (can block destructive patterns)
13
+ - PostEdit: After file is written/edited
14
+
15
+ Configure in ~/.drydock/hooks.json or .drydock/hooks.json:
16
+ {
17
+ "hooks": [
18
+ {
19
+ "event": "PreToolUse",
20
+ "command": "python3 /path/to/guard.py",
21
+ "tools": ["bash", "write_file"]
22
+ }
23
+ ]
24
+ }
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import logging
31
+ import subprocess
32
+ from dataclasses import dataclass, field
33
+ from enum import StrEnum, auto
34
+ from pathlib import Path
35
+ from typing import Any
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class HookEvent(StrEnum):
41
+ PRE_TOOL_USE = auto()
42
+ POST_TOOL_USE = auto()
43
+ SESSION_START = auto()
44
+ SESSION_END = auto()
45
+ PRE_EDIT = auto()
46
+ POST_EDIT = auto()
47
+
48
+
49
+ class HookDecision(StrEnum):
50
+ ALLOW = auto()
51
+ DENY = auto()
52
+ ASK = auto()
53
+
54
+
55
+ @dataclass
56
+ class HookResult:
57
+ decision: HookDecision = HookDecision.ALLOW
58
+ message: str = ""
59
+
60
+
61
+ @dataclass
62
+ class HookConfig:
63
+ event: HookEvent
64
+ command: str
65
+ tools: list[str] = field(default_factory=list) # Empty = all tools
66
+ timeout: int = 10
67
+
68
+
69
+ class HookManager:
70
+ """Manages and executes lifecycle hooks."""
71
+
72
+ def __init__(self) -> None:
73
+ self._hooks: list[HookConfig] = []
74
+ self._loaded = False
75
+
76
+ def load_hooks(self, config_dir: Path | None = None) -> None:
77
+ """Load hooks from config files."""
78
+ if self._loaded:
79
+ return
80
+ self._loaded = True
81
+
82
+ search_paths = []
83
+ if config_dir:
84
+ search_paths.append(config_dir / "hooks.json")
85
+
86
+ # Check project and user dirs
87
+ project_hooks = Path.cwd() / ".drydock" / "hooks.json"
88
+ if project_hooks.exists():
89
+ search_paths.insert(0, project_hooks)
90
+
91
+ try:
92
+ from drydock.core.paths import VIBE_HOME
93
+ user_hooks = VIBE_HOME.path / "hooks.json"
94
+ if user_hooks.exists():
95
+ search_paths.append(user_hooks)
96
+ except Exception:
97
+ pass
98
+
99
+ for path in search_paths:
100
+ try:
101
+ if path.exists():
102
+ data = json.loads(path.read_text())
103
+ for h in data.get("hooks", []):
104
+ self._hooks.append(HookConfig(
105
+ event=HookEvent(h["event"].lower().replace("-", "_")),
106
+ command=h["command"],
107
+ tools=h.get("tools", []),
108
+ timeout=h.get("timeout", 10),
109
+ ))
110
+ logger.info("Loaded %d hooks from %s", len(data.get("hooks", [])), path)
111
+ except Exception as e:
112
+ logger.warning("Failed to load hooks from %s: %s", path, e)
113
+
114
+ def get_hooks(self, event: HookEvent, tool_name: str = "") -> list[HookConfig]:
115
+ """Get hooks matching an event and optional tool name."""
116
+ return [
117
+ h for h in self._hooks
118
+ if h.event == event
119
+ and (not h.tools or tool_name in h.tools)
120
+ ]
121
+
122
+ async def run_hooks(
123
+ self, event: HookEvent, tool_name: str = "", context: dict[str, Any] | None = None
124
+ ) -> HookResult:
125
+ """Execute all hooks for an event. Returns the most restrictive result."""
126
+ hooks = self.get_hooks(event, tool_name)
127
+ if not hooks:
128
+ return HookResult()
129
+
130
+ most_restrictive = HookResult()
131
+
132
+ for hook in hooks:
133
+ try:
134
+ result = await self._execute_hook(hook, context or {})
135
+ if result.decision == HookDecision.DENY:
136
+ return result # Deny immediately
137
+ if result.decision == HookDecision.ASK and most_restrictive.decision == HookDecision.ALLOW:
138
+ most_restrictive = result
139
+ except Exception as e:
140
+ logger.warning("Hook %s failed: %s", hook.command, e)
141
+
142
+ return most_restrictive
143
+
144
+ async def _execute_hook(self, hook: HookConfig, context: dict[str, Any]) -> HookResult:
145
+ """Execute a single hook command."""
146
+ import asyncio
147
+
148
+ env = {
149
+ **__import__("os").environ,
150
+ "DRYDOCK_HOOK_EVENT": hook.event.value,
151
+ "DRYDOCK_HOOK_CONTEXT": json.dumps(context, default=str),
152
+ }
153
+
154
+ try:
155
+ proc = await asyncio.create_subprocess_shell(
156
+ hook.command,
157
+ stdout=asyncio.subprocess.PIPE,
158
+ stderr=asyncio.subprocess.PIPE,
159
+ env=env,
160
+ )
161
+ stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=hook.timeout)
162
+
163
+ output = stdout.decode("utf-8", errors="replace").strip()
164
+
165
+ # Parse output for decision
166
+ if "DENY" in output.upper():
167
+ return HookResult(decision=HookDecision.DENY, message=output)
168
+ if "ASK" in output.upper():
169
+ return HookResult(decision=HookDecision.ASK, message=output)
170
+
171
+ return HookResult(decision=HookDecision.ALLOW, message=output)
172
+
173
+ except asyncio.TimeoutError:
174
+ logger.warning("Hook timed out: %s", hook.command)
175
+ return HookResult()
176
+
177
+
178
+ # Global hook manager instance
179
+ hook_manager = HookManager()
@@ -161,6 +161,13 @@ Bug Fixing in Open-Source Repos
161
161
 
162
162
  NEVER edit test files. The bug is in the library source code, not the tests.
163
163
 
164
+ CRITICAL — Tool Usage:
165
+ - To EDIT files: use search_replace. NEVER use bash with sed/awk/echo to edit files.
166
+ - To READ files: use read_file. NEVER use bash with cat/head/tail.
167
+ - To SEARCH code: use grep. NEVER use bash with grep/find/rg.
168
+ - bash is ONLY for: running tests, checking git status, installing packages, running scripts.
169
+ - If you have not called search_replace, you have not fixed anything. Bash commands do not fix bugs.
170
+
164
171
  Context Budget:
165
172
  Your context window is your most important resource. Every grep result and file read consumes it.
166
173
  Performance degrades as context fills. Budget your investigation:
@@ -0,0 +1,94 @@
1
+ """Glob tool — fast file pattern matching.
2
+
3
+ Matches files by glob pattern (e.g., "**/*.py", "src/**/*.ts").
4
+ Returns sorted file paths. Read-only, always auto-approved.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import AsyncGenerator
10
+ from pathlib import Path
11
+ from typing import TYPE_CHECKING, ClassVar, final
12
+
13
+ from pydantic import BaseModel, Field
14
+
15
+ from drydock.core.tools.base import (
16
+ BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
17
+ )
18
+ from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
19
+ from drydock.core.types import ToolStreamEvent
20
+
21
+ if TYPE_CHECKING:
22
+ from drydock.core.types import ToolResultEvent
23
+
24
+
25
+ class GlobArgs(BaseModel):
26
+ pattern: str = Field(description="Glob pattern (e.g., '**/*.py', 'src/**/*.ts')")
27
+ path: str = Field(default=".", description="Directory to search in")
28
+
29
+
30
+ class GlobResult(BaseModel):
31
+ files: list[str]
32
+ count: int
33
+ truncated: bool = False
34
+
35
+
36
+ class GlobConfig(BaseToolConfig):
37
+ permission: ToolPermission = ToolPermission.ALWAYS
38
+ max_results: int = 200
39
+
40
+
41
+ class Glob(
42
+ BaseTool[GlobArgs, GlobResult, GlobConfig, BaseToolState],
43
+ ToolUIData[GlobArgs, GlobResult],
44
+ ):
45
+ description: ClassVar[str] = (
46
+ "Find files matching a glob pattern. Fast file discovery."
47
+ )
48
+
49
+ @classmethod
50
+ def format_call_display(cls, args: GlobArgs) -> ToolCallDisplay:
51
+ return ToolCallDisplay(summary=f"glob: {args.pattern}")
52
+
53
+ @classmethod
54
+ def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
55
+ if isinstance(event.result, GlobResult):
56
+ return ToolResultDisplay(
57
+ success=True, message=f"Found {event.result.count} files"
58
+ )
59
+ return ToolResultDisplay(success=True, message="Glob complete")
60
+
61
+ @classmethod
62
+ def get_status_text(cls) -> str:
63
+ return "Searching files"
64
+
65
+ def resolve_permission(self, args: GlobArgs) -> ToolPermission | None:
66
+ return ToolPermission.ALWAYS
67
+
68
+ @final
69
+ async def run(
70
+ self, args: GlobArgs, ctx: InvokeContext | None = None
71
+ ) -> AsyncGenerator[ToolStreamEvent | GlobResult, None]:
72
+ search_dir = Path(args.path).expanduser()
73
+ if not search_dir.is_absolute():
74
+ search_dir = Path.cwd() / search_dir
75
+
76
+ if not search_dir.is_dir():
77
+ raise ToolError(f"Directory not found: {search_dir}")
78
+
79
+ try:
80
+ matches = sorted(
81
+ str(p.relative_to(search_dir))
82
+ for p in search_dir.glob(args.pattern)
83
+ if p.is_file()
84
+ and "__pycache__" not in str(p)
85
+ and ".git/" not in str(p)
86
+ and "node_modules/" not in str(p)
87
+ )
88
+ except Exception as e:
89
+ raise ToolError(f"Glob error: {e}") from e
90
+
91
+ truncated = len(matches) > self.config.max_results
92
+ files = matches[:self.config.max_results]
93
+
94
+ yield GlobResult(files=files, count=len(matches), truncated=truncated)
@@ -0,0 +1,96 @@
1
+ """NotebookEdit tool — edit Jupyter notebook cells.
2
+
3
+ Supports editing code and markdown cells in .ipynb files.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ from collections.abc import AsyncGenerator
10
+ from pathlib import Path
11
+ from typing import ClassVar, final
12
+
13
+ from pydantic import BaseModel, Field
14
+
15
+ from drydock.core.tools.base import (
16
+ BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
17
+ )
18
+ from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
19
+ from drydock.core.types import ToolStreamEvent, ToolResultEvent
20
+
21
+
22
+ class NotebookEditArgs(BaseModel):
23
+ path: str = Field(description="Path to the .ipynb file")
24
+ cell_index: int = Field(description="Cell index to edit (0-based)")
25
+ new_source: str = Field(description="New source content for the cell")
26
+ cell_type: str = Field(default="code", description="Cell type: 'code' or 'markdown'")
27
+
28
+
29
+ class NotebookEditResult(BaseModel):
30
+ path: str
31
+ cell_index: int
32
+ cell_type: str
33
+ total_cells: int
34
+
35
+
36
+ class NotebookEdit(
37
+ BaseTool[NotebookEditArgs, NotebookEditResult, BaseToolConfig, BaseToolState],
38
+ ToolUIData[NotebookEditArgs, NotebookEditResult],
39
+ ):
40
+ description: ClassVar[str] = "Edit a cell in a Jupyter notebook (.ipynb file)."
41
+
42
+ @classmethod
43
+ def format_call_display(cls, args: NotebookEditArgs) -> ToolCallDisplay:
44
+ return ToolCallDisplay(summary=f"notebook_edit: cell {args.cell_index} in {args.path}")
45
+
46
+ @classmethod
47
+ def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
48
+ if isinstance(event.result, NotebookEditResult):
49
+ return ToolResultDisplay(success=True, message=f"Edited cell {event.result.cell_index}")
50
+ return ToolResultDisplay(success=True, message="Notebook edited")
51
+
52
+ @classmethod
53
+ def get_status_text(cls) -> str:
54
+ return "Editing notebook"
55
+
56
+ @final
57
+ async def run(
58
+ self, args: NotebookEditArgs, ctx: InvokeContext | None = None
59
+ ) -> AsyncGenerator[ToolStreamEvent | NotebookEditResult, None]:
60
+ file_path = Path(args.path).expanduser()
61
+ if not file_path.is_absolute():
62
+ file_path = Path.cwd() / file_path
63
+
64
+ if not file_path.exists():
65
+ raise ToolError(f"Notebook not found: {file_path}")
66
+
67
+ if not str(file_path).endswith(".ipynb"):
68
+ raise ToolError("File must be a .ipynb notebook")
69
+
70
+ try:
71
+ with open(file_path, encoding="utf-8") as f:
72
+ notebook = json.load(f)
73
+ except (json.JSONDecodeError, OSError) as e:
74
+ raise ToolError(f"Cannot read notebook: {e}") from e
75
+
76
+ cells = notebook.get("cells", [])
77
+ if args.cell_index < 0 or args.cell_index >= len(cells):
78
+ raise ToolError(f"Cell index {args.cell_index} out of range (0-{len(cells)-1})")
79
+
80
+ # Update the cell
81
+ cell = cells[args.cell_index]
82
+ cell["source"] = args.new_source.split("\n")
83
+ cell["cell_type"] = args.cell_type
84
+
85
+ try:
86
+ with open(file_path, "w", encoding="utf-8") as f:
87
+ json.dump(notebook, f, indent=1, ensure_ascii=False)
88
+ except OSError as e:
89
+ raise ToolError(f"Cannot write notebook: {e}") from e
90
+
91
+ yield NotebookEditResult(
92
+ path=str(file_path),
93
+ cell_index=args.cell_index,
94
+ cell_type=args.cell_type,
95
+ total_cells=len(cells),
96
+ )
@@ -0,0 +1,9 @@
1
+ Find files matching a glob pattern. Returns sorted file paths.
2
+
3
+ Examples:
4
+ - `glob(pattern="**/*.py")` — all Python files
5
+ - `glob(pattern="src/**/*.ts", path=".")` — TypeScript files under src/
6
+ - `glob(pattern="*.md")` — markdown files in current directory
7
+ - `glob(pattern="**/test_*.py")` — all test files
8
+
9
+ Use this instead of `bash find` for file discovery.
@@ -0,0 +1,7 @@
1
+ Edit a cell in a Jupyter notebook (.ipynb file).
2
+
3
+ Arguments:
4
+ - path: Path to the .ipynb file
5
+ - cell_index: Cell index to edit (0-based)
6
+ - new_source: New source content for the cell
7
+ - cell_type: "code" or "markdown" (default: "code")
@@ -29,3 +29,10 @@ COMMON PATTERNS:
29
29
  - Missing import: grep for where the symbol is defined, add the import
30
30
  - Off-by-one: check loop bounds and slice indices
31
31
  - Unhashable type: wrap mutable container in tuple() before hashing
32
+
33
+ AFTER YOUR FIRST EDIT:
34
+ After making your first edit, ALWAYS check:
35
+ 1. grep for other files that import/use the function you changed
36
+ 2. If you edited a base class, check if subclasses override the method
37
+ 3. If you edited a config/settings file, check if there's a related migration
38
+ 4. If you edited a backend, check if other backends need the same fix
@@ -0,0 +1,8 @@
1
+ Track work items during a session.
2
+
3
+ Tools:
4
+ - `task_create(title, description)` — Create a new task
5
+ - `task_list()` — List all tasks and their status
6
+ - `task_update(task_id, status, notes)` — Update status: pending, in_progress, completed, blocked
7
+
8
+ Use these to break complex work into trackable steps.
@@ -0,0 +1,169 @@
1
+ """Task lifecycle tools — create, get, list, update tasks.
2
+
3
+ Provides interactive task management for tracking work items
4
+ during a session, similar to Claude Code's TaskCreate/Get/List/Update.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from collections.abc import AsyncGenerator
11
+ from pathlib import Path
12
+ from typing import ClassVar, final
13
+ from uuid import uuid4
14
+
15
+ from pydantic import BaseModel, Field
16
+
17
+ from drydock.core.tools.base import (
18
+ BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
19
+ )
20
+ from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
21
+ from drydock.core.types import ToolStreamEvent, ToolResultEvent
22
+
23
+ # In-memory task store (per session)
24
+ _TASKS: dict[str, dict] = {}
25
+ _TASK_COUNTER = 0
26
+
27
+
28
+ class TaskCreateArgs(BaseModel):
29
+ title: str = Field(description="Brief title for the task")
30
+ description: str = Field(default="", description="Detailed description")
31
+
32
+
33
+ class TaskCreateResult(BaseModel):
34
+ task_id: str
35
+ title: str
36
+ status: str = "pending"
37
+
38
+
39
+ class TaskCreate(
40
+ BaseTool[TaskCreateArgs, TaskCreateResult, BaseToolConfig, BaseToolState],
41
+ ToolUIData[TaskCreateArgs, TaskCreateResult],
42
+ ):
43
+ description: ClassVar[str] = "Create a new task to track work."
44
+
45
+ @classmethod
46
+ def format_call_display(cls, args: TaskCreateArgs) -> ToolCallDisplay:
47
+ return ToolCallDisplay(summary=f"task_create: {args.title[:50]}")
48
+
49
+ @classmethod
50
+ def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
51
+ if isinstance(event.result, TaskCreateResult):
52
+ return ToolResultDisplay(success=True, message=f"Created task #{event.result.task_id}")
53
+ return ToolResultDisplay(success=True, message="Task created")
54
+
55
+ @classmethod
56
+ def get_status_text(cls) -> str:
57
+ return "Creating task"
58
+
59
+ def resolve_permission(self, args: TaskCreateArgs) -> ToolPermission | None:
60
+ return ToolPermission.ALWAYS
61
+
62
+ @final
63
+ async def run(
64
+ self, args: TaskCreateArgs, ctx: InvokeContext | None = None
65
+ ) -> AsyncGenerator[ToolStreamEvent | TaskCreateResult, None]:
66
+ global _TASK_COUNTER
67
+ _TASK_COUNTER += 1
68
+ task_id = str(_TASK_COUNTER)
69
+
70
+ _TASKS[task_id] = {
71
+ "id": task_id,
72
+ "title": args.title,
73
+ "description": args.description,
74
+ "status": "pending",
75
+ }
76
+
77
+ yield TaskCreateResult(task_id=task_id, title=args.title)
78
+
79
+
80
+ class TaskListArgs(BaseModel):
81
+ pass
82
+
83
+
84
+ class TaskListResult(BaseModel):
85
+ tasks: list[dict]
86
+ count: int
87
+
88
+
89
+ class TaskList(
90
+ BaseTool[TaskListArgs, TaskListResult, BaseToolConfig, BaseToolState],
91
+ ToolUIData[TaskListArgs, TaskListResult],
92
+ ):
93
+ description: ClassVar[str] = "List all tasks and their status."
94
+
95
+ @classmethod
96
+ def format_call_display(cls, args: TaskListArgs) -> ToolCallDisplay:
97
+ return ToolCallDisplay(summary="task_list")
98
+
99
+ @classmethod
100
+ def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
101
+ if isinstance(event.result, TaskListResult):
102
+ return ToolResultDisplay(success=True, message=f"{event.result.count} tasks")
103
+ return ToolResultDisplay(success=True, message="Tasks listed")
104
+
105
+ @classmethod
106
+ def get_status_text(cls) -> str:
107
+ return "Listing tasks"
108
+
109
+ def resolve_permission(self, args: TaskListArgs) -> ToolPermission | None:
110
+ return ToolPermission.ALWAYS
111
+
112
+ @final
113
+ async def run(
114
+ self, args: TaskListArgs, ctx: InvokeContext | None = None
115
+ ) -> AsyncGenerator[ToolStreamEvent | TaskListResult, None]:
116
+ tasks = list(_TASKS.values())
117
+ yield TaskListResult(tasks=tasks, count=len(tasks))
118
+
119
+
120
+ class TaskUpdateArgs(BaseModel):
121
+ task_id: str = Field(description="Task ID to update")
122
+ status: str = Field(description="New status: pending, in_progress, completed, blocked")
123
+ notes: str = Field(default="", description="Optional notes")
124
+
125
+
126
+ class TaskUpdateResult(BaseModel):
127
+ task_id: str
128
+ status: str
129
+ title: str
130
+
131
+
132
+ class TaskUpdate(
133
+ BaseTool[TaskUpdateArgs, TaskUpdateResult, BaseToolConfig, BaseToolState],
134
+ ToolUIData[TaskUpdateArgs, TaskUpdateResult],
135
+ ):
136
+ description: ClassVar[str] = "Update a task's status."
137
+
138
+ @classmethod
139
+ def format_call_display(cls, args: TaskUpdateArgs) -> ToolCallDisplay:
140
+ return ToolCallDisplay(summary=f"task_update: #{args.task_id} → {args.status}")
141
+
142
+ @classmethod
143
+ def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
144
+ if isinstance(event.result, TaskUpdateResult):
145
+ return ToolResultDisplay(success=True, message=f"#{event.result.task_id} → {event.result.status}")
146
+ return ToolResultDisplay(success=True, message="Task updated")
147
+
148
+ @classmethod
149
+ def get_status_text(cls) -> str:
150
+ return "Updating task"
151
+
152
+ def resolve_permission(self, args: TaskUpdateArgs) -> ToolPermission | None:
153
+ return ToolPermission.ALWAYS
154
+
155
+ @final
156
+ async def run(
157
+ self, args: TaskUpdateArgs, ctx: InvokeContext | None = None
158
+ ) -> AsyncGenerator[ToolStreamEvent | TaskUpdateResult, None]:
159
+ if args.task_id not in _TASKS:
160
+ raise ToolError(f"Task #{args.task_id} not found")
161
+
162
+ task = _TASKS[args.task_id]
163
+ task["status"] = args.status
164
+ if args.notes:
165
+ task["notes"] = args.notes
166
+
167
+ yield TaskUpdateResult(
168
+ task_id=args.task_id, status=args.status, title=task["title"]
169
+ )