drydock-cli 0.6.1__tar.gz → 0.6.3__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.6.1 → drydock_cli-0.6.3}/PKG-INFO +1 -1
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/commands.py +19 -1
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/app.py +88 -2
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/agent_loop.py +13 -20
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/config/_settings.py +8 -0
- drydock_cli-0.6.3/drydock/core/consultant.py +183 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/paths/_vibe_home.py +32 -4
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/pyproject.toml +1 -1
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_drydock_tasks.py +5 -5
- drydock_cli-0.6.1/drydock/core/consultant.py +0 -141
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/CODEOWNERS +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/workflows/build-and-upload.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/workflows/ci.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/workflows/issue-labeler.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.github/workflows/release.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.gitignore +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.pre-commit-config.yaml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.python-version +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.typos.toml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.vscode/extensions.json +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.vscode/launch.json +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/.vscode/settings.json +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/AGENTS.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/CHANGELOG.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/CLAUDE.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/CONTRIBUTING.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/Drydock_rebrand.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/LICENSE +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/NOTICE +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/PRD.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/README.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/action.yml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/distribution/zed/LICENSE +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/distribution/zed/extension.toml +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/distribution/zed/icons/mistral_vibe.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/docs/README.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/docs/acp-setup.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/docs/proxy-setup.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/acp_agent_loop.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/acp_logger.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/entrypoint.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/base.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/builtins/bash.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/builtins/todo.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/tools/session_update.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/acp/utils.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/autocompletion/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/autocompletion/base.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/autocompletion/path_completion.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/autocompletion/slash_command.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/cli.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/clipboard.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/entrypoint.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/history_manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/terminal_setup.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/app.tcss +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/external_editor.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/compact.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/loading.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/messages.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/tools.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/windowing/history.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/textual_ui/windowing/state.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/update.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/cli/update_notifier/whats_new.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/agents/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/agents/manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/agents/models.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/auth/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/auth/crypto.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/auth/github.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/completers.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/file_indexer/store.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/fuzzy.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/path_prompt.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/config/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/config/harness_files/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/config/harness_files/_harness_manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/config/harness_files/_paths.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/drydock_states.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/backend/anthropic.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/backend/base.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/backend/factory.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/backend/generic.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/backend/mistral.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/backend/vertex.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/exceptions.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/format.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/message_utils.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/llm/types.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/logger.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/middleware.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/output_formatters.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/paths/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/paths/_local_config_walk.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/paths/conventions.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/plan_session.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/programmatic.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/cli.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/compact.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/dangerous_directory.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/diagnostic.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/explore.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/planner.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/project_context.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/prompts/tests.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/proxy_setup.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/session/session_loader.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/session/session_logger.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/session/session_migration.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/session/state_file.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/skills/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/skills/manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/skills/models.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/skills/parser.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/slug.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/system_prompt.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/telemetry/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/telemetry/send.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/teleport/errors.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/teleport/git.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/teleport/nuage.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/teleport/teleport.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/teleport/types.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/base.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/ask_user_question.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/bash.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/grep.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/bash.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/grep.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/task.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/todo.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/task.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/todo.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/webfetch.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/websearch.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/injection_guard.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/mcp/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/mcp/registry.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/mcp/tools.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/mcp_sampling.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/ui.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/tools/utils.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/trusted_folders.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/types.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/core/utils.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/onboarding/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/onboarding/base.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/onboarding/onboarding.tcss +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/onboarding/screens/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/onboarding/screens/api_key.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/onboarding/screens/welcome.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/skills/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/skills/create-presentation/SKILL.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock/whats_new.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock-acp.spec +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/drydock_terms.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/flake.lock +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/flake.nix +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/scripts/README.md +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/scripts/bump_version.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/scripts/deploy_to_github.sh +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/scripts/install.sh +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/scripts/prepare_release.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/scripts/publish_to_pypi.sh +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/conftest.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_acp.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_agent_thought.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_bash.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_compact_session_updates.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_content.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_initialize.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_list_sessions.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_load_session.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_multi_session.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_new_session.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_proxy_setup_acp.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_read_file.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_search_replace.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_set_config_option.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_set_mode.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_set_model.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_tool_call_session_update.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_utils.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/acp/test_write_file.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_file_indexer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_fuzzy.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_path_completer_recursive.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_path_completion_controller.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_slash_command_controller.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/data/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/data/fireworks.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/data/mistral.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/test_anthropic_adapter.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/test_backend.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/test_reasoning_adapter.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_bell_notifications.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_braille_renderer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_clipboard.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_commands.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_copy_shortcuts.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_external_editor.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_no_markup_static.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_question_app.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_spinner.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_switching_mode.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_ui_clipboard_notifications.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_ui_session_resume.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/test_ui_skill_dispatch.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/cli/textual_ui/test_session_picker.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/conftest.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_agents.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_auth_crypto.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_auth_github.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_config_load_dotenv.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_config_paths.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_config_resolution.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_file_logging.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_plan_session.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_proxy_setup.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_slug.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_telemetry_send.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_teleport_git.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_teleport_nuage.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_teleport_service.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_trusted_folders.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/core/test_utils.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/e2e/common.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/e2e/conftest.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/e2e/mock_server.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/e2e/test_cli_tui_onboarding.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/e2e/test_cli_tui_streaming.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/mock/__init__.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/mock/mock_backend_factory.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/mock/mock_entrypoint.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/mock/utils.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/onboarding/test_run_onboarding.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/onboarding/test_ui_onboarding.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/session/test_session_loader.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/session/test_session_logger.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/session/test_session_migration.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/skills/conftest.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/skills/test_manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/skills/test_models.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/skills/test_parser.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/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.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/base_snapshot_test_app.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/conftest.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/snap_compare.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/stubs/fake_backend.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/stubs/fake_client.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/stubs/fake_tool.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_agent_auto_compact.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_agent_backend.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_agent_observer_streaming.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_agent_stats.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_agent_tool_call.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_agents.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_cli_programmatic_preload.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_drydock_regression.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_history_manager.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_message_id.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_message_merging.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_middleware.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_reasoning_content.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_system_prompt.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_tagged_text.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_ui_external_editor.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/test_ui_input_history.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_ask_user_question.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_bash.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_exit_plan_mode.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_grep.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_invoke_context.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_manager_get_tool_config.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_mcp.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_mcp_sampling.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_task.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_ui_bash_execution.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_webfetch.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/tools/test_websearch.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/test_do_update.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/test_github_update_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/test_ui_update_notification.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/test_update_use_case.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/tests/update_notifier/test_whats_new.py +0 -0
- {drydock_cli-0.6.1 → drydock_cli-0.6.3}/uv.lock +0 -0
|
@@ -72,6 +72,11 @@ class CommandRegistry:
|
|
|
72
72
|
description="Configure proxy and SSL certificate settings",
|
|
73
73
|
handler="_show_proxy_setup",
|
|
74
74
|
),
|
|
75
|
+
"consult": Command(
|
|
76
|
+
aliases=frozenset(["/consult"]),
|
|
77
|
+
description="Ask a smarter model for advice (response visible to local model)",
|
|
78
|
+
handler="_consult_command",
|
|
79
|
+
),
|
|
75
80
|
"resume": Command(
|
|
76
81
|
aliases=frozenset(["/resume", "/continue"]),
|
|
77
82
|
description="Browse and resume past sessions",
|
|
@@ -92,7 +97,20 @@ class CommandRegistry:
|
|
|
92
97
|
return self.commands.get(cmd_name) if cmd_name else None
|
|
93
98
|
|
|
94
99
|
def get_command_name(self, user_input: str) -> str | None:
|
|
95
|
-
|
|
100
|
+
stripped = user_input.lower().strip()
|
|
101
|
+
# Exact match first
|
|
102
|
+
if stripped in self._alias_map:
|
|
103
|
+
return self._alias_map[stripped]
|
|
104
|
+
# Prefix match for commands that take arguments (e.g., /consult <question>)
|
|
105
|
+
first_word = stripped.split()[0] if stripped else ""
|
|
106
|
+
if first_word in self._alias_map:
|
|
107
|
+
return self._alias_map[first_word]
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
def get_command_args(self, user_input: str) -> str:
|
|
111
|
+
"""Extract arguments after the command name."""
|
|
112
|
+
parts = user_input.strip().split(None, 1)
|
|
113
|
+
return parts[1] if len(parts) > 1 else ""
|
|
96
114
|
|
|
97
115
|
def get_help_text(self) -> str:
|
|
98
116
|
lines: list[str] = [
|
|
@@ -520,10 +520,18 @@ class VibeApp(App): # noqa: PLR0904
|
|
|
520
520
|
)
|
|
521
521
|
await self._mount_and_scroll(UserMessage(user_input))
|
|
522
522
|
handler = getattr(self, command.handler)
|
|
523
|
+
# Pass command args to handlers that accept them
|
|
524
|
+
cmd_args = self.commands.get_command_args(user_input)
|
|
523
525
|
if asyncio.iscoroutinefunction(handler):
|
|
524
|
-
|
|
526
|
+
try:
|
|
527
|
+
await handler(cmd_args)
|
|
528
|
+
except TypeError:
|
|
529
|
+
await handler() # Handler doesn't accept args
|
|
525
530
|
else:
|
|
526
|
-
|
|
531
|
+
try:
|
|
532
|
+
handler(cmd_args)
|
|
533
|
+
except TypeError:
|
|
534
|
+
handler()
|
|
527
535
|
return True
|
|
528
536
|
return False
|
|
529
537
|
|
|
@@ -927,6 +935,84 @@ class VibeApp(App): # noqa: PLR0904
|
|
|
927
935
|
"""
|
|
928
936
|
await self._mount_and_scroll(UserCommandMessage(status_text))
|
|
929
937
|
|
|
938
|
+
async def _consult_command(self, question: str = "") -> None:
|
|
939
|
+
"""Ask a consultant model for advice. Response goes into context."""
|
|
940
|
+
if not question:
|
|
941
|
+
await self._mount_and_scroll(
|
|
942
|
+
UserCommandMessage(
|
|
943
|
+
"Usage: `/consult <your question>`\n\n"
|
|
944
|
+
"Asks a smarter model for advice. The response appears in the "
|
|
945
|
+
"conversation so the local model can see and act on it.\n\n"
|
|
946
|
+
"Configure: set `consultant_model` in config.toml or use `--consultant MODEL`"
|
|
947
|
+
)
|
|
948
|
+
)
|
|
949
|
+
return
|
|
950
|
+
|
|
951
|
+
# Get consultant model from config, CLI flag, or env
|
|
952
|
+
import os
|
|
953
|
+
consultant_model = (
|
|
954
|
+
getattr(self.agent_loop.config, "consultant_model", "")
|
|
955
|
+
or os.environ.get("DRYDOCK_CONSULTANT_MODEL", "")
|
|
956
|
+
)
|
|
957
|
+
|
|
958
|
+
if not consultant_model:
|
|
959
|
+
# List available models to help the user
|
|
960
|
+
model_names = [m.name for m in self.agent_loop.config.models]
|
|
961
|
+
await self._mount_and_scroll(
|
|
962
|
+
ErrorMessage(
|
|
963
|
+
"No consultant model configured.\n\n"
|
|
964
|
+
f"Available models: {', '.join(model_names)}\n\n"
|
|
965
|
+
"Set in config.toml:\n"
|
|
966
|
+
" `consultant_model = \"gemini-2.5-pro\"`\n\n"
|
|
967
|
+
"Or via CLI:\n"
|
|
968
|
+
" `drydock --consultant gemini-2.5-pro`",
|
|
969
|
+
collapsed=self._tools_collapsed,
|
|
970
|
+
)
|
|
971
|
+
)
|
|
972
|
+
return
|
|
973
|
+
|
|
974
|
+
await self._mount_and_scroll(
|
|
975
|
+
UserCommandMessage(f"Consulting {consultant_model}...")
|
|
976
|
+
)
|
|
977
|
+
|
|
978
|
+
try:
|
|
979
|
+
from drydock.core.consultant import ask_consultant
|
|
980
|
+
advice = await ask_consultant(
|
|
981
|
+
question,
|
|
982
|
+
config=self.agent_loop.config,
|
|
983
|
+
model=consultant_model,
|
|
984
|
+
)
|
|
985
|
+
|
|
986
|
+
if not advice or advice.startswith("(Consultant unavailable"):
|
|
987
|
+
await self._mount_and_scroll(
|
|
988
|
+
ErrorMessage(
|
|
989
|
+
f"Consultant ({consultant_model}) unavailable: {advice}",
|
|
990
|
+
collapsed=self._tools_collapsed,
|
|
991
|
+
)
|
|
992
|
+
)
|
|
993
|
+
return
|
|
994
|
+
|
|
995
|
+
# Show the advice in the UI
|
|
996
|
+
response_text = f"**Consultant ({consultant_model}):**\n\n{advice}"
|
|
997
|
+
await self._mount_and_scroll(UserCommandMessage(response_text))
|
|
998
|
+
|
|
999
|
+
# Inject into the message history so the LOCAL model can see it
|
|
1000
|
+
from drydock.core.types import LLMMessage, Role
|
|
1001
|
+
self.agent_loop.messages.append(
|
|
1002
|
+
LLMMessage(
|
|
1003
|
+
role=Role.user,
|
|
1004
|
+
content=f"[CONSULTANT ADVICE from {consultant_model}]: {advice}"
|
|
1005
|
+
)
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
except Exception as e:
|
|
1009
|
+
await self._mount_and_scroll(
|
|
1010
|
+
ErrorMessage(
|
|
1011
|
+
f"Consultant error: {e}",
|
|
1012
|
+
collapsed=self._tools_collapsed,
|
|
1013
|
+
)
|
|
1014
|
+
)
|
|
1015
|
+
|
|
930
1016
|
async def _show_config(self) -> None:
|
|
931
1017
|
"""Switch to the configuration app in the bottom panel."""
|
|
932
1018
|
if self._current_bottom_app == BottomApp.Config:
|
|
@@ -847,9 +847,8 @@ class AgentLoop:
|
|
|
847
847
|
return "ALREADY ATTEMPTED (do NOT repeat):\n" + "\n".join(attempts[:10])
|
|
848
848
|
return ""
|
|
849
849
|
|
|
850
|
-
|
|
851
|
-
"""Block exact-duplicate tool calls. Returns cached result or None.
|
|
852
|
-
On 3rd attempt, auto-consults the smarter model if available."""
|
|
850
|
+
def _circuit_breaker_check(self, tool_call: ResolvedToolCall) -> str | None:
|
|
851
|
+
"""Block exact-duplicate tool calls. Returns cached result or None."""
|
|
853
852
|
sig = hashlib.md5(
|
|
854
853
|
f"{tool_call.tool_name}:{tool_call.raw_arguments}".encode()
|
|
855
854
|
).hexdigest()
|
|
@@ -868,22 +867,16 @@ class AgentLoop:
|
|
|
868
867
|
f"- Ask the user for clarification"
|
|
869
868
|
)
|
|
870
869
|
|
|
871
|
-
#
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
)
|
|
882
|
-
advice = await ask_consultant(question)
|
|
883
|
-
if advice:
|
|
884
|
-
msg += f"\n\nCONSULTANT ADVICE: {advice}"
|
|
885
|
-
except Exception as e:
|
|
886
|
-
logger.debug("Consultant call failed: %s", e)
|
|
870
|
+
# Suggest using /consult if a consultant model is configured
|
|
871
|
+
try:
|
|
872
|
+
from drydock.core.consultant import is_consultant_available
|
|
873
|
+
if is_consultant_available():
|
|
874
|
+
msg += (
|
|
875
|
+
"\n\nTIP: A consultant model is available. "
|
|
876
|
+
"The user can type `/consult <question>` to ask it for advice."
|
|
877
|
+
)
|
|
878
|
+
except Exception:
|
|
879
|
+
pass
|
|
887
880
|
|
|
888
881
|
return msg
|
|
889
882
|
return None
|
|
@@ -900,7 +893,7 @@ class AgentLoop:
|
|
|
900
893
|
self, tool_call: ResolvedToolCall
|
|
901
894
|
) -> AsyncGenerator[ToolResultEvent | ToolStreamEvent]:
|
|
902
895
|
# Circuit breaker: block exact duplicate calls after 2 attempts
|
|
903
|
-
if blocked :=
|
|
896
|
+
if blocked := self._circuit_breaker_check(tool_call):
|
|
904
897
|
yield ToolResultEvent(
|
|
905
898
|
tool_name=tool_call.tool_name,
|
|
906
899
|
tool_class=tool_call.tool_class,
|
|
@@ -310,6 +310,14 @@ class VibeConfig(BaseSettings):
|
|
|
310
310
|
enable_notifications: bool = True
|
|
311
311
|
api_timeout: float = 720.0
|
|
312
312
|
auto_compact_threshold: int = 200_000
|
|
313
|
+
consultant_model: str = Field(
|
|
314
|
+
default="",
|
|
315
|
+
description=(
|
|
316
|
+
"Model name for the /consult command. Must match a model defined in "
|
|
317
|
+
"your config. The consultant provides read-only advice — it never "
|
|
318
|
+
"calls tools. Leave empty to disable. Example: 'gemini-2.5-pro'"
|
|
319
|
+
),
|
|
320
|
+
)
|
|
313
321
|
|
|
314
322
|
# TODO(vibe-nuage): remove exclude=True once the feature is publicly available
|
|
315
323
|
nuage_enabled: bool = Field(default=False, exclude=True)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Consultant agent — asks a configured model for single-turn advice.
|
|
2
|
+
|
|
3
|
+
The consultant is a READ-ONLY advisor:
|
|
4
|
+
- Uses a model from DryDock's own config (providers + models)
|
|
5
|
+
- NEVER calls tools, writes files, or runs commands
|
|
6
|
+
- Only receives a question and returns reasoning/advice
|
|
7
|
+
- Response is injected into context so the local model can see it
|
|
8
|
+
|
|
9
|
+
Configure in config.toml:
|
|
10
|
+
consultant_model = "gemini-2.5-pro"
|
|
11
|
+
|
|
12
|
+
Or via CLI:
|
|
13
|
+
drydock --consultant gemini-2.5-pro
|
|
14
|
+
|
|
15
|
+
Or via /consult slash command:
|
|
16
|
+
/consult How should I fix this auth bug?
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def ask_consultant(
|
|
28
|
+
question: str,
|
|
29
|
+
config: object | None = None,
|
|
30
|
+
model: str | None = None,
|
|
31
|
+
) -> str:
|
|
32
|
+
"""Ask the consultant model a question using DryDock's backend.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
question : str
|
|
37
|
+
The specific question to ask.
|
|
38
|
+
config : VibeConfig, optional
|
|
39
|
+
DryDock config with providers/models. If None, uses env vars.
|
|
40
|
+
model : str, optional
|
|
41
|
+
Model name override. If None, uses config.consultant_model or env var.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
str
|
|
46
|
+
The consultant's advice text, or an error message.
|
|
47
|
+
"""
|
|
48
|
+
# Resolve model name
|
|
49
|
+
model_name = model or os.environ.get("DRYDOCK_CONSULTANT_MODEL", "")
|
|
50
|
+
if not model_name and config:
|
|
51
|
+
model_name = getattr(config, "consultant_model", "")
|
|
52
|
+
if not model_name:
|
|
53
|
+
return ""
|
|
54
|
+
|
|
55
|
+
prompt = (
|
|
56
|
+
"You are a senior software engineering consultant. An AI coding agent is "
|
|
57
|
+
"asking for your advice. Give concise, actionable guidance. Do NOT provide "
|
|
58
|
+
"code unless specifically asked — just say WHAT to do and WHERE to look.\n\n"
|
|
59
|
+
f"QUESTION:\n{question}"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Try to use DryDock's backend system
|
|
63
|
+
if config:
|
|
64
|
+
try:
|
|
65
|
+
return await _ask_via_backend(prompt, model_name, config)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.warning("Consultant via backend failed: %s — falling back to httpx", e)
|
|
68
|
+
|
|
69
|
+
# Fallback: direct API call
|
|
70
|
+
try:
|
|
71
|
+
return await _ask_via_httpx(prompt, model_name)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
return f"(Consultant unavailable: {e})"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def _ask_via_backend(prompt: str, model_name: str, config: object) -> str:
|
|
77
|
+
"""Use DryDock's configured backend to call the consultant model."""
|
|
78
|
+
from drydock.core.config import ModelConfig, ProviderConfig
|
|
79
|
+
from drydock.core.llm.backend.factory import BACKEND_FACTORY
|
|
80
|
+
from drydock.core.types import LLMMessage, Role
|
|
81
|
+
|
|
82
|
+
# Find the model and its provider in the config
|
|
83
|
+
target_model: ModelConfig | None = None
|
|
84
|
+
for m in config.models:
|
|
85
|
+
if m.name == model_name or m.alias == model_name:
|
|
86
|
+
target_model = m
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
if not target_model:
|
|
90
|
+
raise ValueError(f"Model '{model_name}' not found in config. Available: {[m.name for m in config.models]}")
|
|
91
|
+
|
|
92
|
+
# Find the provider
|
|
93
|
+
target_provider: ProviderConfig | None = None
|
|
94
|
+
for p in config.providers:
|
|
95
|
+
if p.name == target_model.provider:
|
|
96
|
+
target_provider = p
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
if not target_provider:
|
|
100
|
+
raise ValueError(f"Provider '{target_model.provider}' not found for model '{model_name}'")
|
|
101
|
+
|
|
102
|
+
# Create a one-shot backend
|
|
103
|
+
backend = BACKEND_FACTORY[target_provider.backend](
|
|
104
|
+
provider=target_provider,
|
|
105
|
+
timeout=30,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
messages = [LLMMessage(role=Role.user, content=prompt)]
|
|
109
|
+
|
|
110
|
+
result = await backend.complete(
|
|
111
|
+
model=target_model,
|
|
112
|
+
messages=messages,
|
|
113
|
+
temperature=0.3,
|
|
114
|
+
tools=None, # NO tools — read-only
|
|
115
|
+
tool_choice=None,
|
|
116
|
+
max_tokens=500,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return result.message.content or ""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def _ask_via_httpx(prompt: str, model_name: str) -> str:
|
|
123
|
+
"""Fallback: direct API call via httpx for models not in config."""
|
|
124
|
+
import httpx
|
|
125
|
+
|
|
126
|
+
api_key = _detect_api_key(model_name)
|
|
127
|
+
api_base = _detect_api_base(model_name)
|
|
128
|
+
|
|
129
|
+
if not api_key:
|
|
130
|
+
return f"(No API key found for '{model_name}')"
|
|
131
|
+
|
|
132
|
+
verify_ssl = os.environ.get("DRYDOCK_INSECURE") != "1"
|
|
133
|
+
async with httpx.AsyncClient(verify=verify_ssl, timeout=30.0) as client:
|
|
134
|
+
response = await client.post(
|
|
135
|
+
f"{api_base}/chat/completions",
|
|
136
|
+
headers={
|
|
137
|
+
"Authorization": f"Bearer {api_key}",
|
|
138
|
+
"Content-Type": "application/json",
|
|
139
|
+
},
|
|
140
|
+
json={
|
|
141
|
+
"model": model_name,
|
|
142
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
143
|
+
"max_tokens": 500,
|
|
144
|
+
"temperature": 0.3,
|
|
145
|
+
},
|
|
146
|
+
)
|
|
147
|
+
response.raise_for_status()
|
|
148
|
+
return response.json()["choices"][0]["message"]["content"]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _detect_api_key(model: str) -> str:
|
|
152
|
+
model_lower = model.lower()
|
|
153
|
+
if "gemini" in model_lower:
|
|
154
|
+
return os.environ.get("GEMINI_API_KEY", os.environ.get("GOOGLE_API_KEY", ""))
|
|
155
|
+
if "gpt" in model_lower or "o1" in model_lower or "o3" in model_lower:
|
|
156
|
+
return os.environ.get("OPENAI_API_KEY", "")
|
|
157
|
+
if "claude" in model_lower:
|
|
158
|
+
return os.environ.get("ANTHROPIC_API_KEY", "")
|
|
159
|
+
if "mistral" in model_lower or "devstral" in model_lower:
|
|
160
|
+
return os.environ.get("MISTRAL_API_KEY", "")
|
|
161
|
+
return os.environ.get("DRYDOCK_CONSULTANT_API_KEY", "")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _detect_api_base(model: str) -> str:
|
|
165
|
+
model_lower = model.lower()
|
|
166
|
+
if "gemini" in model_lower:
|
|
167
|
+
return "https://generativelanguage.googleapis.com/v1beta/openai"
|
|
168
|
+
if "gpt" in model_lower or "o1" in model_lower or "o3" in model_lower:
|
|
169
|
+
return "https://api.openai.com/v1"
|
|
170
|
+
if "claude" in model_lower:
|
|
171
|
+
return "https://api.anthropic.com/v1"
|
|
172
|
+
if "mistral" in model_lower or "devstral" in model_lower:
|
|
173
|
+
return os.environ.get("MISTRAL_API_BASE", "https://api.mistral.ai/v1")
|
|
174
|
+
return os.environ.get("DRYDOCK_CONSULTANT_API_BASE", "http://localhost:8000/v1")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def is_consultant_available(config: object | None = None) -> bool:
|
|
178
|
+
"""Check if a consultant model is configured."""
|
|
179
|
+
if os.environ.get("DRYDOCK_CONSULTANT_MODEL"):
|
|
180
|
+
return True
|
|
181
|
+
if config and getattr(config, "consultant_model", ""):
|
|
182
|
+
return True
|
|
183
|
+
return False
|
|
@@ -17,9 +17,36 @@ class GlobalPath:
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
_DEFAULT_DRYDOCK_HOME = Path.home() / ".drydock"
|
|
20
|
-
# Fall back to ~/.vibe if it exists and ~/.drydock doesn't (migration support)
|
|
21
20
|
_DEFAULT_VIBE_HOME = Path.home() / ".vibe"
|
|
22
21
|
|
|
22
|
+
_migration_done = False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _migrate_vibe_to_drydock() -> None:
|
|
26
|
+
"""Auto-migrate ~/.vibe → ~/.drydock if the user hasn't migrated yet."""
|
|
27
|
+
global _migration_done
|
|
28
|
+
if _migration_done:
|
|
29
|
+
return
|
|
30
|
+
_migration_done = True
|
|
31
|
+
|
|
32
|
+
if not _DEFAULT_VIBE_HOME.exists() or _DEFAULT_DRYDOCK_HOME.exists():
|
|
33
|
+
return # Nothing to migrate, or already migrated
|
|
34
|
+
|
|
35
|
+
import shutil
|
|
36
|
+
import logging
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
shutil.copytree(_DEFAULT_VIBE_HOME, _DEFAULT_DRYDOCK_HOME)
|
|
41
|
+
logger.info("Migrated ~/.vibe → ~/.drydock")
|
|
42
|
+
# Leave a note in the old directory
|
|
43
|
+
(_DEFAULT_VIBE_HOME / "MIGRATED.txt").write_text(
|
|
44
|
+
"This config has been migrated to ~/.drydock/\n"
|
|
45
|
+
"You can safely delete this .vibe directory.\n"
|
|
46
|
+
)
|
|
47
|
+
except (OSError, PermissionError) as e:
|
|
48
|
+
logger.warning("Could not migrate ~/.vibe → ~/.drydock: %s", e)
|
|
49
|
+
|
|
23
50
|
|
|
24
51
|
def _get_vibe_home() -> Path:
|
|
25
52
|
# Check env vars: DRYDOCK_HOME takes priority, then VIBE_HOME for compat
|
|
@@ -27,11 +54,12 @@ def _get_vibe_home() -> Path:
|
|
|
27
54
|
return Path(drydock_home).expanduser().resolve()
|
|
28
55
|
if vibe_home := os.getenv("VIBE_HOME"):
|
|
29
56
|
return Path(vibe_home).expanduser().resolve()
|
|
30
|
-
|
|
57
|
+
|
|
58
|
+
# Auto-migrate ~/.vibe → ~/.drydock on first run
|
|
59
|
+
_migrate_vibe_to_drydock()
|
|
60
|
+
|
|
31
61
|
if _DEFAULT_DRYDOCK_HOME.exists():
|
|
32
62
|
return _DEFAULT_DRYDOCK_HOME
|
|
33
|
-
if _DEFAULT_VIBE_HOME.exists():
|
|
34
|
-
return _DEFAULT_VIBE_HOME
|
|
35
63
|
return _DEFAULT_DRYDOCK_HOME
|
|
36
64
|
|
|
37
65
|
|
|
@@ -402,12 +402,12 @@ class TestCircuitBreaker:
|
|
|
402
402
|
|
|
403
403
|
tc = SimpleNamespace(tool_name="bash", raw_arguments='{"command": "ls -ltr"}')
|
|
404
404
|
|
|
405
|
-
assert
|
|
405
|
+
assert al._circuit_breaker_check(tc) is None
|
|
406
406
|
al._circuit_breaker_record(tc, "file1.py\nfile2.py")
|
|
407
|
-
assert
|
|
407
|
+
assert al._circuit_breaker_check(tc) is None
|
|
408
408
|
al._circuit_breaker_record(tc, "file1.py\nfile2.py")
|
|
409
409
|
|
|
410
|
-
result =
|
|
410
|
+
result = al._circuit_breaker_check(tc)
|
|
411
411
|
assert result is not None
|
|
412
412
|
assert "CIRCUIT BREAKER" in result
|
|
413
413
|
|
|
@@ -426,8 +426,8 @@ class TestCircuitBreaker:
|
|
|
426
426
|
|
|
427
427
|
al._circuit_breaker_record(tc1, "result1")
|
|
428
428
|
al._circuit_breaker_record(tc1, "result1")
|
|
429
|
-
assert
|
|
430
|
-
assert
|
|
429
|
+
assert al._circuit_breaker_check(tc1) is not None
|
|
430
|
+
assert al._circuit_breaker_check(tc2) is None
|
|
431
431
|
|
|
432
432
|
|
|
433
433
|
# ============================================================================
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
"""Consultant agent — asks a smarter model for single-turn advice.
|
|
2
|
-
|
|
3
|
-
The consultant is a READ-ONLY advisor:
|
|
4
|
-
- The local model stays in control of ALL tool calls
|
|
5
|
-
- The consultant NEVER calls tools, writes files, or runs commands
|
|
6
|
-
- It only receives a question and returns reasoning/advice
|
|
7
|
-
- Used when the local model is stuck, uncertain, or looping
|
|
8
|
-
|
|
9
|
-
Usage from agent_loop:
|
|
10
|
-
advice = await ask_consultant("I found 3 files that match. Which one likely contains the bug?")
|
|
11
|
-
|
|
12
|
-
The consultant model is configured via:
|
|
13
|
-
- CLI: drydock --consultant gemini-2.5-pro
|
|
14
|
-
- Env: DRYDOCK_CONSULTANT_MODEL=gemini-2.5-pro
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
from __future__ import annotations
|
|
18
|
-
|
|
19
|
-
import logging
|
|
20
|
-
import os
|
|
21
|
-
from typing import Any
|
|
22
|
-
|
|
23
|
-
import httpx
|
|
24
|
-
|
|
25
|
-
logger = logging.getLogger(__name__)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
async def ask_consultant(
|
|
29
|
-
question: str,
|
|
30
|
-
context: str = "",
|
|
31
|
-
model: str | None = None,
|
|
32
|
-
api_key: str | None = None,
|
|
33
|
-
api_base: str | None = None,
|
|
34
|
-
) -> str:
|
|
35
|
-
"""Ask the consultant model a question and get text advice back.
|
|
36
|
-
|
|
37
|
-
Parameters
|
|
38
|
-
----------
|
|
39
|
-
question : str
|
|
40
|
-
The specific question to ask.
|
|
41
|
-
context : str
|
|
42
|
-
Optional context (e.g., file contents, error messages).
|
|
43
|
-
model : str
|
|
44
|
-
Model name. Defaults to DRYDOCK_CONSULTANT_MODEL env var.
|
|
45
|
-
api_key : str
|
|
46
|
-
API key. Auto-detected from common env vars if not provided.
|
|
47
|
-
api_base : str
|
|
48
|
-
API base URL. Auto-detected based on model name if not provided.
|
|
49
|
-
|
|
50
|
-
Returns
|
|
51
|
-
-------
|
|
52
|
-
str
|
|
53
|
-
The consultant's advice text, or an error message.
|
|
54
|
-
"""
|
|
55
|
-
model = model or os.environ.get("DRYDOCK_CONSULTANT_MODEL", "")
|
|
56
|
-
if not model:
|
|
57
|
-
return ""
|
|
58
|
-
|
|
59
|
-
# Auto-detect API key and base URL from model name
|
|
60
|
-
api_key = api_key or _detect_api_key(model)
|
|
61
|
-
api_base = api_base or _detect_api_base(model)
|
|
62
|
-
|
|
63
|
-
if not api_key:
|
|
64
|
-
logger.warning("No API key found for consultant model '%s'", model)
|
|
65
|
-
return ""
|
|
66
|
-
|
|
67
|
-
prompt = f"""You are a senior software engineering consultant. A junior AI coding agent is asking for your advice.
|
|
68
|
-
Give concise, actionable guidance. Do NOT provide code — just tell them WHAT to do and WHERE to look.
|
|
69
|
-
|
|
70
|
-
QUESTION:
|
|
71
|
-
{question}
|
|
72
|
-
"""
|
|
73
|
-
if context:
|
|
74
|
-
prompt += f"\nCONTEXT:\n{context[:2000]}\n"
|
|
75
|
-
|
|
76
|
-
try:
|
|
77
|
-
# Use OpenAI-compatible API (works with Gemini, OpenAI, Mistral, local models)
|
|
78
|
-
verify_ssl = os.environ.get("DRYDOCK_INSECURE") != "1"
|
|
79
|
-
async with httpx.AsyncClient(verify=verify_ssl, timeout=30.0) as client:
|
|
80
|
-
response = await client.post(
|
|
81
|
-
f"{api_base}/chat/completions",
|
|
82
|
-
headers={
|
|
83
|
-
"Authorization": f"Bearer {api_key}",
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
},
|
|
86
|
-
json={
|
|
87
|
-
"model": model,
|
|
88
|
-
"messages": [{"role": "user", "content": prompt}],
|
|
89
|
-
"max_tokens": 500,
|
|
90
|
-
"temperature": 0.3,
|
|
91
|
-
# NO tools — consultant is read-only
|
|
92
|
-
},
|
|
93
|
-
)
|
|
94
|
-
response.raise_for_status()
|
|
95
|
-
data = response.json()
|
|
96
|
-
advice = data["choices"][0]["message"]["content"]
|
|
97
|
-
logger.info("Consultant (%s) responded: %s", model, advice[:100])
|
|
98
|
-
return advice
|
|
99
|
-
|
|
100
|
-
except Exception as e:
|
|
101
|
-
logger.warning("Consultant call failed: %s", e)
|
|
102
|
-
return f"(Consultant unavailable: {e})"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def _detect_api_key(model: str) -> str:
|
|
106
|
-
"""Auto-detect API key from environment based on model name."""
|
|
107
|
-
model_lower = model.lower()
|
|
108
|
-
|
|
109
|
-
if "gemini" in model_lower:
|
|
110
|
-
return os.environ.get("GEMINI_API_KEY", os.environ.get("GOOGLE_API_KEY", ""))
|
|
111
|
-
if "gpt" in model_lower or "o1" in model_lower or "o3" in model_lower:
|
|
112
|
-
return os.environ.get("OPENAI_API_KEY", "")
|
|
113
|
-
if "claude" in model_lower:
|
|
114
|
-
return os.environ.get("ANTHROPIC_API_KEY", "")
|
|
115
|
-
if "mistral" in model_lower or "devstral" in model_lower:
|
|
116
|
-
return os.environ.get("MISTRAL_API_KEY", "")
|
|
117
|
-
|
|
118
|
-
# Generic fallback
|
|
119
|
-
return os.environ.get("DRYDOCK_CONSULTANT_API_KEY", "")
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _detect_api_base(model: str) -> str:
|
|
123
|
-
"""Auto-detect API base URL from model name."""
|
|
124
|
-
model_lower = model.lower()
|
|
125
|
-
|
|
126
|
-
if "gemini" in model_lower:
|
|
127
|
-
return "https://generativelanguage.googleapis.com/v1beta/openai"
|
|
128
|
-
if "gpt" in model_lower or "o1" in model_lower or "o3" in model_lower:
|
|
129
|
-
return "https://api.openai.com/v1"
|
|
130
|
-
if "claude" in model_lower:
|
|
131
|
-
return "https://api.anthropic.com/v1"
|
|
132
|
-
if "mistral" in model_lower or "devstral" in model_lower:
|
|
133
|
-
return os.environ.get("MISTRAL_API_BASE", "https://api.mistral.ai/v1")
|
|
134
|
-
|
|
135
|
-
# Try local vLLM
|
|
136
|
-
return os.environ.get("DRYDOCK_CONSULTANT_API_BASE", "http://localhost:8000/v1")
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def is_consultant_available() -> bool:
|
|
140
|
-
"""Check if a consultant model is configured."""
|
|
141
|
-
return bool(os.environ.get("DRYDOCK_CONSULTANT_MODEL"))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|