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.
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/PKG-INFO +1 -1
- drydock_cli-0.9.0/drydock/core/hooks.py +179 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/glob_tool.py +94 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/notebook_edit.py +96 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/glob.md +9 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/notebook_edit.md +7 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/task_manager.md +8 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/task_manager.py +169 -0
- drydock_cli-0.9.0/drydock/skills/batch/SKILL.md +45 -0
- drydock_cli-0.9.0/drydock/skills/simplify/SKILL.md +39 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/pyproject.toml +3 -1
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/CODEOWNERS +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/build-and-upload.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/ci.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/issue-labeler.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.github/workflows/release.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.gitignore +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.pre-commit-config.yaml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.python-version +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.typos.toml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.vscode/extensions.json +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.vscode/launch.json +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/.vscode/settings.json +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/AGENTS.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/CHANGELOG.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/CLAUDE.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/CONTRIBUTING.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/Drydock_rebrand.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/LICENSE +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/NOTICE +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/PRD.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/README.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/action.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/distribution/zed/LICENSE +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/distribution/zed/extension.toml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/distribution/zed/icons/mistral_vibe.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/docs/README.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/docs/acp-setup.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/docs/proxy-setup.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/acp_agent_loop.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/acp_logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/entrypoint.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/todo.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/tools/session_update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/acp/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/path_completion.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/autocompletion/slash_command.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/cli.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/clipboard.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/commands.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/entrypoint.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/history_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/terminal_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.tcss +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/external_editor.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/compact.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/loading.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/messages.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tools.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/state.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/cli/update_notifier/whats_new.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agent_loop.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agents/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agents/manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/agents/models.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/auth/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/auth/crypto.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/auth/github.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/completers.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/store.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/fuzzy.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/_settings.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/harness_files/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_harness_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_paths.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/consultant.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/drydock_states.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/anthropic.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/factory.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/generic.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/mistral.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/backend/vertex.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/exceptions.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/format.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/message_utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/llm/types.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/middleware.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/output_formatters.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/_local_config_walk.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/_vibe_home.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/paths/conventions.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/plan_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/programmatic.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/cli.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/compact.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/dangerous_directory.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/diagnostic.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/explore.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/planner.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/project_context.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/prompts/tests.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/proxy_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/session_loader.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/session_logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/session_migration.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/session/state_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/models.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/skills/parser.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/slug.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/system_prompt.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/telemetry/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/telemetry/send.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/errors.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/git.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/nuage.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/teleport.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/teleport/types.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/ask_user_question.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/grep.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/bash.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/grep.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/task.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/todo.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/task.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/todo.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/webfetch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/websearch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/injection_guard.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp/registry.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp/tools.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/mcp_sampling.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/ui.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/tools/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/trusted_folders.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/types.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/core/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/onboarding.tcss +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/api_key.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/welcome.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/create-presentation/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/deep-research/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/investigate/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/review/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/skills/ship/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock/whats_new.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock-acp.spec +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/drydock_terms.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/flake.lock +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/flake.nix +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/README.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/backup.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/bump_version.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/deploy_to_github.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/install.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/prepare_release.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/scripts/publish_to_pypi.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_acp.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_agent_thought.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_compact_session_updates.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_content.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_initialize.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_list_sessions.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_load_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_multi_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_new_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_proxy_setup_acp.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_read_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_search_replace.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_set_config_option.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_set_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_set_model.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_tool_call_session_update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/acp/test_write_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_file_indexer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_fuzzy.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_recursive.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completion_controller.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_slash_command_controller.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/data/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/data/fireworks.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/data/mistral.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_anthropic_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_backend.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_reasoning_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_bell_notifications.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_braille_renderer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_clipboard.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_commands.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_copy_shortcuts.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_external_editor.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_no_markup_static.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_question_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_spinner.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_switching_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_clipboard_notifications.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_session_resume.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/test_ui_skill_dispatch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/cli/textual_ui/test_session_picker.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_agents.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_auth_crypto.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_auth_github.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_config_load_dotenv.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_config_paths.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_config_resolution.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_file_logging.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_plan_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_proxy_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_slug.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_telemetry_send.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_teleport_git.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_teleport_nuage.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_teleport_service.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_trusted_folders.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/core/test_utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/common.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/mock_server.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_onboarding.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_streaming.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/mock_backend_factory.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/mock_entrypoint.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/mock/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/onboarding/test_run_onboarding.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/onboarding/test_ui_onboarding.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/session/test_session_loader.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/session/test_session_logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/session/test_session_migration.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/test_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/test_models.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/skills/test_parser.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/base_snapshot_test_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/snap_compare.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/stubs/fake_backend.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/stubs/fake_client.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/stubs/fake_tool.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_auto_compact.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_backend.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_observer_streaming.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_stats.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_tasks.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agent_tool_call.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_agents.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_cli_programmatic_preload.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_current_bugs.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_drydock_regression.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_drydock_tasks.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_history_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_integration.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_loop_detection.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_message_id.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_message_merging.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_middleware.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_real_failures.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_real_issues.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_reasoning_content.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_system_prompt.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_tagged_text.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_ui_external_editor.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_ui_input_history.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/test_user_issues.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_ask_user_question.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_exit_plan_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_grep.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_invoke_context.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_manager_get_tool_config.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_mcp.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_mcp_sampling.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_task.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_ui_bash_execution.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_webfetch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/tools/test_websearch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_do_update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_github_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_ui_update_notification.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_update_use_case.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/tests/update_notifier/test_whats_new.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-0.9.0}/uv.lock +0 -0
|
@@ -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,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.
|
|
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]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|