drydock-cli 0.5.1__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/PKG-INFO +1 -1
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/PRD.md +26 -4
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/entrypoint.py +25 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/loading.py +9 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agent_loop.py +53 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/middleware.py +40 -12
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/cli.md +10 -0
- drydock_cli-0.6.0/drydock/core/session/state_file.py +143 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/bash.py +17 -3
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/search_replace.py +7 -3
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/write_file.py +6 -0
- drydock_cli-0.6.0/drydock/core/tools/injection_guard.py +105 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/pyproject.toml +1 -1
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_drydock_tasks.py +192 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/CODEOWNERS +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/build-and-upload.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/ci.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/issue-labeler.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.github/workflows/release.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.gitignore +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.pre-commit-config.yaml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.python-version +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.typos.toml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.vscode/extensions.json +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.vscode/launch.json +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/.vscode/settings.json +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/AGENTS.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/CHANGELOG.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/CLAUDE.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/CONTRIBUTING.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/Drydock_rebrand.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/LICENSE +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/NOTICE +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/README.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/action.yml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/distribution/zed/LICENSE +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/distribution/zed/extension.toml +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/distribution/zed/icons/mistral_vibe.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/docs/README.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/docs/acp-setup.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/docs/proxy-setup.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/acp_agent_loop.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/acp_logger.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/entrypoint.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/base.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/bash.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/todo.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/tools/session_update.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/acp/utils.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/base.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/path_completion.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/autocompletion/slash_command.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/cli.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/clipboard.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/commands.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/history_manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/terminal_setup.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/app.tcss +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/external_editor.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/compact.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/messages.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/tools.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/history.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/textual_ui/windowing/state.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/update.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/cli/update_notifier/whats_new.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agents/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agents/manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/agents/models.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/auth/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/auth/crypto.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/auth/github.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/completers.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/store.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/fuzzy.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/path_prompt.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/_settings.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/harness_files/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/harness_files/_harness_manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/config/harness_files/_paths.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/drydock_states.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/anthropic.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/base.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/factory.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/generic.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/mistral.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/backend/vertex.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/exceptions.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/format.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/message_utils.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/llm/types.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/logger.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/output_formatters.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/_local_config_walk.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/_vibe_home.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/paths/conventions.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/plan_session.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/programmatic.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/compact.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/dangerous_directory.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/diagnostic.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/explore.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/planner.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/project_context.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/prompts/tests.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/proxy_setup.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/session/session_loader.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/session/session_logger.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/session/session_migration.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/models.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/skills/parser.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/slug.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/system_prompt.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/telemetry/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/telemetry/send.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/errors.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/git.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/nuage.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/teleport.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/teleport/types.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/base.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/ask_user_question.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/grep.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/bash.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/grep.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/task.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/todo.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/task.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/todo.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/webfetch.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/builtins/websearch.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp/registry.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp/tools.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/mcp_sampling.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/ui.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/tools/utils.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/trusted_folders.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/types.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/core/utils.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/base.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/onboarding.tcss +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/screens/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/screens/api_key.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/onboarding/screens/welcome.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/skills/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/skills/create-presentation/SKILL.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock/whats_new.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock-acp.spec +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/drydock_terms.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/flake.lock +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/flake.nix +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/README.md +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/bump_version.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/deploy_to_github.sh +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/install.sh +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/prepare_release.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/scripts/publish_to_pypi.sh +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/conftest.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_acp.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_agent_thought.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_bash.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_compact_session_updates.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_content.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_initialize.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_list_sessions.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_load_session.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_multi_session.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_new_session.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_proxy_setup_acp.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_read_file.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_search_replace.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_set_config_option.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_set_mode.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_set_model.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_tool_call_session_update.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_utils.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/acp/test_write_file.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_file_indexer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_fuzzy.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_completer_recursive.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_completion_controller.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_slash_command_controller.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/data/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/data/fireworks.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/data/mistral.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_anthropic_adapter.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_backend.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_reasoning_adapter.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_bell_notifications.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_braille_renderer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_clipboard.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_commands.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_copy_shortcuts.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_external_editor.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_no_markup_static.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_question_app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_spinner.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_switching_mode.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_clipboard_notifications.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_session_resume.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/test_ui_skill_dispatch.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/cli/textual_ui/test_session_picker.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/conftest.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_agents.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_auth_crypto.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_auth_github.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_config_load_dotenv.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_config_paths.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_config_resolution.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_file_logging.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_plan_session.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_proxy_setup.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_slug.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_telemetry_send.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_teleport_git.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_teleport_nuage.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_teleport_service.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_trusted_folders.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/core/test_utils.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/common.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/conftest.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/mock_server.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/test_cli_tui_onboarding.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/test_cli_tui_streaming.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/__init__.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/mock_backend_factory.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/mock_entrypoint.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/mock/utils.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/onboarding/test_run_onboarding.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/onboarding/test_ui_onboarding.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/session/test_session_loader.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/session/test_session_logger.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/session/test_session_migration.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/conftest.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/test_manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/test_models.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/skills/test_parser.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_code_block_horizontal_scrolling/test_snapshot_allows_horizontal_scrolling_for_long_code_blocks.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/base_snapshot_test_app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/conftest.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/snap_compare.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/stubs/fake_backend.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/stubs/fake_client.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/stubs/fake_tool.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_auto_compact.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_backend.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_observer_streaming.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_stats.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agent_tool_call.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_agents.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_cli_programmatic_preload.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_drydock_regression.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_history_manager.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_message_id.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_message_merging.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_middleware.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_reasoning_content.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_system_prompt.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_tagged_text.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_ui_external_editor.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/test_ui_input_history.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_ask_user_question.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_bash.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_exit_plan_mode.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_grep.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_invoke_context.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_manager_get_tool_config.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_mcp.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_mcp_sampling.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_task.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_ui_bash_execution.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_webfetch.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/tools/test_websearch.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_do_update.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_github_update_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_ui_update_notification.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_update_use_case.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/tests/update_notifier/test_whats_new.py +0 -0
- {drydock_cli-0.5.1 → drydock_cli-0.6.0}/uv.lock +0 -0
|
@@ -153,10 +153,32 @@ User-reported issues from real usage testing, all fixed:
|
|
|
153
153
|
|
|
154
154
|
### Phase 5b: Testing & CI (Mar 24)
|
|
155
155
|
|
|
156
|
-
- **
|
|
157
|
-
- **
|
|
158
|
-
- **
|
|
159
|
-
- **PyPI published** — drydock-cli v0.
|
|
156
|
+
- **76 regression tests** gating all deploys
|
|
157
|
+
- **Continuous SWE-bench loop** — 500/500 tasks tested, 49.0% pass rate (+9.4% from baseline)
|
|
158
|
+
- **vibe/ → drydock/** package rename (908 imports updated)
|
|
159
|
+
- **PyPI published** — drydock-cli v0.5.2
|
|
160
|
+
|
|
161
|
+
### Phase 6: GSD Integration & Performance (Mar 25)
|
|
162
|
+
|
|
163
|
+
GSD-inspired features (from [get-shit-done](https://github.com/gsd-build/get-shit-done)):
|
|
164
|
+
|
|
165
|
+
| Feature | Details |
|
|
166
|
+
|---------|---------|
|
|
167
|
+
| Tiered context warnings | 4 levels at 50/65/75/85% usage, debounced every 5 calls |
|
|
168
|
+
| Prompt injection guard | Detects role overrides, invisible Unicode, hidden instructions in file writes |
|
|
169
|
+
| Structured state file | `.drydock/state.md` persists task, files, decisions across sessions |
|
|
170
|
+
| Deviation handling rules | Auto-fix bugs/imports, ask user for architecture/scope decisions |
|
|
171
|
+
|
|
172
|
+
Performance and loop fixes:
|
|
173
|
+
|
|
174
|
+
| Fix | Details |
|
|
175
|
+
|-----|---------|
|
|
176
|
+
| Circuit breaker | Blocks exact same tool call after 2 attempts, returns cached result |
|
|
177
|
+
| Thinking flicker fix | Status words change every 4s, not every token |
|
|
178
|
+
| Conda env protection | Preserves user's active conda environment in subprocesses |
|
|
179
|
+
| `--insecure` / `-k` flag | Disables SSL verification for corporate proxy environments |
|
|
180
|
+
| `--consultant MODEL` flag | Allows calling a smarter model for single-turn advice when stuck |
|
|
181
|
+
| 81 regression tests | Circuit breaker, injection guard, state file, context tiers all tested |
|
|
160
182
|
|
|
161
183
|
---
|
|
162
184
|
|
|
@@ -90,6 +90,19 @@ def parse_arguments() -> argparse.Namespace:
|
|
|
90
90
|
help="Skip all tool permission checks. Equivalent to --agent auto-approve. "
|
|
91
91
|
"Use with caution — tools will execute without confirmation.",
|
|
92
92
|
)
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"-k", "--insecure",
|
|
95
|
+
action="store_true",
|
|
96
|
+
help="Disable SSL certificate verification for web searches and API calls. "
|
|
97
|
+
"Useful behind corporate proxies with self-signed certificates.",
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--consultant",
|
|
101
|
+
metavar="MODEL",
|
|
102
|
+
help="Enable consultant mode: when the agent is uncertain or stuck in a loop, "
|
|
103
|
+
"it can call a more capable model (e.g., 'gemini-2.5-pro') for single-turn advice. "
|
|
104
|
+
"The consultant is NOT used for tool calls, only reasoning.",
|
|
105
|
+
)
|
|
93
106
|
|
|
94
107
|
# Feature flag for teleport, not exposed to the user yet
|
|
95
108
|
parser.add_argument("--teleport", action="store_true", help=argparse.SUPPRESS)
|
|
@@ -160,6 +173,18 @@ def main() -> None:
|
|
|
160
173
|
if args.dangerously_skip_permissions:
|
|
161
174
|
args.agent = BuiltinAgentName.AUTO_APPROVE
|
|
162
175
|
|
|
176
|
+
# --insecure → disable SSL verification globally
|
|
177
|
+
if args.insecure:
|
|
178
|
+
os.environ["DRYDOCK_INSECURE"] = "1"
|
|
179
|
+
os.environ["CURL_CA_BUNDLE"] = ""
|
|
180
|
+
os.environ["REQUESTS_CA_BUNDLE"] = ""
|
|
181
|
+
import ssl
|
|
182
|
+
ssl._create_default_https_context = ssl._create_unverified_context
|
|
183
|
+
|
|
184
|
+
# --consultant → store for agent loop to use
|
|
185
|
+
if args.consultant:
|
|
186
|
+
os.environ["DRYDOCK_CONSULTANT_MODEL"] = args.consultant
|
|
187
|
+
|
|
163
188
|
is_interactive = args.prompt is None
|
|
164
189
|
if is_interactive:
|
|
165
190
|
check_and_resolve_trusted_folder()
|
|
@@ -64,6 +64,9 @@ class LoadingWidget(SpinnerMixin, Static):
|
|
|
64
64
|
"Stocking the galley",
|
|
65
65
|
]
|
|
66
66
|
|
|
67
|
+
# Minimum seconds between status word changes (prevents flicker)
|
|
68
|
+
_STATUS_CHANGE_INTERVAL = 4.0
|
|
69
|
+
|
|
67
70
|
def __init__(self, status: str | None = None) -> None:
|
|
68
71
|
super().__init__(classes="loading-widget")
|
|
69
72
|
self.init_spinner()
|
|
@@ -77,6 +80,7 @@ class LoadingWidget(SpinnerMixin, Static):
|
|
|
77
80
|
self._last_elapsed: int = -1
|
|
78
81
|
self._paused_total: float = 0.0
|
|
79
82
|
self._pause_start: float | None = None
|
|
83
|
+
self._last_status_change: float = 0.0
|
|
80
84
|
|
|
81
85
|
def _get_easter_egg(self) -> str | None:
|
|
82
86
|
EASTER_EGG_PROBABILITY = 0.10
|
|
@@ -111,6 +115,11 @@ class LoadingWidget(SpinnerMixin, Static):
|
|
|
111
115
|
self._pause_start = None
|
|
112
116
|
|
|
113
117
|
def set_status(self, status: str) -> None:
|
|
118
|
+
# Throttle status word changes to prevent flicker
|
|
119
|
+
now = time()
|
|
120
|
+
if now - self._last_status_change < self._STATUS_CHANGE_INTERVAL:
|
|
121
|
+
return # Too soon — keep current word
|
|
122
|
+
self._last_status_change = now
|
|
114
123
|
self.status = self._apply_easter_egg(status)
|
|
115
124
|
self._update_animation()
|
|
116
125
|
|
|
@@ -188,6 +188,10 @@ class AgentLoop:
|
|
|
188
188
|
self.middleware_pipeline = MiddlewarePipeline()
|
|
189
189
|
self._setup_middleware()
|
|
190
190
|
|
|
191
|
+
# Circuit breaker: track tool call signatures to prevent exact repeats
|
|
192
|
+
# Key: hash(tool_name + args), Value: (count, last_result_snippet)
|
|
193
|
+
self._tool_call_history: dict[str, tuple[int, str]] = {}
|
|
194
|
+
|
|
191
195
|
system_prompt = get_universal_system_prompt(
|
|
192
196
|
self.tool_manager, self.config, self.skill_manager, self.agent_manager
|
|
193
197
|
)
|
|
@@ -288,6 +292,18 @@ class AgentLoop:
|
|
|
288
292
|
|
|
289
293
|
async def act(self, msg: str) -> AsyncGenerator[BaseEvent]:
|
|
290
294
|
self._clean_message_history()
|
|
295
|
+
|
|
296
|
+
# Load project state for cross-session context
|
|
297
|
+
try:
|
|
298
|
+
from drydock.core.session.state_file import load_state
|
|
299
|
+
state_content = load_state()
|
|
300
|
+
if state_content:
|
|
301
|
+
self._inject_system_note(
|
|
302
|
+
f"Previous session state:\n{state_content[:500]}"
|
|
303
|
+
)
|
|
304
|
+
except Exception:
|
|
305
|
+
pass # Non-critical
|
|
306
|
+
|
|
291
307
|
async for event in self._conversation_loop(msg):
|
|
292
308
|
yield event
|
|
293
309
|
|
|
@@ -819,9 +835,44 @@ class AgentLoop:
|
|
|
819
835
|
)
|
|
820
836
|
)
|
|
821
837
|
|
|
838
|
+
def _circuit_breaker_check(self, tool_call: ResolvedToolCall) -> str | None:
|
|
839
|
+
"""Block exact-duplicate tool calls. Returns cached result or None."""
|
|
840
|
+
sig = hashlib.md5(
|
|
841
|
+
f"{tool_call.tool_name}:{tool_call.raw_arguments}".encode()
|
|
842
|
+
).hexdigest()
|
|
843
|
+
|
|
844
|
+
count, last_result = self._tool_call_history.get(sig, (0, ""))
|
|
845
|
+
if count >= 2:
|
|
846
|
+
return (
|
|
847
|
+
f"CIRCUIT BREAKER: You already ran this exact command {count} times "
|
|
848
|
+
f"and got the same result. Previous result: {last_result[:300]}\n\n"
|
|
849
|
+
f"Do NOT repeat. Try a DIFFERENT approach, different arguments, "
|
|
850
|
+
f"or a different tool."
|
|
851
|
+
)
|
|
852
|
+
return None
|
|
853
|
+
|
|
854
|
+
def _circuit_breaker_record(self, tool_call: ResolvedToolCall, result_text: str) -> None:
|
|
855
|
+
"""Record a tool call execution for circuit breaker tracking."""
|
|
856
|
+
sig = hashlib.md5(
|
|
857
|
+
f"{tool_call.tool_name}:{tool_call.raw_arguments}".encode()
|
|
858
|
+
).hexdigest()
|
|
859
|
+
count, _ = self._tool_call_history.get(sig, (0, ""))
|
|
860
|
+
self._tool_call_history[sig] = (count + 1, result_text[:500])
|
|
861
|
+
|
|
822
862
|
async def _process_one_tool_call(
|
|
823
863
|
self, tool_call: ResolvedToolCall
|
|
824
864
|
) -> AsyncGenerator[ToolResultEvent | ToolStreamEvent]:
|
|
865
|
+
# Circuit breaker: block exact duplicate calls after 2 attempts
|
|
866
|
+
if blocked := self._circuit_breaker_check(tool_call):
|
|
867
|
+
yield ToolResultEvent(
|
|
868
|
+
tool_name=tool_call.tool_name,
|
|
869
|
+
tool_class=tool_call.tool_class,
|
|
870
|
+
error=blocked,
|
|
871
|
+
tool_call_id=tool_call.call_id,
|
|
872
|
+
)
|
|
873
|
+
self._handle_tool_response(tool_call, blocked, "failure")
|
|
874
|
+
return
|
|
875
|
+
|
|
825
876
|
try:
|
|
826
877
|
tool_instance = self.tool_manager.get(tool_call.tool_name)
|
|
827
878
|
except Exception as exc:
|
|
@@ -890,6 +941,8 @@ class AgentLoop:
|
|
|
890
941
|
|
|
891
942
|
result_dict = result_model.model_dump()
|
|
892
943
|
text = "\n".join(f"{k}: {v}" for k, v in result_dict.items())
|
|
944
|
+
# Record for circuit breaker
|
|
945
|
+
self._circuit_breaker_record(tool_call, text)
|
|
893
946
|
self._handle_tool_response(
|
|
894
947
|
tool_call, text, "success", decision, result_dict
|
|
895
948
|
)
|
|
@@ -98,35 +98,63 @@ class AutoCompactMiddleware:
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
class ContextWarningMiddleware:
|
|
101
|
+
"""Tiered context warnings inspired by GSD's context monitoring.
|
|
102
|
+
|
|
103
|
+
Warns at multiple thresholds as context fills up:
|
|
104
|
+
- 50% used: soft warning ("you're halfway through")
|
|
105
|
+
- 65% used: moderate warning ("wrap up current task")
|
|
106
|
+
- 75% used: critical warning ("finish NOW or compact")
|
|
107
|
+
- 85% used: emergency ("stop exploring, make final edit")
|
|
108
|
+
|
|
109
|
+
Debounced: only warns every 5 tool calls to avoid spamming.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
_TIERS = [
|
|
113
|
+
(0.50, "soft", "You've used {pct:.0f}% of context ({used:,}/{max:,} tokens). Start wrapping up your current task."),
|
|
114
|
+
(0.65, "moderate", "Context at {pct:.0f}%. Stop exploring — finish your current edit and verify it works."),
|
|
115
|
+
(0.75, "critical", "Context at {pct:.0f}% — CRITICAL. Make your final edit NOW. No more grep/read_file. Use /compact if you need to continue."),
|
|
116
|
+
(0.85, "emergency", "Context nearly full ({pct:.0f}%). You MUST stop after this turn. Apply your fix with search_replace immediately or the session will degrade."),
|
|
117
|
+
]
|
|
118
|
+
|
|
101
119
|
def __init__(
|
|
102
120
|
self, threshold_percent: float = 0.5, max_context: int | None = None
|
|
103
121
|
) -> None:
|
|
104
122
|
self.threshold_percent = threshold_percent
|
|
105
123
|
self.max_context = max_context
|
|
106
|
-
self.
|
|
124
|
+
self._tier_warned: set[str] = set()
|
|
125
|
+
self._calls_since_last_warn = 0
|
|
126
|
+
self._debounce_interval = 5
|
|
107
127
|
|
|
108
128
|
async def before_turn(self, context: ConversationContext) -> MiddlewareResult:
|
|
109
|
-
|
|
129
|
+
max_context = self.max_context
|
|
130
|
+
if max_context is None or max_context == 0:
|
|
110
131
|
return MiddlewareResult()
|
|
111
132
|
|
|
112
|
-
|
|
113
|
-
|
|
133
|
+
self._calls_since_last_warn += 1
|
|
134
|
+
|
|
135
|
+
# Debounce: don't warn on every single turn
|
|
136
|
+
if self._calls_since_last_warn < self._debounce_interval:
|
|
114
137
|
return MiddlewareResult()
|
|
115
138
|
|
|
116
|
-
|
|
117
|
-
self.has_warned = True
|
|
139
|
+
pct_used = context.stats.context_tokens / max_context
|
|
118
140
|
|
|
119
|
-
|
|
120
|
-
|
|
141
|
+
# Find the highest tier we've crossed but haven't warned about
|
|
142
|
+
for threshold, tier_name, template in reversed(self._TIERS):
|
|
143
|
+
if pct_used >= threshold and tier_name not in self._tier_warned:
|
|
144
|
+
self._tier_warned.add(tier_name)
|
|
145
|
+
self._calls_since_last_warn = 0
|
|
121
146
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
147
|
+
warning_msg = f"<{VIBE_WARNING_TAG}>{template.format(pct=pct_used * 100, used=context.stats.context_tokens, max=max_context)}</{VIBE_WARNING_TAG}>"
|
|
148
|
+
return MiddlewareResult(
|
|
149
|
+
action=MiddlewareAction.INJECT_MESSAGE, message=warning_msg
|
|
150
|
+
)
|
|
151
|
+
break
|
|
125
152
|
|
|
126
153
|
return MiddlewareResult()
|
|
127
154
|
|
|
128
155
|
def reset(self, reset_reason: ResetReason = ResetReason.STOP) -> None:
|
|
129
|
-
self.
|
|
156
|
+
self._tier_warned.clear()
|
|
157
|
+
self._calls_since_last_warn = 0
|
|
130
158
|
|
|
131
159
|
|
|
132
160
|
def make_plan_agent_reminder(plan_file_path: str) -> str:
|
|
@@ -59,6 +59,16 @@ Flip-flopping (add X → remove X → add X) is a critical failure. Commit to a
|
|
|
59
59
|
Ambiguous Prompts
|
|
60
60
|
If the user's message is very short or ambiguous (e.g., "test", "check", "fix"), ask what they want before exploring the filesystem. Do NOT start scanning directories or running `find` on a vague prompt. Ask: "What would you like me to test/check/fix?"
|
|
61
61
|
|
|
62
|
+
Deviation Handling
|
|
63
|
+
When your fix attempt hits unexpected issues, follow these rules:
|
|
64
|
+
1. **Bug in your fix** → Auto-fix immediately. Re-read the error, adjust your edit, retry.
|
|
65
|
+
2. **Missing dependency/import** → Auto-resolve. Add the import or install the package.
|
|
66
|
+
3. **Blocking issue** (wrong file, missing context) → Auto-resolve by grepping for the right location.
|
|
67
|
+
4. **Architectural decision** (should we refactor? change the API? add a new module?) → STOP and ask the user. Do NOT make architectural decisions unilaterally.
|
|
68
|
+
5. **Scope change** (the fix requires changes to 5+ files, or touches unrelated code) → STOP and ask the user before proceeding.
|
|
69
|
+
|
|
70
|
+
Rules 1-3: fix silently. Rules 4-5: always ask.
|
|
71
|
+
|
|
62
72
|
Response Format
|
|
63
73
|
No Noise
|
|
64
74
|
No greetings, outros, hedging, puffery, or tool narration.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Structured state file for cross-session memory.
|
|
2
|
+
|
|
3
|
+
Maintains a `.drydock/state.md` file in the project directory that tracks:
|
|
4
|
+
- Current task and status
|
|
5
|
+
- Files modified in this session
|
|
6
|
+
- Key decisions made
|
|
7
|
+
- Blockers and next steps
|
|
8
|
+
|
|
9
|
+
Max 100 lines. Loaded on session start, updated on session end.
|
|
10
|
+
Enables "resume where I left off" workflows.
|
|
11
|
+
|
|
12
|
+
Inspired by GSD's STATE.md concept.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
_MAX_LINES = 100
|
|
24
|
+
_STATE_FILENAME = "state.md"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_state_path(project_dir: Path | None = None) -> Path:
|
|
28
|
+
"""Get the path to the state file."""
|
|
29
|
+
base = project_dir or Path.cwd()
|
|
30
|
+
return base / ".drydock" / _STATE_FILENAME
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_state(project_dir: Path | None = None) -> str:
|
|
34
|
+
"""Load the state file content, or empty string if it doesn't exist."""
|
|
35
|
+
path = _get_state_path(project_dir)
|
|
36
|
+
try:
|
|
37
|
+
if path.exists():
|
|
38
|
+
content = path.read_text(encoding="utf-8")
|
|
39
|
+
logger.info("Loaded session state from %s (%d lines)", path, content.count("\n"))
|
|
40
|
+
return content
|
|
41
|
+
except (OSError, PermissionError) as e:
|
|
42
|
+
logger.warning("Could not read state file %s: %s", path, e)
|
|
43
|
+
return ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def save_state(
|
|
47
|
+
project_dir: Path | None = None,
|
|
48
|
+
*,
|
|
49
|
+
current_task: str = "",
|
|
50
|
+
files_modified: list[str] | None = None,
|
|
51
|
+
decisions: list[str] | None = None,
|
|
52
|
+
blockers: list[str] | None = None,
|
|
53
|
+
next_steps: list[str] | None = None,
|
|
54
|
+
custom_notes: str = "",
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Save the state file with structured session information."""
|
|
57
|
+
path = _get_state_path(project_dir)
|
|
58
|
+
|
|
59
|
+
lines: list[str] = [
|
|
60
|
+
f"# DryDock Session State",
|
|
61
|
+
f"_Updated: {datetime.now().strftime('%Y-%m-%d %H:%M')}_",
|
|
62
|
+
"",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
if current_task:
|
|
66
|
+
lines.extend([f"## Current Task", current_task, ""])
|
|
67
|
+
|
|
68
|
+
if files_modified:
|
|
69
|
+
lines.append("## Files Modified")
|
|
70
|
+
for f in files_modified[-20:]: # Keep last 20
|
|
71
|
+
lines.append(f"- `{f}`")
|
|
72
|
+
lines.append("")
|
|
73
|
+
|
|
74
|
+
if decisions:
|
|
75
|
+
lines.append("## Decisions")
|
|
76
|
+
for d in decisions[-10:]: # Keep last 10
|
|
77
|
+
lines.append(f"- {d}")
|
|
78
|
+
lines.append("")
|
|
79
|
+
|
|
80
|
+
if blockers:
|
|
81
|
+
lines.append("## Blockers")
|
|
82
|
+
for b in blockers:
|
|
83
|
+
lines.append(f"- {b}")
|
|
84
|
+
lines.append("")
|
|
85
|
+
|
|
86
|
+
if next_steps:
|
|
87
|
+
lines.append("## Next Steps")
|
|
88
|
+
for n in next_steps:
|
|
89
|
+
lines.append(f"- {n}")
|
|
90
|
+
lines.append("")
|
|
91
|
+
|
|
92
|
+
if custom_notes:
|
|
93
|
+
lines.extend(["## Notes", custom_notes, ""])
|
|
94
|
+
|
|
95
|
+
# Enforce max lines
|
|
96
|
+
content = "\n".join(lines[:_MAX_LINES])
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
path.write_text(content, encoding="utf-8")
|
|
101
|
+
logger.info("Saved session state to %s", path)
|
|
102
|
+
except (OSError, PermissionError) as e:
|
|
103
|
+
logger.warning("Could not write state file %s: %s", path, e)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def update_state_files_modified(
|
|
107
|
+
project_dir: Path | None = None,
|
|
108
|
+
files: list[str] | None = None,
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Quick update: append modified files to the state without rewriting everything."""
|
|
111
|
+
if not files:
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
path = _get_state_path(project_dir)
|
|
115
|
+
try:
|
|
116
|
+
existing = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
117
|
+
|
|
118
|
+
if "## Files Modified" not in existing:
|
|
119
|
+
# Append a new section
|
|
120
|
+
existing += "\n## Files Modified\n"
|
|
121
|
+
|
|
122
|
+
for f in files:
|
|
123
|
+
entry = f"- `{f}`"
|
|
124
|
+
if entry not in existing:
|
|
125
|
+
existing += f"{entry}\n"
|
|
126
|
+
|
|
127
|
+
# Enforce max lines
|
|
128
|
+
lines = existing.split("\n")[:_MAX_LINES]
|
|
129
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
path.write_text("\n".join(lines), encoding="utf-8")
|
|
131
|
+
except (OSError, PermissionError):
|
|
132
|
+
pass # Non-critical
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def clear_state(project_dir: Path | None = None) -> None:
|
|
136
|
+
"""Remove the state file."""
|
|
137
|
+
path = _get_state_path(project_dir)
|
|
138
|
+
try:
|
|
139
|
+
if path.exists():
|
|
140
|
+
path.unlink()
|
|
141
|
+
logger.info("Cleared session state at %s", path)
|
|
142
|
+
except (OSError, PermissionError):
|
|
143
|
+
pass
|
|
@@ -110,11 +110,25 @@ def _get_base_env() -> dict[str, str]:
|
|
|
110
110
|
|
|
111
111
|
# Enable conda in non-interactive shells by setting BASH_ENV
|
|
112
112
|
# to source conda's setup script. Without this, `conda activate`
|
|
113
|
-
# fails because it's a shell function defined in .bashrc
|
|
114
|
-
#
|
|
113
|
+
# fails because it's a shell function defined in .bashrc.
|
|
114
|
+
#
|
|
115
|
+
# IMPORTANT: We also preserve the user's active conda env to avoid
|
|
116
|
+
# interfering with other environments and losing their aliases.
|
|
115
117
|
conda_sh = _get_conda_setup_script()
|
|
116
118
|
if conda_sh:
|
|
117
|
-
|
|
119
|
+
# Create a wrapper that sources conda.sh then re-activates the user's env
|
|
120
|
+
active_env = os.environ.get("CONDA_DEFAULT_ENV", "")
|
|
121
|
+
if active_env and active_env != "base":
|
|
122
|
+
# Write a temp script that initializes conda AND activates the user's env
|
|
123
|
+
import tempfile
|
|
124
|
+
wrapper = tempfile.NamedTemporaryFile(
|
|
125
|
+
mode="w", suffix=".sh", delete=False, prefix="drydock_conda_"
|
|
126
|
+
)
|
|
127
|
+
wrapper.write(f'source "{conda_sh}"\nconda activate "{active_env}" 2>/dev/null\n')
|
|
128
|
+
wrapper.close()
|
|
129
|
+
base_env["BASH_ENV"] = wrapper.name
|
|
130
|
+
else:
|
|
131
|
+
base_env["BASH_ENV"] = conda_sh
|
|
118
132
|
|
|
119
133
|
return base_env
|
|
120
134
|
|
|
@@ -119,15 +119,19 @@ class SearchReplace(
|
|
|
119
119
|
) -> AsyncGenerator[ToolStreamEvent | SearchReplaceResult, None]:
|
|
120
120
|
file_path, search_replace_blocks = self._prepare_and_validate_args(args)
|
|
121
121
|
|
|
122
|
-
# Warn (but don't block) when editing test files
|
|
123
|
-
# says NEVER edit tests, but we add a strong warning to the result
|
|
124
|
-
# so the model sees it made a mistake.
|
|
122
|
+
# Warn (but don't block) when editing test files
|
|
125
123
|
file_str = str(file_path)
|
|
126
124
|
if ("/tests/" in file_str or "/test_" in file_str or
|
|
127
125
|
file_str.endswith("_test.py") or "/testing/" in file_str):
|
|
128
126
|
import logging
|
|
129
127
|
logging.getLogger(__name__).warning("search_replace targeting test file: %s", file_str)
|
|
130
128
|
|
|
129
|
+
# Injection guard: scan replacement content for suspicious patterns
|
|
130
|
+
from drydock.core.tools.injection_guard import check_content_for_injection
|
|
131
|
+
if warning := check_content_for_injection(args.content, args.file_path):
|
|
132
|
+
import logging
|
|
133
|
+
logging.getLogger(__name__).warning("search_replace: %s", warning)
|
|
134
|
+
|
|
131
135
|
original_content = await self._read_file(file_path)
|
|
132
136
|
|
|
133
137
|
block_result = self._apply_blocks(
|
|
@@ -84,6 +84,12 @@ class WriteFile(
|
|
|
84
84
|
) -> AsyncGenerator[ToolStreamEvent | WriteFileResult, None]:
|
|
85
85
|
file_path, file_existed, content_bytes = self._prepare_and_validate_path(args)
|
|
86
86
|
|
|
87
|
+
# Injection guard: scan content for suspicious patterns
|
|
88
|
+
from drydock.core.tools.injection_guard import check_content_for_injection
|
|
89
|
+
if warning := check_content_for_injection(args.content, args.path):
|
|
90
|
+
import logging
|
|
91
|
+
logging.getLogger(__name__).warning("write_file: %s", warning)
|
|
92
|
+
|
|
87
93
|
await self._write_file(args, file_path)
|
|
88
94
|
|
|
89
95
|
yield WriteFileResult(
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Prompt injection detection for file write operations.
|
|
2
|
+
|
|
3
|
+
Scans content being written to files for common injection patterns:
|
|
4
|
+
- Role override attempts ("You are now...", "Ignore previous instructions")
|
|
5
|
+
- System prompt extraction ("Show me your system prompt")
|
|
6
|
+
- Invisible Unicode characters used to hide instructions
|
|
7
|
+
- Base64 encoded instructions
|
|
8
|
+
- Attempts to modify DryDock config/state files
|
|
9
|
+
|
|
10
|
+
Inspired by GSD's prompt-guard approach.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import base64
|
|
16
|
+
import logging
|
|
17
|
+
import re
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# Patterns that indicate prompt injection attempts
|
|
22
|
+
_INJECTION_PATTERNS: list[tuple[str, re.Pattern[str]]] = [
|
|
23
|
+
("role_override", re.compile(
|
|
24
|
+
r"(?:you are now|act as|pretend to be|ignore (?:all )?previous|"
|
|
25
|
+
r"disregard (?:all )?(?:prior|previous)|forget (?:all )?(?:prior|previous)|"
|
|
26
|
+
r"new instructions|override (?:system|instructions))",
|
|
27
|
+
re.IGNORECASE,
|
|
28
|
+
)),
|
|
29
|
+
("system_prompt_leak", re.compile(
|
|
30
|
+
r"(?:show (?:me )?your (?:system )?prompt|print your instructions|"
|
|
31
|
+
r"reveal your (?:system )?(?:prompt|instructions)|"
|
|
32
|
+
r"what are your (?:system )?instructions)",
|
|
33
|
+
re.IGNORECASE,
|
|
34
|
+
)),
|
|
35
|
+
("hidden_instruction", re.compile(
|
|
36
|
+
r"(?:<!-- (?:SYSTEM|INSTRUCTION|HIDDEN|INJECT)|"
|
|
37
|
+
r"\[INST\]|\[/INST\]|<\|im_start\|>|<\|system\|>)",
|
|
38
|
+
re.IGNORECASE,
|
|
39
|
+
)),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Invisible Unicode characters that can hide instructions
|
|
43
|
+
_INVISIBLE_CHARS = set(
|
|
44
|
+
"\u200b" # Zero-width space
|
|
45
|
+
"\u200c" # Zero-width non-joiner
|
|
46
|
+
"\u200d" # Zero-width joiner
|
|
47
|
+
"\u2060" # Word joiner
|
|
48
|
+
"\u2062" # Invisible times
|
|
49
|
+
"\u2063" # Invisible separator
|
|
50
|
+
"\u2064" # Invisible plus
|
|
51
|
+
"\ufeff" # Zero-width no-break space (BOM)
|
|
52
|
+
"\u00ad" # Soft hyphen
|
|
53
|
+
"\u034f" # Combining grapheme joiner
|
|
54
|
+
"\u061c" # Arabic letter mark
|
|
55
|
+
"\u180e" # Mongolian vowel separator
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def check_content_for_injection(content: str, file_path: str = "") -> str | None:
|
|
60
|
+
"""Check content for prompt injection patterns.
|
|
61
|
+
|
|
62
|
+
Returns a warning message if injection is detected, None otherwise.
|
|
63
|
+
This is advisory — it warns but does not block the write.
|
|
64
|
+
"""
|
|
65
|
+
if not content:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
warnings: list[str] = []
|
|
69
|
+
|
|
70
|
+
# Check for injection patterns
|
|
71
|
+
for pattern_name, pattern in _INJECTION_PATTERNS:
|
|
72
|
+
if match := pattern.search(content):
|
|
73
|
+
warnings.append(f"Suspicious pattern '{pattern_name}': '{match.group()[:50]}'")
|
|
74
|
+
|
|
75
|
+
# Check for invisible Unicode
|
|
76
|
+
invisible_found = [c for c in content if c in _INVISIBLE_CHARS]
|
|
77
|
+
if len(invisible_found) > 3:
|
|
78
|
+
warnings.append(f"Found {len(invisible_found)} invisible Unicode characters")
|
|
79
|
+
|
|
80
|
+
# Check for base64 encoded blocks that might contain instructions
|
|
81
|
+
b64_pattern = re.compile(r'[A-Za-z0-9+/]{40,}={0,2}')
|
|
82
|
+
for match in b64_pattern.finditer(content):
|
|
83
|
+
try:
|
|
84
|
+
decoded = base64.b64decode(match.group()).decode('utf-8', errors='ignore')
|
|
85
|
+
# Check if the decoded content looks like instructions
|
|
86
|
+
if any(kw in decoded.lower() for kw in ['ignore', 'override', 'system', 'instruction', 'you are']):
|
|
87
|
+
warnings.append(f"Base64 encoded block may contain hidden instructions")
|
|
88
|
+
break
|
|
89
|
+
except Exception:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
# Check for attempts to modify DryDock internals
|
|
93
|
+
if file_path:
|
|
94
|
+
protected = ['.drydock/config.toml', '.drydock/.env', 'drydock/core/', 'CLAUDE.md']
|
|
95
|
+
for p in protected:
|
|
96
|
+
if p in file_path:
|
|
97
|
+
# Not injection per se, but worth flagging
|
|
98
|
+
logger.info("Write to protected path: %s", file_path)
|
|
99
|
+
|
|
100
|
+
if warnings:
|
|
101
|
+
msg = "INJECTION WARNING: " + "; ".join(warnings)
|
|
102
|
+
logger.warning("Injection guard triggered for %s: %s", file_path, msg)
|
|
103
|
+
return msg
|
|
104
|
+
|
|
105
|
+
return None
|