drydock-cli 0.8.1__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/PKG-INFO +1 -1
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agent_loop.py +29 -9
- drydock_cli-0.9.0/drydock/core/hooks.py +179 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/cli.md +7 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/glob_tool.py +94 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/notebook_edit.py +96 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/glob.md +9 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/notebook_edit.md +7 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/swe_bench.md +7 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/prompts/task_manager.md +8 -0
- drydock_cli-0.9.0/drydock/core/tools/builtins/task_manager.py +169 -0
- drydock_cli-0.9.0/drydock/skills/batch/SKILL.md +45 -0
- drydock_cli-0.9.0/drydock/skills/simplify/SKILL.md +39 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/pyproject.toml +3 -1
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/deploy_to_github.sh +1 -1
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/publish_to_pypi.sh +1 -1
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/CODEOWNERS +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/build-and-upload.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/ci.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/issue-labeler.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.github/workflows/release.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.gitignore +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.pre-commit-config.yaml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.python-version +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.typos.toml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.vscode/extensions.json +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.vscode/launch.json +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/.vscode/settings.json +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/AGENTS.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/CHANGELOG.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/CLAUDE.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/CONTRIBUTING.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/Drydock_rebrand.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/LICENSE +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/NOTICE +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/PRD.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/README.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/action.yml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/distribution/zed/LICENSE +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/distribution/zed/extension.toml +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/distribution/zed/icons/mistral_vibe.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/docs/README.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/docs/acp-setup.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/docs/proxy-setup.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/acp_agent_loop.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/acp_logger.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/entrypoint.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/base.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/bash.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/todo.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/tools/session_update.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/acp/utils.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/base.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/path_completion.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/autocompletion/slash_command.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/cli.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/clipboard.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/commands.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/entrypoint.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/history_manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/terminal_setup.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/app.tcss +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/external_editor.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/compact.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/loading.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/messages.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/tools.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/textual_ui/windowing/state.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/update.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/cli/update_notifier/whats_new.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agents/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agents/manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/agents/models.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/auth/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/auth/crypto.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/auth/github.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/completers.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/store.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/fuzzy.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/_settings.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/harness_files/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_harness_manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/config/harness_files/_paths.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/consultant.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/drydock_states.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/anthropic.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/base.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/factory.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/generic.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/mistral.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/backend/vertex.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/exceptions.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/format.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/message_utils.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/llm/types.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/logger.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/middleware.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/output_formatters.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/_local_config_walk.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/_vibe_home.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/paths/conventions.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/plan_session.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/programmatic.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/compact.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/dangerous_directory.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/diagnostic.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/explore.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/planner.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/project_context.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/prompts/tests.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/proxy_setup.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/session_loader.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/session_logger.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/session_migration.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/session/state_file.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/models.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/skills/parser.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/slug.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/system_prompt.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/telemetry/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/telemetry/send.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/errors.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/git.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/nuage.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/teleport.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/teleport/types.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/base.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/ask_user_question.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/bash.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/grep.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/bash.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/grep.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/task.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/todo.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/task.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/todo.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/webfetch.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/websearch.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/injection_guard.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp/registry.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp/tools.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/mcp_sampling.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/ui.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/tools/utils.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/trusted_folders.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/types.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/core/utils.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/base.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/onboarding.tcss +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/api_key.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/onboarding/screens/welcome.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/create-presentation/SKILL.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/deep-research/SKILL.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/investigate/SKILL.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/review/SKILL.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/skills/ship/SKILL.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock/whats_new.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock-acp.spec +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/drydock_terms.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/flake.lock +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/flake.nix +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/README.md +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/backup.sh +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/bump_version.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/install.sh +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/scripts/prepare_release.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/conftest.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_acp.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_agent_thought.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_bash.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_compact_session_updates.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_content.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_initialize.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_list_sessions.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_load_session.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_multi_session.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_new_session.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_proxy_setup_acp.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_read_file.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_search_replace.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_set_config_option.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_set_mode.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_set_model.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_tool_call_session_update.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_utils.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/acp/test_write_file.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_file_indexer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_fuzzy.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completer_recursive.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_completion_controller.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_slash_command_controller.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/data/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/data/fireworks.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/data/mistral.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_anthropic_adapter.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_backend.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_reasoning_adapter.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_bell_notifications.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_braille_renderer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_clipboard.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_commands.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_copy_shortcuts.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_external_editor.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_no_markup_static.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_question_app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_spinner.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_switching_mode.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_clipboard_notifications.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_session_resume.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/test_ui_skill_dispatch.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/cli/textual_ui/test_session_picker.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/conftest.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_agents.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_auth_crypto.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_auth_github.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_config_load_dotenv.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_config_paths.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_config_resolution.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_file_logging.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_plan_session.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_proxy_setup.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_slug.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_telemetry_send.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_teleport_git.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_teleport_nuage.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_teleport_service.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_trusted_folders.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/core/test_utils.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/common.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/conftest.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/mock_server.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_onboarding.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_streaming.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/__init__.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/mock_backend_factory.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/mock_entrypoint.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/mock/utils.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/onboarding/test_run_onboarding.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/onboarding/test_ui_onboarding.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/session/test_session_loader.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/session/test_session_logger.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/session/test_session_migration.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/conftest.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/test_manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/test_models.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/skills/test_parser.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_code_block_horizontal_scrolling/test_snapshot_allows_horizontal_scrolling_for_long_code_blocks.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/base_snapshot_test_app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/conftest.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/snap_compare.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/stubs/fake_backend.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/stubs/fake_client.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/stubs/fake_tool.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_auto_compact.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_backend.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_observer_streaming.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_stats.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_tasks.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agent_tool_call.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_agents.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_cli_programmatic_preload.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_current_bugs.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_drydock_regression.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_drydock_tasks.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_history_manager.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_integration.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_loop_detection.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_message_id.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_message_merging.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_middleware.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_real_failures.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_real_issues.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_reasoning_content.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_system_prompt.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_tagged_text.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_ui_external_editor.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_ui_input_history.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/test_user_issues.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_ask_user_question.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_bash.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_exit_plan_mode.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_grep.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_invoke_context.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_manager_get_tool_config.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_mcp.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_mcp_sampling.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_task.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_ui_bash_execution.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_webfetch.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/tools/test_websearch.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_do_update.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_github_update_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_ui_update_notification.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_update_use_case.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/tests/update_notifier/test_whats_new.py +0 -0
- {drydock_cli-0.8.1 → drydock_cli-0.9.0}/uv.lock +0 -0
|
@@ -599,15 +599,35 @@ class AgentLoop:
|
|
|
599
599
|
f"Consider stopping to verify your approach."
|
|
600
600
|
)
|
|
601
601
|
|
|
602
|
-
# Bash abuse detection:
|
|
603
|
-
#
|
|
604
|
-
if not has_made_edit and bash_count >=
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
602
|
+
# Bash abuse detection: model uses bash instead of proper tools
|
|
603
|
+
# This is the #1 cause of no-patch failures (88% of cases)
|
|
604
|
+
if not has_made_edit and bash_count >= 5:
|
|
605
|
+
if bash_count == 5:
|
|
606
|
+
self.messages.append(LLMMessage(
|
|
607
|
+
role=Role.user,
|
|
608
|
+
content=(
|
|
609
|
+
"You have run 5 bash commands without making any edit. "
|
|
610
|
+
"STOP using bash for reading/searching. Use the proper tools: "
|
|
611
|
+
"read_file (not cat), grep (not bash grep), search_replace (not sed). "
|
|
612
|
+
"Call search_replace NOW to fix the bug."
|
|
613
|
+
)
|
|
614
|
+
))
|
|
615
|
+
elif bash_count == 8:
|
|
616
|
+
self.messages.append(LLMMessage(
|
|
617
|
+
role=Role.user,
|
|
618
|
+
content=(
|
|
619
|
+
"WARNING: 8 bash commands, ZERO edits. You are wasting context. "
|
|
620
|
+
"Bash does NOT fix bugs — search_replace does. "
|
|
621
|
+
"Use search_replace on your NEXT response or the session ends."
|
|
622
|
+
)
|
|
623
|
+
))
|
|
624
|
+
elif bash_count >= 12:
|
|
625
|
+
# Force stop — model is clearly stuck in bash loop
|
|
626
|
+
yield AssistantEvent(
|
|
627
|
+
content="\n\n[STOPPED: 12+ bash commands without any edit. Use search_replace.]\n",
|
|
628
|
+
stopped_by_middleware=True,
|
|
629
|
+
)
|
|
630
|
+
return
|
|
611
631
|
|
|
612
632
|
# 3-Strike Rule: search_replace keeps failing
|
|
613
633
|
if search_replace_failures >= 3:
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Hook system for DryDock lifecycle events.
|
|
2
|
+
|
|
3
|
+
Hooks run shell scripts or Python callables at specific points
|
|
4
|
+
in the agent's workflow. They are deterministic — they always run,
|
|
5
|
+
unlike CLAUDE.md which is advisory.
|
|
6
|
+
|
|
7
|
+
Hook events:
|
|
8
|
+
- PreToolUse: Before any tool executes (can block with "deny")
|
|
9
|
+
- PostToolUse: After a tool completes
|
|
10
|
+
- SessionStart: When a new session begins
|
|
11
|
+
- SessionEnd: When session ends
|
|
12
|
+
- PreEdit: Before file write/edit (can block destructive patterns)
|
|
13
|
+
- PostEdit: After file is written/edited
|
|
14
|
+
|
|
15
|
+
Configure in ~/.drydock/hooks.json or .drydock/hooks.json:
|
|
16
|
+
{
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"event": "PreToolUse",
|
|
20
|
+
"command": "python3 /path/to/guard.py",
|
|
21
|
+
"tools": ["bash", "write_file"]
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import json
|
|
30
|
+
import logging
|
|
31
|
+
import subprocess
|
|
32
|
+
from dataclasses import dataclass, field
|
|
33
|
+
from enum import StrEnum, auto
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class HookEvent(StrEnum):
|
|
41
|
+
PRE_TOOL_USE = auto()
|
|
42
|
+
POST_TOOL_USE = auto()
|
|
43
|
+
SESSION_START = auto()
|
|
44
|
+
SESSION_END = auto()
|
|
45
|
+
PRE_EDIT = auto()
|
|
46
|
+
POST_EDIT = auto()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class HookDecision(StrEnum):
|
|
50
|
+
ALLOW = auto()
|
|
51
|
+
DENY = auto()
|
|
52
|
+
ASK = auto()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class HookResult:
|
|
57
|
+
decision: HookDecision = HookDecision.ALLOW
|
|
58
|
+
message: str = ""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class HookConfig:
|
|
63
|
+
event: HookEvent
|
|
64
|
+
command: str
|
|
65
|
+
tools: list[str] = field(default_factory=list) # Empty = all tools
|
|
66
|
+
timeout: int = 10
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class HookManager:
|
|
70
|
+
"""Manages and executes lifecycle hooks."""
|
|
71
|
+
|
|
72
|
+
def __init__(self) -> None:
|
|
73
|
+
self._hooks: list[HookConfig] = []
|
|
74
|
+
self._loaded = False
|
|
75
|
+
|
|
76
|
+
def load_hooks(self, config_dir: Path | None = None) -> None:
|
|
77
|
+
"""Load hooks from config files."""
|
|
78
|
+
if self._loaded:
|
|
79
|
+
return
|
|
80
|
+
self._loaded = True
|
|
81
|
+
|
|
82
|
+
search_paths = []
|
|
83
|
+
if config_dir:
|
|
84
|
+
search_paths.append(config_dir / "hooks.json")
|
|
85
|
+
|
|
86
|
+
# Check project and user dirs
|
|
87
|
+
project_hooks = Path.cwd() / ".drydock" / "hooks.json"
|
|
88
|
+
if project_hooks.exists():
|
|
89
|
+
search_paths.insert(0, project_hooks)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
from drydock.core.paths import VIBE_HOME
|
|
93
|
+
user_hooks = VIBE_HOME.path / "hooks.json"
|
|
94
|
+
if user_hooks.exists():
|
|
95
|
+
search_paths.append(user_hooks)
|
|
96
|
+
except Exception:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
for path in search_paths:
|
|
100
|
+
try:
|
|
101
|
+
if path.exists():
|
|
102
|
+
data = json.loads(path.read_text())
|
|
103
|
+
for h in data.get("hooks", []):
|
|
104
|
+
self._hooks.append(HookConfig(
|
|
105
|
+
event=HookEvent(h["event"].lower().replace("-", "_")),
|
|
106
|
+
command=h["command"],
|
|
107
|
+
tools=h.get("tools", []),
|
|
108
|
+
timeout=h.get("timeout", 10),
|
|
109
|
+
))
|
|
110
|
+
logger.info("Loaded %d hooks from %s", len(data.get("hooks", [])), path)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.warning("Failed to load hooks from %s: %s", path, e)
|
|
113
|
+
|
|
114
|
+
def get_hooks(self, event: HookEvent, tool_name: str = "") -> list[HookConfig]:
|
|
115
|
+
"""Get hooks matching an event and optional tool name."""
|
|
116
|
+
return [
|
|
117
|
+
h for h in self._hooks
|
|
118
|
+
if h.event == event
|
|
119
|
+
and (not h.tools or tool_name in h.tools)
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
async def run_hooks(
|
|
123
|
+
self, event: HookEvent, tool_name: str = "", context: dict[str, Any] | None = None
|
|
124
|
+
) -> HookResult:
|
|
125
|
+
"""Execute all hooks for an event. Returns the most restrictive result."""
|
|
126
|
+
hooks = self.get_hooks(event, tool_name)
|
|
127
|
+
if not hooks:
|
|
128
|
+
return HookResult()
|
|
129
|
+
|
|
130
|
+
most_restrictive = HookResult()
|
|
131
|
+
|
|
132
|
+
for hook in hooks:
|
|
133
|
+
try:
|
|
134
|
+
result = await self._execute_hook(hook, context or {})
|
|
135
|
+
if result.decision == HookDecision.DENY:
|
|
136
|
+
return result # Deny immediately
|
|
137
|
+
if result.decision == HookDecision.ASK and most_restrictive.decision == HookDecision.ALLOW:
|
|
138
|
+
most_restrictive = result
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.warning("Hook %s failed: %s", hook.command, e)
|
|
141
|
+
|
|
142
|
+
return most_restrictive
|
|
143
|
+
|
|
144
|
+
async def _execute_hook(self, hook: HookConfig, context: dict[str, Any]) -> HookResult:
|
|
145
|
+
"""Execute a single hook command."""
|
|
146
|
+
import asyncio
|
|
147
|
+
|
|
148
|
+
env = {
|
|
149
|
+
**__import__("os").environ,
|
|
150
|
+
"DRYDOCK_HOOK_EVENT": hook.event.value,
|
|
151
|
+
"DRYDOCK_HOOK_CONTEXT": json.dumps(context, default=str),
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
proc = await asyncio.create_subprocess_shell(
|
|
156
|
+
hook.command,
|
|
157
|
+
stdout=asyncio.subprocess.PIPE,
|
|
158
|
+
stderr=asyncio.subprocess.PIPE,
|
|
159
|
+
env=env,
|
|
160
|
+
)
|
|
161
|
+
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=hook.timeout)
|
|
162
|
+
|
|
163
|
+
output = stdout.decode("utf-8", errors="replace").strip()
|
|
164
|
+
|
|
165
|
+
# Parse output for decision
|
|
166
|
+
if "DENY" in output.upper():
|
|
167
|
+
return HookResult(decision=HookDecision.DENY, message=output)
|
|
168
|
+
if "ASK" in output.upper():
|
|
169
|
+
return HookResult(decision=HookDecision.ASK, message=output)
|
|
170
|
+
|
|
171
|
+
return HookResult(decision=HookDecision.ALLOW, message=output)
|
|
172
|
+
|
|
173
|
+
except asyncio.TimeoutError:
|
|
174
|
+
logger.warning("Hook timed out: %s", hook.command)
|
|
175
|
+
return HookResult()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# Global hook manager instance
|
|
179
|
+
hook_manager = HookManager()
|
|
@@ -161,6 +161,13 @@ Bug Fixing in Open-Source Repos
|
|
|
161
161
|
|
|
162
162
|
NEVER edit test files. The bug is in the library source code, not the tests.
|
|
163
163
|
|
|
164
|
+
CRITICAL — Tool Usage:
|
|
165
|
+
- To EDIT files: use search_replace. NEVER use bash with sed/awk/echo to edit files.
|
|
166
|
+
- To READ files: use read_file. NEVER use bash with cat/head/tail.
|
|
167
|
+
- To SEARCH code: use grep. NEVER use bash with grep/find/rg.
|
|
168
|
+
- bash is ONLY for: running tests, checking git status, installing packages, running scripts.
|
|
169
|
+
- If you have not called search_replace, you have not fixed anything. Bash commands do not fix bugs.
|
|
170
|
+
|
|
164
171
|
Context Budget:
|
|
165
172
|
Your context window is your most important resource. Every grep result and file read consumes it.
|
|
166
173
|
Performance degrades as context fills. Budget your investigation:
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Glob tool — fast file pattern matching.
|
|
2
|
+
|
|
3
|
+
Matches files by glob pattern (e.g., "**/*.py", "src/**/*.ts").
|
|
4
|
+
Returns sorted file paths. Read-only, always auto-approved.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import AsyncGenerator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import TYPE_CHECKING, ClassVar, final
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
from drydock.core.tools.base import (
|
|
16
|
+
BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
|
|
17
|
+
)
|
|
18
|
+
from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
|
|
19
|
+
from drydock.core.types import ToolStreamEvent
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from drydock.core.types import ToolResultEvent
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GlobArgs(BaseModel):
|
|
26
|
+
pattern: str = Field(description="Glob pattern (e.g., '**/*.py', 'src/**/*.ts')")
|
|
27
|
+
path: str = Field(default=".", description="Directory to search in")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class GlobResult(BaseModel):
|
|
31
|
+
files: list[str]
|
|
32
|
+
count: int
|
|
33
|
+
truncated: bool = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class GlobConfig(BaseToolConfig):
|
|
37
|
+
permission: ToolPermission = ToolPermission.ALWAYS
|
|
38
|
+
max_results: int = 200
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Glob(
|
|
42
|
+
BaseTool[GlobArgs, GlobResult, GlobConfig, BaseToolState],
|
|
43
|
+
ToolUIData[GlobArgs, GlobResult],
|
|
44
|
+
):
|
|
45
|
+
description: ClassVar[str] = (
|
|
46
|
+
"Find files matching a glob pattern. Fast file discovery."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def format_call_display(cls, args: GlobArgs) -> ToolCallDisplay:
|
|
51
|
+
return ToolCallDisplay(summary=f"glob: {args.pattern}")
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
55
|
+
if isinstance(event.result, GlobResult):
|
|
56
|
+
return ToolResultDisplay(
|
|
57
|
+
success=True, message=f"Found {event.result.count} files"
|
|
58
|
+
)
|
|
59
|
+
return ToolResultDisplay(success=True, message="Glob complete")
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def get_status_text(cls) -> str:
|
|
63
|
+
return "Searching files"
|
|
64
|
+
|
|
65
|
+
def resolve_permission(self, args: GlobArgs) -> ToolPermission | None:
|
|
66
|
+
return ToolPermission.ALWAYS
|
|
67
|
+
|
|
68
|
+
@final
|
|
69
|
+
async def run(
|
|
70
|
+
self, args: GlobArgs, ctx: InvokeContext | None = None
|
|
71
|
+
) -> AsyncGenerator[ToolStreamEvent | GlobResult, None]:
|
|
72
|
+
search_dir = Path(args.path).expanduser()
|
|
73
|
+
if not search_dir.is_absolute():
|
|
74
|
+
search_dir = Path.cwd() / search_dir
|
|
75
|
+
|
|
76
|
+
if not search_dir.is_dir():
|
|
77
|
+
raise ToolError(f"Directory not found: {search_dir}")
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
matches = sorted(
|
|
81
|
+
str(p.relative_to(search_dir))
|
|
82
|
+
for p in search_dir.glob(args.pattern)
|
|
83
|
+
if p.is_file()
|
|
84
|
+
and "__pycache__" not in str(p)
|
|
85
|
+
and ".git/" not in str(p)
|
|
86
|
+
and "node_modules/" not in str(p)
|
|
87
|
+
)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
raise ToolError(f"Glob error: {e}") from e
|
|
90
|
+
|
|
91
|
+
truncated = len(matches) > self.config.max_results
|
|
92
|
+
files = matches[:self.config.max_results]
|
|
93
|
+
|
|
94
|
+
yield GlobResult(files=files, count=len(matches), truncated=truncated)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""NotebookEdit tool — edit Jupyter notebook cells.
|
|
2
|
+
|
|
3
|
+
Supports editing code and markdown cells in .ipynb files.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from collections.abc import AsyncGenerator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import ClassVar, final
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
from drydock.core.tools.base import (
|
|
16
|
+
BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
|
|
17
|
+
)
|
|
18
|
+
from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
|
|
19
|
+
from drydock.core.types import ToolStreamEvent, ToolResultEvent
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NotebookEditArgs(BaseModel):
|
|
23
|
+
path: str = Field(description="Path to the .ipynb file")
|
|
24
|
+
cell_index: int = Field(description="Cell index to edit (0-based)")
|
|
25
|
+
new_source: str = Field(description="New source content for the cell")
|
|
26
|
+
cell_type: str = Field(default="code", description="Cell type: 'code' or 'markdown'")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NotebookEditResult(BaseModel):
|
|
30
|
+
path: str
|
|
31
|
+
cell_index: int
|
|
32
|
+
cell_type: str
|
|
33
|
+
total_cells: int
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class NotebookEdit(
|
|
37
|
+
BaseTool[NotebookEditArgs, NotebookEditResult, BaseToolConfig, BaseToolState],
|
|
38
|
+
ToolUIData[NotebookEditArgs, NotebookEditResult],
|
|
39
|
+
):
|
|
40
|
+
description: ClassVar[str] = "Edit a cell in a Jupyter notebook (.ipynb file)."
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def format_call_display(cls, args: NotebookEditArgs) -> ToolCallDisplay:
|
|
44
|
+
return ToolCallDisplay(summary=f"notebook_edit: cell {args.cell_index} in {args.path}")
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
48
|
+
if isinstance(event.result, NotebookEditResult):
|
|
49
|
+
return ToolResultDisplay(success=True, message=f"Edited cell {event.result.cell_index}")
|
|
50
|
+
return ToolResultDisplay(success=True, message="Notebook edited")
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def get_status_text(cls) -> str:
|
|
54
|
+
return "Editing notebook"
|
|
55
|
+
|
|
56
|
+
@final
|
|
57
|
+
async def run(
|
|
58
|
+
self, args: NotebookEditArgs, ctx: InvokeContext | None = None
|
|
59
|
+
) -> AsyncGenerator[ToolStreamEvent | NotebookEditResult, None]:
|
|
60
|
+
file_path = Path(args.path).expanduser()
|
|
61
|
+
if not file_path.is_absolute():
|
|
62
|
+
file_path = Path.cwd() / file_path
|
|
63
|
+
|
|
64
|
+
if not file_path.exists():
|
|
65
|
+
raise ToolError(f"Notebook not found: {file_path}")
|
|
66
|
+
|
|
67
|
+
if not str(file_path).endswith(".ipynb"):
|
|
68
|
+
raise ToolError("File must be a .ipynb notebook")
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
with open(file_path, encoding="utf-8") as f:
|
|
72
|
+
notebook = json.load(f)
|
|
73
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
74
|
+
raise ToolError(f"Cannot read notebook: {e}") from e
|
|
75
|
+
|
|
76
|
+
cells = notebook.get("cells", [])
|
|
77
|
+
if args.cell_index < 0 or args.cell_index >= len(cells):
|
|
78
|
+
raise ToolError(f"Cell index {args.cell_index} out of range (0-{len(cells)-1})")
|
|
79
|
+
|
|
80
|
+
# Update the cell
|
|
81
|
+
cell = cells[args.cell_index]
|
|
82
|
+
cell["source"] = args.new_source.split("\n")
|
|
83
|
+
cell["cell_type"] = args.cell_type
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
87
|
+
json.dump(notebook, f, indent=1, ensure_ascii=False)
|
|
88
|
+
except OSError as e:
|
|
89
|
+
raise ToolError(f"Cannot write notebook: {e}") from e
|
|
90
|
+
|
|
91
|
+
yield NotebookEditResult(
|
|
92
|
+
path=str(file_path),
|
|
93
|
+
cell_index=args.cell_index,
|
|
94
|
+
cell_type=args.cell_type,
|
|
95
|
+
total_cells=len(cells),
|
|
96
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Find files matching a glob pattern. Returns sorted file paths.
|
|
2
|
+
|
|
3
|
+
Examples:
|
|
4
|
+
- `glob(pattern="**/*.py")` — all Python files
|
|
5
|
+
- `glob(pattern="src/**/*.ts", path=".")` — TypeScript files under src/
|
|
6
|
+
- `glob(pattern="*.md")` — markdown files in current directory
|
|
7
|
+
- `glob(pattern="**/test_*.py")` — all test files
|
|
8
|
+
|
|
9
|
+
Use this instead of `bash find` for file discovery.
|
|
@@ -29,3 +29,10 @@ COMMON PATTERNS:
|
|
|
29
29
|
- Missing import: grep for where the symbol is defined, add the import
|
|
30
30
|
- Off-by-one: check loop bounds and slice indices
|
|
31
31
|
- Unhashable type: wrap mutable container in tuple() before hashing
|
|
32
|
+
|
|
33
|
+
AFTER YOUR FIRST EDIT:
|
|
34
|
+
After making your first edit, ALWAYS check:
|
|
35
|
+
1. grep for other files that import/use the function you changed
|
|
36
|
+
2. If you edited a base class, check if subclasses override the method
|
|
37
|
+
3. If you edited a config/settings file, check if there's a related migration
|
|
38
|
+
4. If you edited a backend, check if other backends need the same fix
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Track work items during a session.
|
|
2
|
+
|
|
3
|
+
Tools:
|
|
4
|
+
- `task_create(title, description)` — Create a new task
|
|
5
|
+
- `task_list()` — List all tasks and their status
|
|
6
|
+
- `task_update(task_id, status, notes)` — Update status: pending, in_progress, completed, blocked
|
|
7
|
+
|
|
8
|
+
Use these to break complex work into trackable steps.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Task lifecycle tools — create, get, list, update tasks.
|
|
2
|
+
|
|
3
|
+
Provides interactive task management for tracking work items
|
|
4
|
+
during a session, similar to Claude Code's TaskCreate/Get/List/Update.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from collections.abc import AsyncGenerator
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import ClassVar, final
|
|
13
|
+
from uuid import uuid4
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
|
|
17
|
+
from drydock.core.tools.base import (
|
|
18
|
+
BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
|
|
19
|
+
)
|
|
20
|
+
from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
|
|
21
|
+
from drydock.core.types import ToolStreamEvent, ToolResultEvent
|
|
22
|
+
|
|
23
|
+
# In-memory task store (per session)
|
|
24
|
+
_TASKS: dict[str, dict] = {}
|
|
25
|
+
_TASK_COUNTER = 0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TaskCreateArgs(BaseModel):
|
|
29
|
+
title: str = Field(description="Brief title for the task")
|
|
30
|
+
description: str = Field(default="", description="Detailed description")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TaskCreateResult(BaseModel):
|
|
34
|
+
task_id: str
|
|
35
|
+
title: str
|
|
36
|
+
status: str = "pending"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TaskCreate(
|
|
40
|
+
BaseTool[TaskCreateArgs, TaskCreateResult, BaseToolConfig, BaseToolState],
|
|
41
|
+
ToolUIData[TaskCreateArgs, TaskCreateResult],
|
|
42
|
+
):
|
|
43
|
+
description: ClassVar[str] = "Create a new task to track work."
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def format_call_display(cls, args: TaskCreateArgs) -> ToolCallDisplay:
|
|
47
|
+
return ToolCallDisplay(summary=f"task_create: {args.title[:50]}")
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
51
|
+
if isinstance(event.result, TaskCreateResult):
|
|
52
|
+
return ToolResultDisplay(success=True, message=f"Created task #{event.result.task_id}")
|
|
53
|
+
return ToolResultDisplay(success=True, message="Task created")
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def get_status_text(cls) -> str:
|
|
57
|
+
return "Creating task"
|
|
58
|
+
|
|
59
|
+
def resolve_permission(self, args: TaskCreateArgs) -> ToolPermission | None:
|
|
60
|
+
return ToolPermission.ALWAYS
|
|
61
|
+
|
|
62
|
+
@final
|
|
63
|
+
async def run(
|
|
64
|
+
self, args: TaskCreateArgs, ctx: InvokeContext | None = None
|
|
65
|
+
) -> AsyncGenerator[ToolStreamEvent | TaskCreateResult, None]:
|
|
66
|
+
global _TASK_COUNTER
|
|
67
|
+
_TASK_COUNTER += 1
|
|
68
|
+
task_id = str(_TASK_COUNTER)
|
|
69
|
+
|
|
70
|
+
_TASKS[task_id] = {
|
|
71
|
+
"id": task_id,
|
|
72
|
+
"title": args.title,
|
|
73
|
+
"description": args.description,
|
|
74
|
+
"status": "pending",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
yield TaskCreateResult(task_id=task_id, title=args.title)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class TaskListArgs(BaseModel):
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class TaskListResult(BaseModel):
|
|
85
|
+
tasks: list[dict]
|
|
86
|
+
count: int
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TaskList(
|
|
90
|
+
BaseTool[TaskListArgs, TaskListResult, BaseToolConfig, BaseToolState],
|
|
91
|
+
ToolUIData[TaskListArgs, TaskListResult],
|
|
92
|
+
):
|
|
93
|
+
description: ClassVar[str] = "List all tasks and their status."
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def format_call_display(cls, args: TaskListArgs) -> ToolCallDisplay:
|
|
97
|
+
return ToolCallDisplay(summary="task_list")
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
101
|
+
if isinstance(event.result, TaskListResult):
|
|
102
|
+
return ToolResultDisplay(success=True, message=f"{event.result.count} tasks")
|
|
103
|
+
return ToolResultDisplay(success=True, message="Tasks listed")
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def get_status_text(cls) -> str:
|
|
107
|
+
return "Listing tasks"
|
|
108
|
+
|
|
109
|
+
def resolve_permission(self, args: TaskListArgs) -> ToolPermission | None:
|
|
110
|
+
return ToolPermission.ALWAYS
|
|
111
|
+
|
|
112
|
+
@final
|
|
113
|
+
async def run(
|
|
114
|
+
self, args: TaskListArgs, ctx: InvokeContext | None = None
|
|
115
|
+
) -> AsyncGenerator[ToolStreamEvent | TaskListResult, None]:
|
|
116
|
+
tasks = list(_TASKS.values())
|
|
117
|
+
yield TaskListResult(tasks=tasks, count=len(tasks))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TaskUpdateArgs(BaseModel):
|
|
121
|
+
task_id: str = Field(description="Task ID to update")
|
|
122
|
+
status: str = Field(description="New status: pending, in_progress, completed, blocked")
|
|
123
|
+
notes: str = Field(default="", description="Optional notes")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TaskUpdateResult(BaseModel):
|
|
127
|
+
task_id: str
|
|
128
|
+
status: str
|
|
129
|
+
title: str
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class TaskUpdate(
|
|
133
|
+
BaseTool[TaskUpdateArgs, TaskUpdateResult, BaseToolConfig, BaseToolState],
|
|
134
|
+
ToolUIData[TaskUpdateArgs, TaskUpdateResult],
|
|
135
|
+
):
|
|
136
|
+
description: ClassVar[str] = "Update a task's status."
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def format_call_display(cls, args: TaskUpdateArgs) -> ToolCallDisplay:
|
|
140
|
+
return ToolCallDisplay(summary=f"task_update: #{args.task_id} → {args.status}")
|
|
141
|
+
|
|
142
|
+
@classmethod
|
|
143
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
144
|
+
if isinstance(event.result, TaskUpdateResult):
|
|
145
|
+
return ToolResultDisplay(success=True, message=f"#{event.result.task_id} → {event.result.status}")
|
|
146
|
+
return ToolResultDisplay(success=True, message="Task updated")
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def get_status_text(cls) -> str:
|
|
150
|
+
return "Updating task"
|
|
151
|
+
|
|
152
|
+
def resolve_permission(self, args: TaskUpdateArgs) -> ToolPermission | None:
|
|
153
|
+
return ToolPermission.ALWAYS
|
|
154
|
+
|
|
155
|
+
@final
|
|
156
|
+
async def run(
|
|
157
|
+
self, args: TaskUpdateArgs, ctx: InvokeContext | None = None
|
|
158
|
+
) -> AsyncGenerator[ToolStreamEvent | TaskUpdateResult, None]:
|
|
159
|
+
if args.task_id not in _TASKS:
|
|
160
|
+
raise ToolError(f"Task #{args.task_id} not found")
|
|
161
|
+
|
|
162
|
+
task = _TASKS[args.task_id]
|
|
163
|
+
task["status"] = args.status
|
|
164
|
+
if args.notes:
|
|
165
|
+
task["notes"] = args.notes
|
|
166
|
+
|
|
167
|
+
yield TaskUpdateResult(
|
|
168
|
+
task_id=args.task_id, status=args.status, title=task["title"]
|
|
169
|
+
)
|