drydock-cli 0.8.2__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/PKG-INFO +1 -1
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/agents/models.py +47 -0
- drydock_cli-1.0.0/drydock/core/hooks.py +179 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/skills/models.py +18 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/glob_tool.py +94 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/invoke_skill.py +100 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/notebook_edit.py +96 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/prompts/glob.md +9 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/prompts/invoke_skill.md +9 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/prompts/notebook_edit.md +7 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/prompts/task_manager.md +8 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/prompts/worktree.md +7 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/task_manager.py +169 -0
- drydock_cli-1.0.0/drydock/core/tools/builtins/worktree.py +166 -0
- drydock_cli-1.0.0/drydock/skills/batch/SKILL.md +45 -0
- drydock_cli-1.0.0/drydock/skills/simplify/SKILL.md +39 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/pyproject.toml +3 -1
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/CODEOWNERS +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/DISCUSSION_TEMPLATE/ideas.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/workflows/build-and-upload.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/workflows/ci.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/workflows/issue-labeler.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.github/workflows/release.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.gitignore +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.pre-commit-config.yaml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.python-version +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.typos.toml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.vscode/extensions.json +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.vscode/launch.json +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/.vscode/settings.json +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/AGENTS.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/CHANGELOG.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/CLAUDE.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/CONTRIBUTING.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/Drydock_rebrand.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/LICENSE +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/NOTICE +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/PRD.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/README.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/action.yml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/distribution/zed/LICENSE +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/distribution/zed/extension.toml +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/distribution/zed/icons/mistral_vibe.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/docs/README.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/docs/acp-setup.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/docs/proxy-setup.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/acp_agent_loop.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/acp_logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/entrypoint.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/builtins/bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/builtins/todo.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/tools/session_update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/acp/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/autocompletion/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/autocompletion/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/autocompletion/path_completion.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/autocompletion/slash_command.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/cli.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/clipboard.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/commands.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/entrypoint.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/history_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/plan_offer/adapters/http_whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/plan_offer/decide_plan_offer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/plan_offer/ports/whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/terminal_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/ansi_markdown.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/app.tcss +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/external_editor.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/handlers/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/handlers/event_handler.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/notifications/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/notifications/adapters/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/notifications/adapters/textual_notification_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/notifications/ports/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/notifications/ports/notification_port.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/approval_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/banner/banner.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/banner/petit_chat.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/braille_renderer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/chat_input/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/chat_input/body.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/chat_input/completion_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/chat_input/completion_popup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/chat_input/container.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/chat_input/text_area.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/compact.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/config_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/context_progress.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/load_more.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/loading.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/messages.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/no_markup_static.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/path_display.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/proxy_setup_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/question_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/session_picker.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/spinner.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/status_message.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/teleport_message.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/tool_widgets.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/tools.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/widgets/vscode_compat.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/windowing/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/windowing/history.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/windowing/history_windowing.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/textual_ui/windowing/state.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/adapters/filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/adapters/github_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/adapters/pypi_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/ports/update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/ports/update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/cli/update_notifier/whats_new.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/agent_loop.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/agents/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/agents/manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/auth/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/auth/crypto.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/auth/github.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/completers.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/file_indexer/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/file_indexer/ignore_rules.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/file_indexer/indexer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/file_indexer/store.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/file_indexer/watcher.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/fuzzy.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/path_prompt.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/autocompletion/path_prompt_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/config/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/config/_settings.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/config/harness_files/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/config/harness_files/_harness_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/config/harness_files/_paths.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/consultant.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/drydock_states.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/backend/anthropic.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/backend/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/backend/factory.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/backend/generic.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/backend/mistral.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/backend/reasoning_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/backend/vertex.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/exceptions.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/format.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/message_utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/llm/types.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/middleware.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/output_formatters.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/paths/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/paths/_local_config_walk.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/paths/_vibe_home.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/paths/conventions.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/plan_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/programmatic.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/cli.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/compact.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/dangerous_directory.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/diagnostic.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/explore.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/planner.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/project_context.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/prompts/tests.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/proxy_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/session/session_loader.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/session/session_logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/session/session_migration.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/session/state_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/skills/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/skills/manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/skills/parser.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/slug.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/system_prompt.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/telemetry/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/telemetry/send.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/teleport/errors.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/teleport/git.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/teleport/nuage.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/teleport/teleport.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/teleport/types.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/ask_user_question.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/exit_plan_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/grep.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/ask_user_question.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/bash.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/grep.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/read_file.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/search_replace.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/swe_bench.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/task.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/todo.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/webfetch.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/websearch.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/prompts/write_file.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/read_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/search_replace.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/task.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/todo.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/webfetch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/websearch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/builtins/write_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/injection_guard.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/mcp/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/mcp/registry.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/mcp/tools.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/mcp_sampling.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/ui.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/tools/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/trusted_folders.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/types.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/core/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/onboarding/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/onboarding/base.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/onboarding/onboarding.tcss +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/onboarding/screens/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/onboarding/screens/api_key.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/onboarding/screens/welcome.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/trusted_folders/trust_folder_dialog.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/setup/trusted_folders/trust_folder_dialog.tcss +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/skills/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/skills/create-presentation/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/skills/deep-research/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/skills/investigate/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/skills/review/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/skills/ship/SKILL.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock/whats_new.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock-acp.spec +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/drydock_terms.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/flake.lock +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/flake.nix +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/scripts/README.md +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/scripts/backup.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/scripts/bump_version.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/scripts/deploy_to_github.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/scripts/install.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/scripts/prepare_release.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/scripts/publish_to_pypi.sh +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_acp.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_agent_thought.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_compact_session_updates.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_content.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_initialize.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_list_sessions.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_load_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_multi_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_new_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_proxy_setup_acp.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_read_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_search_replace.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_set_config_option.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_set_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_set_model.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_tool_call_session_update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/acp/test_write_file.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_file_indexer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_fuzzy.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_path_completer_fuzzy.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_path_completer_recursive.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_path_completion_controller.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_path_prompt_transformer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_slash_command_controller.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/autocompletion/test_ui_chat_autocompletion.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/data/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/data/fireworks.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/data/mistral.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/test_anthropic_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/test_backend.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/test_reasoning_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/backend/test_vertex_anthropic_adapter.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/plan_offer/adapters/fake_whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/plan_offer/test_decide_plan_offer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/plan_offer/test_http_whoami_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_bell_notifications.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_braille_renderer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_clipboard.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_commands.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_copy_shortcuts.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_external_editor.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_no_markup_static.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_question_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_spinner.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_switching_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_ui_clipboard_notifications.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_ui_session_incremental_renderer.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_ui_session_resume.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/test_ui_skill_dispatch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/textual_ui/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/cli/textual_ui/test_session_picker.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_agents.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_auth_crypto.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_auth_github.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_config_load_dotenv.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_config_paths.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_config_resolution.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_file_logging.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_plan_session.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_proxy_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_slug.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_telemetry_send.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_teleport_git.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_teleport_nuage.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_teleport_service.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_trusted_folders.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/core/test_utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/e2e/common.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/e2e/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/e2e/mock_server.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/e2e/test_cli_tui_onboarding.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/e2e/test_cli_tui_streaming.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/e2e/test_cli_tui_tool_approval.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/mock/__init__.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/mock/mock_backend_factory.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/mock/mock_entrypoint.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/mock/utils.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/onboarding/test_run_onboarding.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/onboarding/test_ui_onboarding.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/session/test_session_loader.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/session/test_session_logger.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/session/test_session_migration.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/skills/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/skills/test_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/skills/test_models.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/skills/test_parser.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_collapsed.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_ask_user_question/test_snapshot_ask_user_question_expanded.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_basic_conversation/test_snapshot_shows_basic_conversation.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_code_block_horizontal_scrolling/test_snapshot_allows_horizontal_scrolling_for_long_code_blocks.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_empty_assistant_before_reasoning/test_snapshot_empty_assistant_removed_when_reasoning_starts.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_accept_edits_mode.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_auto_approve_mode.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_to_plan_mode.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_cycle_wraps_to_default.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_modes/test_snapshot_default_mode.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_pending.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_parallel_tool_calls/test_snapshot_parallel_tool_calls_resolved.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_cancel_discards_changes.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_edit_existing_values.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_error.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_save_new_values.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_buffered_reasoning_yields_before_content.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_interleaved_reasoning.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_reasoning_content/test_snapshot_shows_reasoning_content_expanded.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_release_update_notification/test_snapshot_shows_release_update_notification.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_session_resume/test_snapshot_shows_resumed_session_messages.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_partial.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_streaming_tool_call/test_snapshot_tool_call_updated.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_complete.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_auth_required.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_checking_git.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_complete.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_error.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_pushing.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_sending_token.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_status_starting_workflow.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_no_plan_message.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_switch_message.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_upgrade_message.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/__snapshots__/test_ui_snapshot_whats_new/test_snapshot_shows_whats_new_message.svg +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/base_snapshot_test_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/conftest.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/snap_compare.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_ask_user_question.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_basic_conversation.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_code_block_horizontal_scrolling.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_empty_assistant_before_reasoning.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_modes.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_parallel_tool_calls.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_proxy_setup.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_question_app.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_reasoning_content.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_release_update_notification.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_session_resume.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_streaming_tool_call.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_teleport.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/snapshots/test_ui_snapshot_whats_new.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/stubs/fake_backend.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/stubs/fake_client.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/stubs/fake_tool.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_agent_auto_compact.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_agent_backend.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_agent_observer_streaming.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_agent_stats.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_agent_tasks.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_agent_tool_call.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_agents.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_cli_programmatic_preload.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_current_bugs.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_drydock_regression.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_drydock_tasks.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_history_manager.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_integration.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_loop_detection.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_message_id.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_message_merging.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_middleware.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_real_failures.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_real_issues.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_reasoning_content.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_system_prompt.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_tagged_text.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_ui_external_editor.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_ui_input_history.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/test_user_issues.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_ask_user_question.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_bash.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_exit_plan_mode.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_grep.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_invoke_context.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_manager_get_tool_config.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_mcp.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_mcp_sampling.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_task.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_ui_bash_execution.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_webfetch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/tools/test_websearch.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/adapters/fake_update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/adapters/fake_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/test_do_update.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/test_filesystem_update_cache_repository.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/test_github_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/test_pypi_update_gateway.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/test_ui_update_notification.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/test_update_use_case.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/tests/update_notifier/test_whats_new.py +0 -0
- {drydock_cli-0.8.2 → drydock_cli-1.0.0}/uv.lock +0 -0
|
@@ -53,11 +53,22 @@ class AgentProfile:
|
|
|
53
53
|
safety: AgentSafety
|
|
54
54
|
agent_type: AgentType = AgentType.AGENT
|
|
55
55
|
overrides: dict[str, Any] = field(default_factory=dict)
|
|
56
|
+
# New fields for feature parity with Claude Code
|
|
57
|
+
model: str = "" # Per-subagent model selection (e.g., "gemini-2.5-pro")
|
|
58
|
+
max_turns: int | None = None # Per-subagent turn limit
|
|
59
|
+
background: bool = False # Run in background (non-blocking)
|
|
60
|
+
isolation: str = "" # "worktree" for git worktree isolation
|
|
61
|
+
initial_prompt: str = "" # Auto-run this prompt on start
|
|
62
|
+
allowed_tools: list[str] = field(default_factory=list) # Restrict tools
|
|
63
|
+
hooks: list[str] = field(default_factory=list) # Hook configs
|
|
56
64
|
|
|
57
65
|
def apply_to_config(self, base: VibeConfig) -> VibeConfig:
|
|
58
66
|
from drydock.core.config import VibeConfig as VC
|
|
59
67
|
|
|
60
68
|
merged = _deep_merge(base.model_dump(), self.overrides)
|
|
69
|
+
# Apply per-subagent model if set
|
|
70
|
+
if self.model:
|
|
71
|
+
merged["active_model"] = self.model
|
|
61
72
|
return VC.model_validate(merged)
|
|
62
73
|
|
|
63
74
|
@classmethod
|
|
@@ -70,9 +81,45 @@ class AgentProfile:
|
|
|
70
81
|
description=data.pop("description", ""),
|
|
71
82
|
safety=AgentSafety(data.pop("safety", AgentSafety.NEUTRAL)),
|
|
72
83
|
agent_type=AgentType(data.pop("agent_type", AgentType.AGENT)),
|
|
84
|
+
model=data.pop("model", ""),
|
|
85
|
+
max_turns=data.pop("max_turns", None),
|
|
86
|
+
background=data.pop("background", False),
|
|
87
|
+
isolation=data.pop("isolation", ""),
|
|
88
|
+
initial_prompt=data.pop("initial_prompt", ""),
|
|
89
|
+
allowed_tools=data.pop("allowed_tools", []),
|
|
90
|
+
hooks=data.pop("hooks", []),
|
|
73
91
|
overrides=data,
|
|
74
92
|
)
|
|
75
93
|
|
|
94
|
+
@classmethod
|
|
95
|
+
def from_markdown(cls, path: Path) -> AgentProfile:
|
|
96
|
+
"""Load agent profile from Markdown with YAML frontmatter."""
|
|
97
|
+
import yaml
|
|
98
|
+
content = path.read_text(encoding="utf-8")
|
|
99
|
+
if not content.startswith("---"):
|
|
100
|
+
raise ValueError(f"No YAML frontmatter in {path}")
|
|
101
|
+
parts = content.split("---", 2)
|
|
102
|
+
if len(parts) < 3:
|
|
103
|
+
raise ValueError(f"Invalid frontmatter in {path}")
|
|
104
|
+
meta = yaml.safe_load(parts[1]) or {}
|
|
105
|
+
body = parts[2].strip()
|
|
106
|
+
|
|
107
|
+
return cls(
|
|
108
|
+
name=meta.get("name", path.stem),
|
|
109
|
+
display_name=meta.get("display_name", meta.get("name", path.stem)),
|
|
110
|
+
description=meta.get("description", body[:200]),
|
|
111
|
+
safety=AgentSafety(meta.get("safety", AgentSafety.NEUTRAL)),
|
|
112
|
+
agent_type=AgentType(meta.get("agent_type", AgentType.SUBAGENT)),
|
|
113
|
+
model=meta.get("model", ""),
|
|
114
|
+
max_turns=meta.get("max_turns"),
|
|
115
|
+
background=meta.get("background", False),
|
|
116
|
+
isolation=meta.get("isolation", ""),
|
|
117
|
+
initial_prompt=meta.get("initial_prompt", ""),
|
|
118
|
+
allowed_tools=meta.get("allowed_tools") or meta.get("allowed-tools") or [],
|
|
119
|
+
hooks=meta.get("hooks", []),
|
|
120
|
+
overrides=meta.get("overrides", {}),
|
|
121
|
+
)
|
|
122
|
+
|
|
76
123
|
|
|
77
124
|
CHAT_AGENT_TOOLS = ["grep", "read_file", "ask_user_question", "task"]
|
|
78
125
|
DIAGNOSTIC_AGENT_TOOLS = ["grep", "read_file", "bash"]
|
|
@@ -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()
|
|
@@ -44,6 +44,24 @@ class SkillMetadata(BaseModel):
|
|
|
44
44
|
validation_alias="user-invocable",
|
|
45
45
|
description="Controls whether the skill appears in the slash command menu.",
|
|
46
46
|
)
|
|
47
|
+
# New fields for Claude Code parity
|
|
48
|
+
context: str = Field(
|
|
49
|
+
default="",
|
|
50
|
+
description="'fork' to run in a subagent with its own context window.",
|
|
51
|
+
)
|
|
52
|
+
agent: str = Field(
|
|
53
|
+
default="",
|
|
54
|
+
description="Subagent type to use when context is 'fork' (e.g., 'explore').",
|
|
55
|
+
)
|
|
56
|
+
model: str = Field(
|
|
57
|
+
default="",
|
|
58
|
+
description="Model to use for this skill (overrides active_model).",
|
|
59
|
+
)
|
|
60
|
+
disable_model_invocation: bool = Field(
|
|
61
|
+
default=False,
|
|
62
|
+
validation_alias="disable-model-invocation",
|
|
63
|
+
description="If true, only users can invoke this skill (not the model).",
|
|
64
|
+
)
|
|
47
65
|
|
|
48
66
|
@field_validator("allowed_tools", mode="before")
|
|
49
67
|
@classmethod
|
|
@@ -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,100 @@
|
|
|
1
|
+
"""Invoke Skill tool — lets the model call skills programmatically.
|
|
2
|
+
|
|
3
|
+
The model can invoke any registered skill by name, equivalent to
|
|
4
|
+
the user typing /skill-name in the chat.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import AsyncGenerator
|
|
10
|
+
from typing import ClassVar, final
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
from drydock.core.tools.base import (
|
|
15
|
+
BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
|
|
16
|
+
)
|
|
17
|
+
from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
|
|
18
|
+
from drydock.core.types import ToolStreamEvent, ToolResultEvent
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InvokeSkillArgs(BaseModel):
|
|
22
|
+
skill_name: str = Field(description="Name of the skill to invoke (e.g., 'investigate', 'review')")
|
|
23
|
+
arguments: str = Field(default="", description="Arguments to pass to the skill")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class InvokeSkillResult(BaseModel):
|
|
27
|
+
skill_name: str
|
|
28
|
+
content: str
|
|
29
|
+
loaded: bool
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InvokeSkill(
|
|
33
|
+
BaseTool[InvokeSkillArgs, InvokeSkillResult, BaseToolConfig, BaseToolState],
|
|
34
|
+
ToolUIData[InvokeSkillArgs, InvokeSkillResult],
|
|
35
|
+
):
|
|
36
|
+
description: ClassVar[str] = (
|
|
37
|
+
"Invoke a skill by name. Use this to activate specialized workflows "
|
|
38
|
+
"like /investigate, /review, /ship, /batch, etc."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def format_call_display(cls, args: InvokeSkillArgs) -> ToolCallDisplay:
|
|
43
|
+
return ToolCallDisplay(summary=f"skill: /{args.skill_name}")
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
47
|
+
if isinstance(event.result, InvokeSkillResult):
|
|
48
|
+
return ToolResultDisplay(success=event.result.loaded, message=f"/{event.result.skill_name}")
|
|
49
|
+
return ToolResultDisplay(success=True, message="Skill invoked")
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def get_status_text(cls) -> str:
|
|
53
|
+
return "Loading skill"
|
|
54
|
+
|
|
55
|
+
def resolve_permission(self, args: InvokeSkillArgs) -> ToolPermission | None:
|
|
56
|
+
return ToolPermission.ALWAYS
|
|
57
|
+
|
|
58
|
+
@final
|
|
59
|
+
async def run(
|
|
60
|
+
self, args: InvokeSkillArgs, ctx: InvokeContext | None = None
|
|
61
|
+
) -> AsyncGenerator[ToolStreamEvent | InvokeSkillResult, None]:
|
|
62
|
+
if not ctx or not ctx.agent_manager:
|
|
63
|
+
raise ToolError("No agent manager available")
|
|
64
|
+
|
|
65
|
+
# Find the skill
|
|
66
|
+
try:
|
|
67
|
+
from drydock.core.skills.manager import SkillManager
|
|
68
|
+
config = ctx.agent_manager.config
|
|
69
|
+
manager = SkillManager(lambda: config)
|
|
70
|
+
skills = manager.available_skills
|
|
71
|
+
|
|
72
|
+
if args.skill_name not in skills:
|
|
73
|
+
available = ", ".join(sorted(skills.keys()))
|
|
74
|
+
raise ToolError(
|
|
75
|
+
f"Skill '{args.skill_name}' not found. "
|
|
76
|
+
f"Available: {available}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
skill_info = skills[args.skill_name]
|
|
80
|
+
content = skill_info.skill_path.read_text(encoding="utf-8") if skill_info.skill_path else ""
|
|
81
|
+
|
|
82
|
+
# Strip frontmatter
|
|
83
|
+
if content.startswith("---"):
|
|
84
|
+
parts = content.split("---", 2)
|
|
85
|
+
content = parts[2].strip() if len(parts) >= 3 else content
|
|
86
|
+
|
|
87
|
+
# Prepend arguments if provided
|
|
88
|
+
if args.arguments:
|
|
89
|
+
content = f"{args.arguments}\n\n{content}"
|
|
90
|
+
|
|
91
|
+
yield InvokeSkillResult(
|
|
92
|
+
skill_name=args.skill_name,
|
|
93
|
+
content=content,
|
|
94
|
+
loaded=True,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
except ToolError:
|
|
98
|
+
raise
|
|
99
|
+
except Exception as e:
|
|
100
|
+
raise ToolError(f"Failed to load skill: {e}") from e
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""NotebookEdit tool — edit Jupyter notebook cells.
|
|
2
|
+
|
|
3
|
+
Supports editing code and markdown cells in .ipynb files.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from collections.abc import AsyncGenerator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import ClassVar, final
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
from drydock.core.tools.base import (
|
|
16
|
+
BaseTool, BaseToolConfig, BaseToolState, InvokeContext, ToolError, ToolPermission,
|
|
17
|
+
)
|
|
18
|
+
from drydock.core.tools.ui import ToolCallDisplay, ToolResultDisplay, ToolUIData
|
|
19
|
+
from drydock.core.types import ToolStreamEvent, ToolResultEvent
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NotebookEditArgs(BaseModel):
|
|
23
|
+
path: str = Field(description="Path to the .ipynb file")
|
|
24
|
+
cell_index: int = Field(description="Cell index to edit (0-based)")
|
|
25
|
+
new_source: str = Field(description="New source content for the cell")
|
|
26
|
+
cell_type: str = Field(default="code", description="Cell type: 'code' or 'markdown'")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NotebookEditResult(BaseModel):
|
|
30
|
+
path: str
|
|
31
|
+
cell_index: int
|
|
32
|
+
cell_type: str
|
|
33
|
+
total_cells: int
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class NotebookEdit(
|
|
37
|
+
BaseTool[NotebookEditArgs, NotebookEditResult, BaseToolConfig, BaseToolState],
|
|
38
|
+
ToolUIData[NotebookEditArgs, NotebookEditResult],
|
|
39
|
+
):
|
|
40
|
+
description: ClassVar[str] = "Edit a cell in a Jupyter notebook (.ipynb file)."
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def format_call_display(cls, args: NotebookEditArgs) -> ToolCallDisplay:
|
|
44
|
+
return ToolCallDisplay(summary=f"notebook_edit: cell {args.cell_index} in {args.path}")
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def get_result_display(cls, event: ToolResultEvent) -> ToolResultDisplay:
|
|
48
|
+
if isinstance(event.result, NotebookEditResult):
|
|
49
|
+
return ToolResultDisplay(success=True, message=f"Edited cell {event.result.cell_index}")
|
|
50
|
+
return ToolResultDisplay(success=True, message="Notebook edited")
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def get_status_text(cls) -> str:
|
|
54
|
+
return "Editing notebook"
|
|
55
|
+
|
|
56
|
+
@final
|
|
57
|
+
async def run(
|
|
58
|
+
self, args: NotebookEditArgs, ctx: InvokeContext | None = None
|
|
59
|
+
) -> AsyncGenerator[ToolStreamEvent | NotebookEditResult, None]:
|
|
60
|
+
file_path = Path(args.path).expanduser()
|
|
61
|
+
if not file_path.is_absolute():
|
|
62
|
+
file_path = Path.cwd() / file_path
|
|
63
|
+
|
|
64
|
+
if not file_path.exists():
|
|
65
|
+
raise ToolError(f"Notebook not found: {file_path}")
|
|
66
|
+
|
|
67
|
+
if not str(file_path).endswith(".ipynb"):
|
|
68
|
+
raise ToolError("File must be a .ipynb notebook")
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
with open(file_path, encoding="utf-8") as f:
|
|
72
|
+
notebook = json.load(f)
|
|
73
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
74
|
+
raise ToolError(f"Cannot read notebook: {e}") from e
|
|
75
|
+
|
|
76
|
+
cells = notebook.get("cells", [])
|
|
77
|
+
if args.cell_index < 0 or args.cell_index >= len(cells):
|
|
78
|
+
raise ToolError(f"Cell index {args.cell_index} out of range (0-{len(cells)-1})")
|
|
79
|
+
|
|
80
|
+
# Update the cell
|
|
81
|
+
cell = cells[args.cell_index]
|
|
82
|
+
cell["source"] = args.new_source.split("\n")
|
|
83
|
+
cell["cell_type"] = args.cell_type
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
87
|
+
json.dump(notebook, f, indent=1, ensure_ascii=False)
|
|
88
|
+
except OSError as e:
|
|
89
|
+
raise ToolError(f"Cannot write notebook: {e}") from e
|
|
90
|
+
|
|
91
|
+
yield NotebookEditResult(
|
|
92
|
+
path=str(file_path),
|
|
93
|
+
cell_index=args.cell_index,
|
|
94
|
+
cell_type=args.cell_type,
|
|
95
|
+
total_cells=len(cells),
|
|
96
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Find files matching a glob pattern. Returns sorted file paths.
|
|
2
|
+
|
|
3
|
+
Examples:
|
|
4
|
+
- `glob(pattern="**/*.py")` — all Python files
|
|
5
|
+
- `glob(pattern="src/**/*.ts", path=".")` — TypeScript files under src/
|
|
6
|
+
- `glob(pattern="*.md")` — markdown files in current directory
|
|
7
|
+
- `glob(pattern="**/test_*.py")` — all test files
|
|
8
|
+
|
|
9
|
+
Use this instead of `bash find` for file discovery.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Invoke a skill by name to activate specialized workflows.
|
|
2
|
+
|
|
3
|
+
Examples:
|
|
4
|
+
- `invoke_skill(skill_name="investigate", arguments="Fix the login timeout bug")`
|
|
5
|
+
- `invoke_skill(skill_name="review")` — run a code review
|
|
6
|
+
- `invoke_skill(skill_name="ship")` — run the shipping pipeline
|
|
7
|
+
- `invoke_skill(skill_name="batch", arguments="Add type hints to all files in src/")`
|
|
8
|
+
|
|
9
|
+
Available skills depend on what's installed. Use this to activate domain-specific workflows.
|
|
@@ -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,7 @@
|
|
|
1
|
+
Git worktree tools for parallel work in isolated copies.
|
|
2
|
+
|
|
3
|
+
- `enter_worktree(branch="feature-x")` — Create isolated worktree and switch to it
|
|
4
|
+
- `exit_worktree(cleanup=true)` — Return to main directory, optionally remove worktree
|
|
5
|
+
|
|
6
|
+
Use worktrees when you need to make changes without affecting the main branch,
|
|
7
|
+
or to work on multiple things in parallel.
|