drydock-cli 0.8.2__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.2 → drydock_cli-0.9.0}/PKG-INFO +1 -1
  2. drydock_cli-0.9.0/drydock/core/hooks.py +179 -0
  3. drydock_cli-0.9.0/drydock/core/tools/builtins/glob_tool.py +94 -0
  4. drydock_cli-0.9.0/drydock/core/tools/builtins/notebook_edit.py +96 -0
  5. drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/glob.md +9 -0
  6. drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/notebook_edit.md +7 -0
  7. drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/task_manager.md +8 -0
  8. drydock_cli-0.9.0/drydock/core/tools/builtins/task_manager.py +169 -0
  9. drydock_cli-0.9.0/drydock/skills/batch/SKILL.md +45 -0
  10. drydock_cli-0.9.0/drydock/skills/simplify/SKILL.md +39 -0
  11. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/pyproject.toml +3 -1
  12. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/CODEOWNERS +0 -0
  13. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
  14. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  15. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  16. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/build-and-upload.yml +0 -0
  17. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/ci.yml +0 -0
  18. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/issue-labeler.yml +0 -0
  19. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/release.yml +0 -0
  20. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.gitignore +0 -0
  21. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.pre-commit-config.yaml +0 -0
  22. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.python-version +0 -0
  23. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.typos.toml +0 -0
  24. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.vscode/extensions.json +0 -0
  25. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.vscode/launch.json +0 -0
  26. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.vscode/settings.json +0 -0
  27. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/AGENTS.md +0 -0
  28. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/CHANGELOG.md +0 -0
  29. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/CLAUDE.md +0 -0
  30. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/CONTRIBUTING.md +0 -0
  31. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/Drydock_rebrand.md +0 -0
  32. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/LICENSE +0 -0
  33. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/NOTICE +0 -0
  34. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/PRD.md +0 -0
  35. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/README.md +0 -0
  36. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/action.yml +0 -0
  37. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/distribution/zed/LICENSE +0 -0
  38. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/distribution/zed/extension.toml +0 -0
  39. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/distribution/zed/icons/mistral_vibe.svg +0 -0
  40. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/docs/README.md +0 -0
  41. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/docs/acp-setup.md +0 -0
  42. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/docs/proxy-setup.md +0 -0
  43. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/__init__.py +0 -0
  44. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/__init__.py +0 -0
  45. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/acp_agent_loop.py +0 -0
  46. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/acp_logger.py +0 -0
  47. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/entrypoint.py +0 -0
  48. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/__init__.py +0 -0
  49. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/base.py +0 -0
  50. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/bash.py +0 -0
  51. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/read_file.py +0 -0
  52. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/search_replace.py +0 -0
  53. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/todo.py +0 -0
  54. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/write_file.py +0 -0
  55. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/session_update.py +0 -0
  56. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/utils.py +0 -0
  57. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/__init__.py +0 -0
  58. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/__init__.py +0 -0
  59. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/base.py +0 -0
  60. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/path_completion.py +0 -0
  61. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/slash_command.py +0 -0
  62. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/cli.py +0 -0
  63. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/clipboard.py +0 -0
  64. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/commands.py +0 -0
  65. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/entrypoint.py +0 -0
  66. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/history_manager.py +0 -0
  67. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
  68. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
  69. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
  70. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/terminal_setup.py +0 -0
  71. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/__init__.py +0 -0
  72. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
  73. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.py +0 -0
  74. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.tcss +0 -0
  75. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/external_editor.py +0 -0
  76. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
  77. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
  78. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
  79. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
  80. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
  81. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
  82. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
  83. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
  84. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
  85. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
  86. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
  87. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
  88. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
  89. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
  90. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
  91. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
  92. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
  93. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
  94. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/compact.py +0 -0
  95. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
  96. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
  97. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
  98. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/loading.py +0 -0
  99. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/messages.py +0 -0
  100. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
  101. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
  102. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
  103. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
  104. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
  105. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
  106. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
  107. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
  108. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
  109. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tools.py +0 -0
  110. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
  111. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
  112. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history.py +0 -0
  113. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
  114. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/state.py +0 -0
  115. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/__init__.py +0 -0
  116. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
  117. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
  118. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
  119. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
  120. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
  121. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/update.py +0 -0
  122. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/whats_new.py +0 -0
  123. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/__init__.py +0 -0
  124. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agent_loop.py +0 -0
  125. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agents/__init__.py +0 -0
  126. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agents/manager.py +0 -0
  127. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agents/models.py +0 -0
  128. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/auth/__init__.py +0 -0
  129. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/auth/crypto.py +0 -0
  130. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/auth/github.py +0 -0
  131. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/__init__.py +0 -0
  132. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/completers.py +0 -0
  133. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
  134. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
  135. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
  136. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/store.py +0 -0
  137. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
  138. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/fuzzy.py +0 -0
  139. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt.py +0 -0
  140. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
  141. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/__init__.py +0 -0
  142. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/_settings.py +0 -0
  143. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/harness_files/__init__.py +0 -0
  144. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_harness_manager.py +0 -0
  145. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_paths.py +0 -0
  146. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/consultant.py +0 -0
  147. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/drydock_states.py +0 -0
  148. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/__init__.py +0 -0
  149. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/anthropic.py +0 -0
  150. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/base.py +0 -0
  151. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/factory.py +0 -0
  152. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/generic.py +0 -0
  153. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/mistral.py +0 -0
  154. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
  155. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/vertex.py +0 -0
  156. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/exceptions.py +0 -0
  157. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/format.py +0 -0
  158. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/message_utils.py +0 -0
  159. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/types.py +0 -0
  160. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/logger.py +0 -0
  161. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/middleware.py +0 -0
  162. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/output_formatters.py +0 -0
  163. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/__init__.py +0 -0
  164. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/_local_config_walk.py +0 -0
  165. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/_vibe_home.py +0 -0
  166. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/conventions.py +0 -0
  167. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/plan_session.py +0 -0
  168. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/programmatic.py +0 -0
  169. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/__init__.py +0 -0
  170. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/cli.md +0 -0
  171. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/compact.md +0 -0
  172. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/dangerous_directory.md +0 -0
  173. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/diagnostic.md +0 -0
  174. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/explore.md +0 -0
  175. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/planner.md +0 -0
  176. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/project_context.md +0 -0
  177. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/tests.md +0 -0
  178. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/proxy_setup.py +0 -0
  179. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/session_loader.py +0 -0
  180. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/session_logger.py +0 -0
  181. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/session_migration.py +0 -0
  182. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/state_file.py +0 -0
  183. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/__init__.py +0 -0
  184. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/manager.py +0 -0
  185. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/models.py +0 -0
  186. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/parser.py +0 -0
  187. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/slug.py +0 -0
  188. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/system_prompt.py +0 -0
  189. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/telemetry/__init__.py +0 -0
  190. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/telemetry/send.py +0 -0
  191. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/errors.py +0 -0
  192. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/git.py +0 -0
  193. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/nuage.py +0 -0
  194. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/teleport.py +0 -0
  195. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/types.py +0 -0
  196. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/base.py +0 -0
  197. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/ask_user_question.py +0 -0
  198. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/bash.py +0 -0
  199. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
  200. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/grep.py +0 -0
  201. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
  202. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
  203. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/bash.md +0 -0
  204. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/grep.md +0 -0
  205. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
  206. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
  207. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
  208. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/task.md +0 -0
  209. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/todo.md +0 -0
  210. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
  211. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
  212. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
  213. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/read_file.py +0 -0
  214. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/search_replace.py +0 -0
  215. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/task.py +0 -0
  216. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/todo.py +0 -0
  217. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/webfetch.py +0 -0
  218. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/websearch.py +0 -0
  219. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/write_file.py +0 -0
  220. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/injection_guard.py +0 -0
  221. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/manager.py +0 -0
  222. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp/__init__.py +0 -0
  223. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp/registry.py +0 -0
  224. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp/tools.py +0 -0
  225. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp_sampling.py +0 -0
  226. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/ui.py +0 -0
  227. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/utils.py +0 -0
  228. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/trusted_folders.py +0 -0
  229. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/types.py +0 -0
  230. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/utils.py +0 -0
  231. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/__init__.py +0 -0
  232. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/base.py +0 -0
  233. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/onboarding.tcss +0 -0
  234. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/__init__.py +0 -0
  235. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/api_key.py +0 -0
  236. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/welcome.py +0 -0
  237. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
  238. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
  239. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/__init__.py +0 -0
  240. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/create-presentation/SKILL.md +0 -0
  241. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/deep-research/SKILL.md +0 -0
  242. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/investigate/SKILL.md +0 -0
  243. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/review/SKILL.md +0 -0
  244. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/ship/SKILL.md +0 -0
  245. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/whats_new.md +0 -0
  246. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock-acp.spec +0 -0
  247. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock_terms.md +0 -0
  248. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/flake.lock +0 -0
  249. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/flake.nix +0 -0
  250. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/README.md +0 -0
  251. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/backup.sh +0 -0
  252. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/bump_version.py +0 -0
  253. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/deploy_to_github.sh +0 -0
  254. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/install.sh +0 -0
  255. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/prepare_release.py +0 -0
  256. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/publish_to_pypi.sh +0 -0
  257. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/__init__.py +0 -0
  258. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/conftest.py +0 -0
  259. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_acp.py +0 -0
  260. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_agent_thought.py +0 -0
  261. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_bash.py +0 -0
  262. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_compact_session_updates.py +0 -0
  263. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_content.py +0 -0
  264. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_initialize.py +0 -0
  265. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_list_sessions.py +0 -0
  266. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_load_session.py +0 -0
  267. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_multi_session.py +0 -0
  268. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_new_session.py +0 -0
  269. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_proxy_setup_acp.py +0 -0
  270. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_read_file.py +0 -0
  271. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_search_replace.py +0 -0
  272. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_set_config_option.py +0 -0
  273. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_set_mode.py +0 -0
  274. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_set_model.py +0 -0
  275. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_tool_call_session_update.py +0 -0
  276. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_utils.py +0 -0
  277. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_write_file.py +0 -0
  278. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_file_indexer.py +0 -0
  279. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_fuzzy.py +0 -0
  280. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
  281. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_recursive.py +0 -0
  282. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completion_controller.py +0 -0
  283. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
  284. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_slash_command_controller.py +0 -0
  285. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
  286. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/__init__.py +0 -0
  287. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/data/__init__.py +0 -0
  288. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/data/fireworks.py +0 -0
  289. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/data/mistral.py +0 -0
  290. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_anthropic_adapter.py +0 -0
  291. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_backend.py +0 -0
  292. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_reasoning_adapter.py +0 -0
  293. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
  294. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
  295. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
  296. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
  297. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_bell_notifications.py +0 -0
  298. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_braille_renderer.py +0 -0
  299. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_clipboard.py +0 -0
  300. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_commands.py +0 -0
  301. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_copy_shortcuts.py +0 -0
  302. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_external_editor.py +0 -0
  303. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_no_markup_static.py +0 -0
  304. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_question_app.py +0 -0
  305. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_spinner.py +0 -0
  306. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_switching_mode.py +0 -0
  307. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_clipboard_notifications.py +0 -0
  308. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
  309. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_session_resume.py +0 -0
  310. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_skill_dispatch.py +0 -0
  311. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/textual_ui/__init__.py +0 -0
  312. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/textual_ui/test_session_picker.py +0 -0
  313. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/conftest.py +0 -0
  314. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_agents.py +0 -0
  315. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_auth_crypto.py +0 -0
  316. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_auth_github.py +0 -0
  317. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_config_load_dotenv.py +0 -0
  318. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_config_paths.py +0 -0
  319. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_config_resolution.py +0 -0
  320. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_file_logging.py +0 -0
  321. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_plan_session.py +0 -0
  322. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_proxy_setup.py +0 -0
  323. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_slug.py +0 -0
  324. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_telemetry_send.py +0 -0
  325. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_teleport_git.py +0 -0
  326. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_teleport_nuage.py +0 -0
  327. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_teleport_service.py +0 -0
  328. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_trusted_folders.py +0 -0
  329. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_utils.py +0 -0
  330. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/common.py +0 -0
  331. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/conftest.py +0 -0
  332. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/mock_server.py +0 -0
  333. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_onboarding.py +0 -0
  334. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_streaming.py +0 -0
  335. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
  336. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/__init__.py +0 -0
  337. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/mock_backend_factory.py +0 -0
  338. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/mock_entrypoint.py +0 -0
  339. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/utils.py +0 -0
  340. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/onboarding/test_run_onboarding.py +0 -0
  341. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/onboarding/test_ui_onboarding.py +0 -0
  342. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/session/test_session_loader.py +0 -0
  343. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/session/test_session_logger.py +0 -0
  344. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/session/test_session_migration.py +0 -0
  345. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/conftest.py +0 -0
  346. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/test_manager.py +0 -0
  347. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/test_models.py +0 -0
  348. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/test_parser.py +0 -0
  349. {drydock_cli-0.8.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
  359. {drydock_cli-0.8.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → drydock_cli-0.9.0}/tests/snapshots/base_snapshot_test_app.py +0 -0
  410. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/conftest.py +0 -0
  411. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/snap_compare.py +0 -0
  412. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
  413. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
  414. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
  415. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
  416. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
  417. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
  418. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
  419. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
  420. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
  421. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
  422. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
  423. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
  424. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
  425. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
  426. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/stubs/fake_backend.py +0 -0
  427. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/stubs/fake_client.py +0 -0
  428. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/stubs/fake_tool.py +0 -0
  429. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_auto_compact.py +0 -0
  430. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_backend.py +0 -0
  431. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_observer_streaming.py +0 -0
  432. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_stats.py +0 -0
  433. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_tasks.py +0 -0
  434. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_tool_call.py +0 -0
  435. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agents.py +0 -0
  436. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_cli_programmatic_preload.py +0 -0
  437. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_current_bugs.py +0 -0
  438. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_drydock_regression.py +0 -0
  439. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_drydock_tasks.py +0 -0
  440. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_history_manager.py +0 -0
  441. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_integration.py +0 -0
  442. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_loop_detection.py +0 -0
  443. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_message_id.py +0 -0
  444. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_message_merging.py +0 -0
  445. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_middleware.py +0 -0
  446. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_real_failures.py +0 -0
  447. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_real_issues.py +0 -0
  448. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_reasoning_content.py +0 -0
  449. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_system_prompt.py +0 -0
  450. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_tagged_text.py +0 -0
  451. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_ui_external_editor.py +0 -0
  452. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_ui_input_history.py +0 -0
  453. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_user_issues.py +0 -0
  454. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_ask_user_question.py +0 -0
  455. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_bash.py +0 -0
  456. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_exit_plan_mode.py +0 -0
  457. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_grep.py +0 -0
  458. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_invoke_context.py +0 -0
  459. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_manager_get_tool_config.py +0 -0
  460. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_mcp.py +0 -0
  461. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_mcp_sampling.py +0 -0
  462. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_task.py +0 -0
  463. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_ui_bash_execution.py +0 -0
  464. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_webfetch.py +0 -0
  465. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_websearch.py +0 -0
  466. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
  467. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
  468. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_do_update.py +0 -0
  469. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
  470. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_github_update_gateway.py +0 -0
  471. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
  472. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_ui_update_notification.py +0 -0
  473. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_update_use_case.py +0 -0
  474. {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_whats_new.py +0 -0
  475. {drydock_cli-0.8.2 → 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.2
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
@@ -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()
@@ -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")
@@ -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
+ )
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: batch
3
+ description: Apply the same change across many files. Generates a plan, then processes each file.
4
+ user-invocable: true
5
+ allowed-tools:
6
+ - bash
7
+ - grep
8
+ - read_file
9
+ - search_replace
10
+ - write_file
11
+ - task_create
12
+ - task_update
13
+ ---
14
+
15
+ # Batch Changes
16
+
17
+ Apply the same change pattern across multiple files.
18
+
19
+ ## Workflow
20
+
21
+ 1. **Identify scope**: Use `grep` or `glob` to find all files that need the change
22
+ 2. **Create plan**: List each file and the specific change needed
23
+ 3. **Track with tasks**: Create a task for each file change
24
+ 4. **Execute**: Process each file one at a time
25
+ 5. **Verify**: Read back each changed file to confirm
26
+
27
+ ## Rules
28
+
29
+ - Process ONE file at a time — do not batch edits
30
+ - Create a task_create for each file before editing
31
+ - Mark task_update as completed after each file
32
+ - If a file edit fails, skip it and note the failure
33
+ - After all files, report: X/Y files changed successfully
34
+
35
+ ## Example
36
+
37
+ User: "Add type hints to all function parameters in src/"
38
+
39
+ 1. `grep -r "def " src/ --include="*.py"` → find all functions
40
+ 2. For each file with untyped params:
41
+ - `task_create`: "Add type hints to src/utils.py"
42
+ - `read_file` the function
43
+ - `search_replace` to add types
44
+ - `task_update`: completed
45
+ 3. Summary: "Added type hints to 12/15 files (3 skipped: complex generics)"
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: simplify
3
+ description: Review changed code for reuse, quality, and efficiency. Fix issues found.
4
+ user-invocable: true
5
+ allowed-tools:
6
+ - bash
7
+ - grep
8
+ - read_file
9
+ - search_replace
10
+ ---
11
+
12
+ # Simplify
13
+
14
+ Review recent changes for code quality and fix issues.
15
+
16
+ ## Three-Pass Review
17
+
18
+ ### Pass 1: Duplication
19
+ - Check if any new code duplicates existing patterns
20
+ - Look for copy-pasted blocks that should be a shared function
21
+ - Search for similar patterns: `grep -r "pattern" --include="*.py"`
22
+
23
+ ### Pass 2: Complexity
24
+ - Functions over 30 lines → should they be split?
25
+ - Nesting deeper than 3 levels → use early returns
26
+ - Long parameter lists → use a config object or dataclass
27
+
28
+ ### Pass 3: Correctness
29
+ - Missing error handling on I/O operations
30
+ - Unclosed resources (files, connections)
31
+ - Missing null/empty checks on external input
32
+ - Type mismatches
33
+
34
+ ## Rules
35
+
36
+ - Fix mechanical issues immediately (duplication, style)
37
+ - Ask about judgment calls (architecture, naming)
38
+ - Keep changes minimal — don't refactor beyond what's needed
39
+ - Run `git diff` first to see what changed
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "drydock-cli"
3
- version = "0.8.2"
3
+ version = "0.9.0"
4
4
  description = "Drydock — nautical CLI coding agent"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -81,6 +81,8 @@ include = ["drydock/"]
81
81
  "drydock/skills/investigate" = "drydock/skills/investigate"
82
82
  "drydock/skills/review" = "drydock/skills/review"
83
83
  "drydock/skills/ship" = "drydock/skills/ship"
84
+ "drydock/skills/batch" = "drydock/skills/batch"
85
+ "drydock/skills/simplify" = "drydock/skills/simplify"
84
86
 
85
87
 
86
88
  [project.scripts]