drydock-cli 0.7.4__tar.gz → 0.8.1__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.7.4 → drydock_cli-0.8.1}/PKG-INFO +2 -1
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/cli.py +1 -1
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/external_editor.py +1 -1
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/agent_loop.py +50 -8
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/auth/github.py +1 -1
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/config/_settings.py +1 -1
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/logger.py +1 -1
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/programmatic.py +1 -1
- drydock_cli-0.8.1/drydock/core/tools/builtins/websearch.py +133 -0
- drydock_cli-0.8.1/drydock/skills/investigate/SKILL.md +67 -0
- drydock_cli-0.8.1/drydock/skills/review/SKILL.md +67 -0
- drydock_cli-0.8.1/drydock/skills/ship/SKILL.md +65 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/pyproject.toml +5 -1
- drydock_cli-0.8.1/tests/test_current_bugs.py +150 -0
- drydock_cli-0.7.4/drydock/core/tools/builtins/websearch.py +0 -158
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/CODEOWNERS +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/workflows/build-and-upload.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/workflows/ci.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/workflows/issue-labeler.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.github/workflows/release.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.gitignore +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.pre-commit-config.yaml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.python-version +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.typos.toml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.vscode/extensions.json +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.vscode/launch.json +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/.vscode/settings.json +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/AGENTS.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/CHANGELOG.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/CLAUDE.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/CONTRIBUTING.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/Drydock_rebrand.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/LICENSE +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/NOTICE +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/PRD.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/README.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/action.yml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/distribution/zed/LICENSE +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/distribution/zed/extension.toml +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/distribution/zed/icons/mistral_vibe.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/docs/README.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/docs/acp-setup.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/docs/proxy-setup.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/acp_agent_loop.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/acp_logger.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/entrypoint.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/base.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/builtins/bash.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/builtins/todo.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/tools/session_update.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/acp/utils.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/autocompletion/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/autocompletion/base.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/autocompletion/path_completion.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/autocompletion/slash_command.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/clipboard.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/commands.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/entrypoint.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/history_manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/terminal_setup.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/app.tcss +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/compact.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/loading.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/messages.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/tools.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/windowing/history.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/textual_ui/windowing/state.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/update.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/cli/update_notifier/whats_new.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/agents/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/agents/manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/agents/models.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/auth/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/auth/crypto.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/completers.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/file_indexer/store.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/fuzzy.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/path_prompt.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/config/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/config/harness_files/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/config/harness_files/_harness_manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/config/harness_files/_paths.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/consultant.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/drydock_states.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/backend/anthropic.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/backend/base.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/backend/factory.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/backend/generic.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/backend/mistral.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/backend/vertex.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/exceptions.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/format.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/message_utils.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/llm/types.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/middleware.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/output_formatters.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/paths/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/paths/_local_config_walk.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/paths/_vibe_home.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/paths/conventions.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/plan_session.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/cli.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/compact.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/dangerous_directory.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/diagnostic.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/explore.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/planner.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/project_context.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/prompts/tests.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/proxy_setup.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/session/session_loader.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/session/session_logger.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/session/session_migration.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/session/state_file.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/skills/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/skills/manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/skills/models.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/skills/parser.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/slug.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/system_prompt.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/telemetry/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/telemetry/send.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/teleport/errors.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/teleport/git.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/teleport/nuage.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/teleport/teleport.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/teleport/types.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/base.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/ask_user_question.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/bash.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/grep.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/bash.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/grep.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/task.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/todo.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/task.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/todo.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/webfetch.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/injection_guard.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/mcp/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/mcp/registry.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/mcp/tools.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/mcp_sampling.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/ui.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/tools/utils.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/trusted_folders.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/types.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/core/utils.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/onboarding/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/onboarding/base.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/onboarding/onboarding.tcss +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/onboarding/screens/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/onboarding/screens/api_key.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/onboarding/screens/welcome.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/skills/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/skills/create-presentation/SKILL.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/skills/deep-research/SKILL.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock/whats_new.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock-acp.spec +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/drydock_terms.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/flake.lock +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/flake.nix +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/scripts/README.md +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/scripts/backup.sh +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/scripts/bump_version.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/scripts/deploy_to_github.sh +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/scripts/install.sh +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/scripts/prepare_release.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/scripts/publish_to_pypi.sh +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/conftest.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_acp.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_agent_thought.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_bash.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_compact_session_updates.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_content.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_initialize.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_list_sessions.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_load_session.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_multi_session.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_new_session.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_proxy_setup_acp.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_read_file.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_search_replace.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_set_config_option.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_set_mode.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_set_model.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_tool_call_session_update.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_utils.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/acp/test_write_file.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_file_indexer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_fuzzy.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_path_completer_recursive.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_path_completion_controller.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_slash_command_controller.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/data/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/data/fireworks.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/data/mistral.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/test_anthropic_adapter.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/test_backend.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/test_reasoning_adapter.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_bell_notifications.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_braille_renderer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_clipboard.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_commands.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_copy_shortcuts.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_external_editor.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_no_markup_static.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_question_app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_spinner.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_switching_mode.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_ui_clipboard_notifications.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_ui_session_resume.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/test_ui_skill_dispatch.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/cli/textual_ui/test_session_picker.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/conftest.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_agents.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_auth_crypto.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_auth_github.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_config_load_dotenv.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_config_paths.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_config_resolution.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_file_logging.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_plan_session.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_proxy_setup.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_slug.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_telemetry_send.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_teleport_git.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_teleport_nuage.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_teleport_service.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_trusted_folders.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/core/test_utils.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/e2e/common.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/e2e/conftest.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/e2e/mock_server.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/e2e/test_cli_tui_onboarding.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/e2e/test_cli_tui_streaming.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/mock/__init__.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/mock/mock_backend_factory.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/mock/mock_entrypoint.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/mock/utils.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/onboarding/test_run_onboarding.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/onboarding/test_ui_onboarding.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/session/test_session_loader.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/session/test_session_logger.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/session/test_session_migration.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/skills/conftest.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/skills/test_manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/skills/test_models.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/skills/test_parser.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/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.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/base_snapshot_test_app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/conftest.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/snap_compare.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/stubs/fake_backend.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/stubs/fake_client.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/stubs/fake_tool.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_agent_auto_compact.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_agent_backend.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_agent_observer_streaming.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_agent_stats.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_agent_tasks.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_agent_tool_call.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_agents.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_cli_programmatic_preload.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_drydock_regression.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_drydock_tasks.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_history_manager.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_integration.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_loop_detection.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_message_id.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_message_merging.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_middleware.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_real_failures.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_real_issues.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_reasoning_content.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_system_prompt.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_tagged_text.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_ui_external_editor.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_ui_input_history.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/test_user_issues.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_ask_user_question.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_bash.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_exit_plan_mode.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_grep.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_invoke_context.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_manager_get_tool_config.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_mcp.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_mcp_sampling.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_task.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_ui_bash_execution.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_webfetch.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/tools/test_websearch.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/test_do_update.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/test_github_update_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/test_ui_update_notification.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/test_update_use_case.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/tests/update_notifier/test_whats_new.py +0 -0
- {drydock_cli-0.7.4 → drydock_cli-0.8.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: drydock-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
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
|
|
@@ -24,6 +24,7 @@ Classifier: Topic :: Utilities
|
|
|
24
24
|
Requires-Python: >=3.12
|
|
25
25
|
Requires-Dist: agent-client-protocol==0.8.1
|
|
26
26
|
Requires-Dist: anyio>=4.12.0
|
|
27
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
27
28
|
Requires-Dist: cachetools>=5.5.0
|
|
28
29
|
Requires-Dist: cryptography<=46.0.3,>=44.0.0
|
|
29
30
|
Requires-Dist: gitpython>=3.1.46
|
|
@@ -196,7 +196,7 @@ def run_cli(args: argparse.Namespace) -> None:
|
|
|
196
196
|
entrypoint_metadata=EntrypointMetadata(
|
|
197
197
|
agent_entrypoint="cli",
|
|
198
198
|
agent_version=__version__,
|
|
199
|
-
client_name="
|
|
199
|
+
client_name="drydock_cli",
|
|
200
200
|
client_version=__version__,
|
|
201
201
|
),
|
|
202
202
|
)
|
|
@@ -16,7 +16,7 @@ class ExternalEditor:
|
|
|
16
16
|
|
|
17
17
|
def edit(self, initial_content: str = "") -> str | None:
|
|
18
18
|
editor = self.get_editor()
|
|
19
|
-
fd, filepath = tempfile.mkstemp(suffix=".md", prefix="
|
|
19
|
+
fd, filepath = tempfile.mkstemp(suffix=".md", prefix="drydock_")
|
|
20
20
|
try:
|
|
21
21
|
with os.fdopen(fd, "w") as f:
|
|
22
22
|
f.write(initial_content)
|
|
@@ -482,6 +482,7 @@ class AgentLoop:
|
|
|
482
482
|
bash_count = 0 # Track consecutive bash calls without an edit
|
|
483
483
|
search_replace_failures = 0 # Track failed search_replace attempts
|
|
484
484
|
files_explored: list[str] = [] # Track files read for context state
|
|
485
|
+
files_modified: list[str] = [] # Track files edited for blast radius
|
|
485
486
|
context_summary_injected = False
|
|
486
487
|
while not should_break_loop:
|
|
487
488
|
# Loop protection: prevent infinite tool-call loops
|
|
@@ -549,8 +550,28 @@ class AgentLoop:
|
|
|
549
550
|
if not tc.function:
|
|
550
551
|
continue
|
|
551
552
|
if tc.function.name in ("search_replace", "write_file"):
|
|
553
|
+
first_edit = not has_made_edit
|
|
552
554
|
has_made_edit = True
|
|
553
555
|
bash_count = 0 # Reset on successful edit
|
|
556
|
+
# Track blast radius
|
|
557
|
+
try:
|
|
558
|
+
edit_args = json.loads(tc.function.arguments or "{}")
|
|
559
|
+
edit_path = edit_args.get("file_path", edit_args.get("path", ""))
|
|
560
|
+
if edit_path and edit_path not in files_modified:
|
|
561
|
+
files_modified.append(edit_path)
|
|
562
|
+
except (json.JSONDecodeError, AttributeError):
|
|
563
|
+
edit_path = ""
|
|
564
|
+
|
|
565
|
+
# After first edit: prompt to check related files
|
|
566
|
+
if first_edit and edit_path:
|
|
567
|
+
self._inject_system_note(
|
|
568
|
+
f"Good — you edited {edit_path}. Now check: "
|
|
569
|
+
f"does this bug have a RELATED file that also needs changes? "
|
|
570
|
+
f"Common patterns: if you edited a model, check the serializer/migration. "
|
|
571
|
+
f"If you edited a base class, check subclasses. "
|
|
572
|
+
f"If you edited a util, check callers. "
|
|
573
|
+
f"Use grep to search for imports of the function/class you changed."
|
|
574
|
+
)
|
|
554
575
|
if tc.function.name == "read_file":
|
|
555
576
|
try:
|
|
556
577
|
args = json.loads(tc.function.arguments or "{}")
|
|
@@ -569,6 +590,15 @@ class AgentLoop:
|
|
|
569
590
|
if "search_replace" in content.lower() and "error" in content.lower():
|
|
570
591
|
search_replace_failures += 1
|
|
571
592
|
|
|
593
|
+
# Blast radius check: warn when touching 5+ files
|
|
594
|
+
if len(files_modified) >= 5 and len(files_modified) % 5 == 0:
|
|
595
|
+
self._inject_system_note(
|
|
596
|
+
f"BLAST RADIUS WARNING: You have modified {len(files_modified)} files. "
|
|
597
|
+
f"Files: {', '.join(files_modified[-5:])}. "
|
|
598
|
+
f"This is a large change. Are you sure all these edits are needed? "
|
|
599
|
+
f"Consider stopping to verify your approach."
|
|
600
|
+
)
|
|
601
|
+
|
|
572
602
|
# Bash abuse detection: if model keeps using bash instead of
|
|
573
603
|
# search_replace/read_file, redirect it to the proper tools
|
|
574
604
|
if not has_made_edit and bash_count >= 10 and bash_count % 5 == 0:
|
|
@@ -579,14 +609,26 @@ class AgentLoop:
|
|
|
579
609
|
"Call search_replace NOW with your fix."
|
|
580
610
|
)
|
|
581
611
|
|
|
582
|
-
# search_replace keeps failing
|
|
583
|
-
if search_replace_failures >= 3
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
612
|
+
# 3-Strike Rule: search_replace keeps failing
|
|
613
|
+
if search_replace_failures >= 3:
|
|
614
|
+
if search_replace_failures == 3:
|
|
615
|
+
# Strike 3: STOP and ask user
|
|
616
|
+
self.messages.append(LLMMessage(
|
|
617
|
+
role=Role.user,
|
|
618
|
+
content=(
|
|
619
|
+
f"3-STRIKE RULE: Your search_replace has failed {search_replace_failures} times. "
|
|
620
|
+
"You have exhausted your attempts. STOP trying the same approach. "
|
|
621
|
+
"Either: (1) use read_file to get the EXACT text first, "
|
|
622
|
+
"(2) try a completely different file, or "
|
|
623
|
+
"(3) ask the user for help with /consult."
|
|
624
|
+
)
|
|
625
|
+
))
|
|
626
|
+
elif search_replace_failures % 3 == 0:
|
|
627
|
+
# Every 3 more failures, escalate
|
|
628
|
+
self.messages.append(LLMMessage(
|
|
629
|
+
role=Role.user,
|
|
630
|
+
content=f"STOP: {search_replace_failures} failed edits. You are stuck. Ask the user for help."
|
|
631
|
+
))
|
|
590
632
|
|
|
591
633
|
# Context budget warning: after 7 tool turns without an edit,
|
|
592
634
|
# warn that context is being consumed without progress
|
|
@@ -11,7 +11,7 @@ import keyring.errors
|
|
|
11
11
|
|
|
12
12
|
GITHUB_CLIENT_ID = "Ov23liJ7sk5kFDMEyvDT"
|
|
13
13
|
|
|
14
|
-
_SERVICE_NAME = "
|
|
14
|
+
_SERVICE_NAME = "drydock"
|
|
15
15
|
_KEYRING_USERNAME = "github_token"
|
|
16
16
|
_DEVICE_CODE_URL = "https://github.com/login/device/code"
|
|
17
17
|
_TOKEN_URL = "https://github.com/login/oauth/access_token"
|
|
@@ -21,7 +21,7 @@ from drydock.core.types import (
|
|
|
21
21
|
)
|
|
22
22
|
from drydock.core.utils import ConversationLimitException
|
|
23
23
|
|
|
24
|
-
_DEFAULT_CLIENT_METADATA = ClientMetadata(name="
|
|
24
|
+
_DEFAULT_CLIENT_METADATA = ClientMetadata(name="drydock_programmatic", version=__version__)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def run_programmatic(
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import AsyncGenerator
|
|
4
|
+
import os
|
|
5
|
+
import urllib.parse
|
|
6
|
+
from typing import ClassVar, final
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
from bs4 import BeautifulSoup
|
|
10
|
+
|
|
11
|
+
from drydock.core.tools.base import (
|
|
12
|
+
BaseTool,
|
|
13
|
+
BaseToolConfig,
|
|
14
|
+
BaseToolState,
|
|
15
|
+
InvokeContext,
|
|
16
|
+
ToolError,
|
|
17
|
+
ToolPermission,
|
|
18
|
+
)
|
|
19
|
+
from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
|
|
20
|
+
from drydock.core.types import ToolStreamEvent
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class WebSearchSource(BaseModel):
|
|
24
|
+
title: str
|
|
25
|
+
url: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class WebSearchArgs(BaseModel):
|
|
29
|
+
query: str = Field(min_length=1)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class WebSearchResult(BaseModel):
|
|
33
|
+
answer: str
|
|
34
|
+
sources: list[WebSearchSource] = Field(default_factory=list)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class WebSearchConfig(BaseToolConfig):
|
|
38
|
+
permission: ToolPermission = ToolPermission.ASK
|
|
39
|
+
timeout: int = Field(default=30, description="HTTP timeout in seconds.")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class WebSearch(
|
|
43
|
+
BaseTool[WebSearchArgs, WebSearchResult, WebSearchConfig, BaseToolState],
|
|
44
|
+
ToolUIData[WebSearchArgs, WebSearchResult],
|
|
45
|
+
):
|
|
46
|
+
description: ClassVar[str] = "Search the web directly without an API key to find current information."
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def is_available(cls) -> bool:
|
|
50
|
+
# Always available! No API key required.
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
@final
|
|
54
|
+
async def run(
|
|
55
|
+
self, args: WebSearchArgs, ctx: InvokeContext | None = None
|
|
56
|
+
) -> AsyncGenerator[ToolStreamEvent | WebSearchResult, None]:
|
|
57
|
+
|
|
58
|
+
import httpx
|
|
59
|
+
|
|
60
|
+
# Grab your proxy settings
|
|
61
|
+
proxy_url = os.getenv("HTTPS_PROXY") or os.getenv("https_proxy") or os.getenv("HTTP_PROXY") or os.getenv("http_proxy")
|
|
62
|
+
|
|
63
|
+
headers = {
|
|
64
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
65
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Respect DRYDOCK_INSECURE flag for SSL bypass
|
|
70
|
+
verify_ssl = os.environ.get("DRYDOCK_INSECURE") != "1"
|
|
71
|
+
async with httpx.AsyncClient(proxy=proxy_url, verify=not verify_ssl if verify_ssl else False, headers=headers) as client:
|
|
72
|
+
response = await client.post(
|
|
73
|
+
"https://html.duckduckgo.com/html/",
|
|
74
|
+
data={"q": args.query},
|
|
75
|
+
timeout=self.config.timeout
|
|
76
|
+
)
|
|
77
|
+
response.raise_for_status()
|
|
78
|
+
except Exception as e:
|
|
79
|
+
raise ToolError(f"Failed to fetch search results: {str(e)}")
|
|
80
|
+
|
|
81
|
+
# Parse the DuckDuckGo HTML results
|
|
82
|
+
soup = BeautifulSoup(response.text, "html.parser")
|
|
83
|
+
results = soup.find_all("div", class_="result")
|
|
84
|
+
|
|
85
|
+
text_parts = []
|
|
86
|
+
sources = []
|
|
87
|
+
|
|
88
|
+
# Extract the top 5 results
|
|
89
|
+
for res in results[:5]:
|
|
90
|
+
title_tag = res.find("a", class_="result__url")
|
|
91
|
+
snippet_tag = res.find("a", class_="result__snippet")
|
|
92
|
+
|
|
93
|
+
if title_tag and snippet_tag:
|
|
94
|
+
title = title_tag.text.strip()
|
|
95
|
+
snippet = snippet_tag.text.strip()
|
|
96
|
+
raw_url = title_tag.get("href", "")
|
|
97
|
+
|
|
98
|
+
# Clean up DuckDuckGo's redirect URLs
|
|
99
|
+
if "uddg=" in raw_url:
|
|
100
|
+
clean_url = urllib.parse.unquote(raw_url.split("uddg=")[1].split("&")[0])
|
|
101
|
+
else:
|
|
102
|
+
clean_url = raw_url
|
|
103
|
+
|
|
104
|
+
sources.append(WebSearchSource(title=title, url=clean_url))
|
|
105
|
+
text_parts.append(f"Source: {title}\nURL: {clean_url}\nInformation: {snippet}\n")
|
|
106
|
+
|
|
107
|
+
if not text_parts:
|
|
108
|
+
answer = "No relevant information found on the web."
|
|
109
|
+
else:
|
|
110
|
+
# We bundle the snippets into a single text block for your local model to read
|
|
111
|
+
answer = "Here is the raw information retrieved from the web:\n\n" + "\n".join(text_parts)
|
|
112
|
+
|
|
113
|
+
yield WebSearchResult(answer=answer, sources=sources)
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def get_call_display(cls, event: ToolCallEvent) -> ToolCallDisplay:
|
|
117
|
+
if event.args is None or not isinstance(event.args, WebSearchArgs):
|
|
118
|
+
return ToolCallDisplay(summary="websearch")
|
|
119
|
+
return ToolCallDisplay(summary=f"Searching DuckDuckGo for: '{event.args.query}'")
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
123
|
+
if not isinstance(event.result, WebSearchResult):
|
|
124
|
+
return ToolResultDisplay(
|
|
125
|
+
success=False, message=event.error or event.skip_reason or "No result"
|
|
126
|
+
)
|
|
127
|
+
return ToolResultDisplay(
|
|
128
|
+
success=True, message=f"{len(event.result.sources)} web sources retrieved for the local model."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def get_status_text(cls) -> str:
|
|
133
|
+
return "Scraping the web..."
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: investigate
|
|
3
|
+
description: Systematic debugging with 3-strike rule. Investigate → Analyze → Hypothesize → Fix. Stops after 3 failed hypotheses.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- bash
|
|
7
|
+
- grep
|
|
8
|
+
- read_file
|
|
9
|
+
- search_replace
|
|
10
|
+
- write_file
|
|
11
|
+
- ask_user_question
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Investigate
|
|
15
|
+
|
|
16
|
+
Systematic debugging workflow. You are a senior debugger.
|
|
17
|
+
|
|
18
|
+
## Iron Law
|
|
19
|
+
|
|
20
|
+
**Never fix without root cause.** If you don't understand WHY the bug happens, you don't fix it.
|
|
21
|
+
|
|
22
|
+
## Workflow
|
|
23
|
+
|
|
24
|
+
### Phase 1: INVESTIGATE
|
|
25
|
+
- Read the error message / bug report carefully
|
|
26
|
+
- Identify the module and function where the bug manifests
|
|
27
|
+
- grep for the relevant code paths
|
|
28
|
+
- Read the specific functions involved
|
|
29
|
+
|
|
30
|
+
### Phase 2: ANALYZE
|
|
31
|
+
- Trace the data flow from input to the point of failure
|
|
32
|
+
- Identify what value is wrong and where it diverges from expected
|
|
33
|
+
- Check edge cases: null, empty, boundary values
|
|
34
|
+
|
|
35
|
+
### Phase 3: HYPOTHESIZE
|
|
36
|
+
State your hypothesis clearly:
|
|
37
|
+
```
|
|
38
|
+
HYPOTHESIS: The bug is caused by [X] in [file:line] because [reason].
|
|
39
|
+
EXPECTED: [what should happen]
|
|
40
|
+
ACTUAL: [what happens instead]
|
|
41
|
+
FIX: [what change to make]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Phase 4: IMPLEMENT
|
|
45
|
+
- Make the minimal fix
|
|
46
|
+
- Read back the changed code to verify
|
|
47
|
+
- If the fix doesn't work, go back to Phase 3
|
|
48
|
+
|
|
49
|
+
## 3-Strike Rule
|
|
50
|
+
|
|
51
|
+
Track your failed hypotheses:
|
|
52
|
+
- Strike 1: "Hypothesis 1 failed because [reason]. Trying different approach."
|
|
53
|
+
- Strike 2: "Hypothesis 2 failed because [reason]. One more attempt."
|
|
54
|
+
- Strike 3: **STOP.** Ask the user: "I've tried 3 approaches and none worked. Here's what I've learned: [summary]. What should I try next?"
|
|
55
|
+
|
|
56
|
+
Do NOT continue past 3 strikes. Ask for help.
|
|
57
|
+
|
|
58
|
+
## Scope Lock
|
|
59
|
+
|
|
60
|
+
Once you identify the module, **stay in that module**. Do not wander to other parts of the codebase unless the bug clearly crosses module boundaries.
|
|
61
|
+
|
|
62
|
+
## Blast Radius Check
|
|
63
|
+
|
|
64
|
+
Before applying a fix, count the files you're about to change:
|
|
65
|
+
- 1-2 files: proceed
|
|
66
|
+
- 3-4 files: mention it to the user first
|
|
67
|
+
- 5+ files: **STOP and ask.** "This fix touches {N} files. Should I proceed?"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review
|
|
3
|
+
description: Automated code review. Two-pass (critical then informational), scope drift detection, fix-first.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- bash
|
|
7
|
+
- grep
|
|
8
|
+
- read_file
|
|
9
|
+
- search_replace
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Code Review
|
|
13
|
+
|
|
14
|
+
You are a senior code reviewer. Review the current changes.
|
|
15
|
+
|
|
16
|
+
## Two-Pass Review
|
|
17
|
+
|
|
18
|
+
### Pass 1: CRITICAL (must fix before merge)
|
|
19
|
+
- Security vulnerabilities (injection, XSS, auth bypass)
|
|
20
|
+
- Data loss risks (missing transactions, race conditions)
|
|
21
|
+
- Breaking changes to public APIs
|
|
22
|
+
- Missing error handling that could crash
|
|
23
|
+
- Tests missing for new functionality
|
|
24
|
+
|
|
25
|
+
### Pass 2: INFORMATIONAL (nice to have)
|
|
26
|
+
- Code style and naming
|
|
27
|
+
- Performance suggestions
|
|
28
|
+
- Refactoring opportunities
|
|
29
|
+
- Documentation gaps
|
|
30
|
+
|
|
31
|
+
## Workflow
|
|
32
|
+
|
|
33
|
+
1. Run `git diff` to see what changed
|
|
34
|
+
2. Read each changed file
|
|
35
|
+
3. For each issue found:
|
|
36
|
+
- **Critical**: Fix it immediately with search_replace
|
|
37
|
+
- **Judgment call**: Ask the user with options
|
|
38
|
+
- **Informational**: Note it but don't block
|
|
39
|
+
|
|
40
|
+
## Scope Drift Detection
|
|
41
|
+
|
|
42
|
+
If the diff contains changes unrelated to the stated goal:
|
|
43
|
+
- Flag: "These files seem unrelated to the task: [list]"
|
|
44
|
+
- Ask: "Should these be in a separate commit?"
|
|
45
|
+
|
|
46
|
+
## Fix-First Philosophy
|
|
47
|
+
|
|
48
|
+
- Mechanical issues (typos, missing imports, obvious bugs): fix silently
|
|
49
|
+
- Style issues: fix if trivial, note if subjective
|
|
50
|
+
- Architecture decisions: always ask
|
|
51
|
+
|
|
52
|
+
## Output Format
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
## Review Summary
|
|
56
|
+
|
|
57
|
+
### Critical (must fix)
|
|
58
|
+
- [ ] [file:line] Issue description
|
|
59
|
+
|
|
60
|
+
### Informational
|
|
61
|
+
- [file:line] Suggestion
|
|
62
|
+
|
|
63
|
+
### Scope
|
|
64
|
+
- Files changed: N
|
|
65
|
+
- Lines added: N, removed: N
|
|
66
|
+
- Test coverage: [assessed/not assessed]
|
|
67
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ship
|
|
3
|
+
description: Automated shipping pipeline. Test → Review → Commit → Push → PR. Non-interactive.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- bash
|
|
7
|
+
- grep
|
|
8
|
+
- read_file
|
|
9
|
+
- write_file
|
|
10
|
+
- search_replace
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Ship
|
|
14
|
+
|
|
15
|
+
Automated shipping pipeline. You are a release engineer.
|
|
16
|
+
|
|
17
|
+
## Verification Gate
|
|
18
|
+
|
|
19
|
+
**Never claim completion without fresh test evidence.**
|
|
20
|
+
|
|
21
|
+
## Pipeline
|
|
22
|
+
|
|
23
|
+
### Step 1: Pre-flight checks
|
|
24
|
+
```bash
|
|
25
|
+
git status
|
|
26
|
+
git diff --stat
|
|
27
|
+
```
|
|
28
|
+
- If working tree is dirty, ask what to include
|
|
29
|
+
- If on main/master, ask to create a branch first
|
|
30
|
+
|
|
31
|
+
### Step 2: Run tests
|
|
32
|
+
```bash
|
|
33
|
+
python3 -m pytest tests/ -q --tb=short 2>&1 | tail -20
|
|
34
|
+
```
|
|
35
|
+
- If tests fail: **STOP.** Fix the failures first.
|
|
36
|
+
- If no test runner found, note it and continue.
|
|
37
|
+
|
|
38
|
+
### Step 3: Review diff
|
|
39
|
+
- Run `/review` mentally (check for critical issues)
|
|
40
|
+
- If >5 files changed, confirm with user
|
|
41
|
+
|
|
42
|
+
### Step 4: Commit
|
|
43
|
+
- Write a clear commit message following conventional commits:
|
|
44
|
+
- `feat:` for new features
|
|
45
|
+
- `fix:` for bug fixes
|
|
46
|
+
- `refactor:` for code changes that aren't fixes or features
|
|
47
|
+
- `docs:` for documentation
|
|
48
|
+
- `test:` for test changes
|
|
49
|
+
- Each commit should be bisectable (tests pass at every commit)
|
|
50
|
+
|
|
51
|
+
### Step 5: Push and PR
|
|
52
|
+
```bash
|
|
53
|
+
git push origin HEAD
|
|
54
|
+
```
|
|
55
|
+
- If `gh` CLI is available, create a PR:
|
|
56
|
+
```bash
|
|
57
|
+
gh pr create --title "..." --body "..."
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
|
|
62
|
+
- Never force push to main/master
|
|
63
|
+
- Never skip tests
|
|
64
|
+
- Never commit secrets, tokens, or credentials
|
|
65
|
+
- Always include what changed and why in the commit message
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "drydock-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.8.1"
|
|
4
4
|
description = "Drydock — nautical CLI coding agent"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -30,6 +30,7 @@ classifiers = [
|
|
|
30
30
|
dependencies = [
|
|
31
31
|
"agent-client-protocol==0.8.1",
|
|
32
32
|
"anyio>=4.12.0",
|
|
33
|
+
"beautifulsoup4>=4.12.0",
|
|
33
34
|
"cachetools>=5.5.0",
|
|
34
35
|
"cryptography>=44.0.0,<=46.0.3",
|
|
35
36
|
"gitpython>=3.1.46",
|
|
@@ -77,6 +78,9 @@ include = ["drydock/"]
|
|
|
77
78
|
[tool.hatch.build.targets.wheel.force-include]
|
|
78
79
|
"drydock/skills/create-presentation" = "drydock/skills/create-presentation"
|
|
79
80
|
"drydock/skills/deep-research" = "drydock/skills/deep-research"
|
|
81
|
+
"drydock/skills/investigate" = "drydock/skills/investigate"
|
|
82
|
+
"drydock/skills/review" = "drydock/skills/review"
|
|
83
|
+
"drydock/skills/ship" = "drydock/skills/ship"
|
|
80
84
|
|
|
81
85
|
|
|
82
86
|
[project.scripts]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Tests for current bugs — must FAIL before fix, PASS after.
|
|
2
|
+
|
|
3
|
+
These reproduce real issues found by using DryDock.
|
|
4
|
+
All tests hit the real vLLM backend.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from drydock.core.config.harness_files import init_harness_files_manager
|
|
17
|
+
try:
|
|
18
|
+
init_harness_files_manager("user", "project")
|
|
19
|
+
except RuntimeError:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
from drydock.core.agent_loop import AgentLoop
|
|
23
|
+
from drydock.core.agents.models import BuiltinAgentName
|
|
24
|
+
from drydock.core.config import Backend, ModelConfig, ProviderConfig, VibeConfig
|
|
25
|
+
from drydock.core.types import (
|
|
26
|
+
AssistantEvent, BaseEvent, Role, ToolCallEvent, ToolResultEvent,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _vllm_ok():
|
|
31
|
+
try:
|
|
32
|
+
return httpx.get("http://localhost:8000/v1/models", timeout=3).status_code == 200
|
|
33
|
+
except Exception:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
pytestmark = pytest.mark.skipif(not _vllm_ok(), reason="vLLM not running")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _config(tmp_path):
|
|
40
|
+
return VibeConfig(
|
|
41
|
+
active_model="devstral", auto_approve=True, enable_telemetry=False,
|
|
42
|
+
include_project_context=False, system_prompt_id="cli",
|
|
43
|
+
providers=[ProviderConfig(name="local", api_base="http://localhost:8000/v1", api_key_env_var="", backend=Backend.GENERIC)],
|
|
44
|
+
models=[ModelConfig(name="devstral", provider="local", input_price=0, output_price=0)],
|
|
45
|
+
session_logging={"enabled": False, "save_dir": str(tmp_path / "logs")},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def _agent(tmp_path, max_turns=10):
|
|
49
|
+
return AgentLoop(config=_config(tmp_path), agent_name=BuiltinAgentName.AUTO_APPROVE, max_turns=max_turns)
|
|
50
|
+
|
|
51
|
+
async def _run(agent, prompt, max_events=100):
|
|
52
|
+
events = []
|
|
53
|
+
async for ev in agent.act(prompt):
|
|
54
|
+
events.append(ev)
|
|
55
|
+
if len(events) >= max_events:
|
|
56
|
+
break
|
|
57
|
+
return events
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ============================================================================
|
|
61
|
+
# BUG: "vibe" still referenced in config defaults
|
|
62
|
+
# ============================================================================
|
|
63
|
+
|
|
64
|
+
class TestVibeReferences:
|
|
65
|
+
def test_no_vibe_in_config_model_names(self):
|
|
66
|
+
"""Default model name should not contain 'vibe'."""
|
|
67
|
+
from drydock.core.config._settings import DEFAULT_MODELS
|
|
68
|
+
for m in DEFAULT_MODELS:
|
|
69
|
+
assert "vibe" not in m.name.lower(), \
|
|
70
|
+
f"Model name '{m.name}' still references 'vibe'"
|
|
71
|
+
|
|
72
|
+
def test_no_vibe_in_logger_name(self):
|
|
73
|
+
"""Logger should use 'drydock' not 'vibe'."""
|
|
74
|
+
from drydock.core.logger import logger
|
|
75
|
+
assert logger.name != "vibe", \
|
|
76
|
+
f"Logger name is '{logger.name}' — should be 'drydock'"
|
|
77
|
+
|
|
78
|
+
def test_no_vibe_in_client_metadata(self):
|
|
79
|
+
"""Programmatic client should identify as drydock."""
|
|
80
|
+
from drydock.core.programmatic import _DEFAULT_CLIENT_METADATA
|
|
81
|
+
assert "vibe" not in _DEFAULT_CLIENT_METADATA.name.lower(), \
|
|
82
|
+
f"Client metadata '{_DEFAULT_CLIENT_METADATA.name}' still references 'vibe'"
|
|
83
|
+
|
|
84
|
+
def test_no_vibe_in_stop_event_tag(self):
|
|
85
|
+
"""Internal tags should use drydock not vibe."""
|
|
86
|
+
from drydock.core.utils import VIBE_STOP_EVENT_TAG, VIBE_WARNING_TAG
|
|
87
|
+
# These are internal protocol tags — checking they exist
|
|
88
|
+
# but flagging the naming for awareness
|
|
89
|
+
assert VIBE_STOP_EVENT_TAG # exists
|
|
90
|
+
assert VIBE_WARNING_TAG # exists
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ============================================================================
|
|
94
|
+
# BUG: Websearch loops — model keeps searching the same thing
|
|
95
|
+
# ============================================================================
|
|
96
|
+
|
|
97
|
+
@pytest.mark.asyncio
|
|
98
|
+
async def test_websearch_doesnt_loop(tmp_path):
|
|
99
|
+
"""Web search should not repeat the same query more than twice."""
|
|
100
|
+
agent = _agent(tmp_path, max_turns=10)
|
|
101
|
+
events = await _run(agent, "Search the web for 'Python asyncio best practices' and summarize", max_events=60)
|
|
102
|
+
|
|
103
|
+
websearch_calls = [e for e in events if isinstance(e, ToolCallEvent) and e.tool_name == "websearch"]
|
|
104
|
+
assert len(websearch_calls) <= 3, \
|
|
105
|
+
f"Websearch called {len(websearch_calls)} times — looping"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ============================================================================
|
|
109
|
+
# BUG: Still looping reading files — reads same file over and over
|
|
110
|
+
# ============================================================================
|
|
111
|
+
|
|
112
|
+
@pytest.mark.asyncio
|
|
113
|
+
async def test_file_read_doesnt_loop(tmp_path):
|
|
114
|
+
"""Agent should not read the same file more than 3 times."""
|
|
115
|
+
# Create a test file
|
|
116
|
+
test_file = tmp_path / "test_code.py"
|
|
117
|
+
test_file.write_text("def hello():\n print('hello world')\n\ndef goodbye():\n print('bye')\n")
|
|
118
|
+
|
|
119
|
+
agent = _agent(tmp_path, max_turns=10)
|
|
120
|
+
events = await _run(agent, f"Read {test_file} and explain what it does", max_events=60)
|
|
121
|
+
|
|
122
|
+
read_calls = [e for e in events if isinstance(e, ToolCallEvent) and e.tool_name == "read_file"]
|
|
123
|
+
|
|
124
|
+
# Should read the file once or twice, not 5+
|
|
125
|
+
assert len(read_calls) <= 3, \
|
|
126
|
+
f"read_file called {len(read_calls)} times for one file — looping"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ============================================================================
|
|
130
|
+
# BUG: DuckDuckGo websearch respects --insecure flag
|
|
131
|
+
# ============================================================================
|
|
132
|
+
|
|
133
|
+
class TestWebSearchSSL:
|
|
134
|
+
def test_websearch_uses_insecure_env(self):
|
|
135
|
+
"""New DuckDuckGo websearch should check DRYDOCK_INSECURE."""
|
|
136
|
+
import inspect
|
|
137
|
+
from drydock.core.tools.builtins.websearch import WebSearch
|
|
138
|
+
src = inspect.getsource(WebSearch)
|
|
139
|
+
assert "DRYDOCK_INSECURE" in src or "verify" in src, \
|
|
140
|
+
"WebSearch doesn't check DRYDOCK_INSECURE for SSL bypass"
|
|
141
|
+
|
|
142
|
+
def test_websearch_uses_duckduckgo(self):
|
|
143
|
+
"""WebSearch should use DuckDuckGo, not Mistral API."""
|
|
144
|
+
import inspect
|
|
145
|
+
from drydock.core.tools.builtins.websearch import WebSearch
|
|
146
|
+
src = inspect.getsource(WebSearch)
|
|
147
|
+
assert "duckduckgo" in src.lower(), \
|
|
148
|
+
"WebSearch should use DuckDuckGo (your refactored version)"
|
|
149
|
+
assert "mistralai" not in src or "import mistralai" not in src, \
|
|
150
|
+
"WebSearch should not require Mistral API"
|