From 4e495f658dedbc701aa96920a2b2e5e2998e9bfa Mon Sep 17 00:00:00 2001 From: Mathias Gesbert Date: Tue, 30 Jun 2026 15:03:21 +0200 Subject: [PATCH] v2.18.3 (#861) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Clément Drouin Co-authored-by: Laure Hugo <201583486+laure0303@users.noreply.github.com> Co-authored-by: Paul VEZIA <166131032+le-codeur-rapide@users.noreply.github.com> Co-authored-by: Peter Evers Co-authored-by: Jules YZERD Co-authored-by: Mistral Vibe --- .github/workflows/build-and-upload.yml | 2 +- .github/workflows/issue-labeler.yml | 2 +- .github/workflows/release.yml | 2 +- AGENTS.md | 8 +- CHANGELOG.md | 19 ++ README.md | 2 +- distribution/zed/extension.toml | 12 +- pyproject.toml | 10 +- scripts/bump_version.py | 4 +- tests/acp/test_initialize.py | 4 +- tests/cli/test_connector_auth_app.py | 26 +- tests/cli/test_mcp_app.py | 9 +- tests/cli/test_question_app.py | 25 ++ tests/cli/test_ui_skill_dispatch.py | 2 +- .../cli/textual_ui/test_quit_confirmation.py | 15 +- tests/cli/textual_ui/test_session_picker.py | 9 +- tests/core/test_config_orchestrator.py | 11 +- tests/core/test_project_config_layer.py | 53 +++++ tests/core/test_teleport_git.py | 28 +++ tests/onboarding/test_ui_onboarding.py | 34 ++- .../test_snapshot_config_initial.svg | 41 ++-- .../test_snapshot_config_navigate_down.svg | 41 ++-- .../test_snapshot_config_toggle_autocopy.svg | 42 ++-- ...ite_approval_long_content_after_resize.svg | 7 +- ...roval_long_content_bottom_lines_hidden.svg | 7 +- ..._snapshot_write_approval_short_content.svg | 3 +- .../test_snapshot_edit_approval_diff.svg | 5 +- .../test_snapshot_edit_approval_diff_ansi.svg | 3 +- ...napshot_edit_approval_diff_replace_all.svg | 5 +- ...st_snapshot_connector_auth_back_to_mcp.svg | 3 +- ...t_connector_auth_opens_on_disconnected.svg | 14 +- .../test_snapshot_connector_auth_show_url.svg | 16 +- ...shot_mcp_backspace_returns_to_overview.svg | 3 +- .../test_snapshot_mcp_broken_server.svg | 3 +- ...napshot_mcp_connector_back_to_overview.svg | 3 +- .../test_snapshot_mcp_connectors_only.svg | 3 +- ...apshot_mcp_connectors_sorted_by_status.svg | 3 +- ...test_snapshot_mcp_drill_into_connector.svg | 7 +- ..._snapshot_mcp_enter_drills_into_server.svg | 7 +- ...st_snapshot_mcp_help_bar_shows_connect.svg | 3 +- .../test_snapshot_mcp_overview.svg | 3 +- ...st_snapshot_mcp_overview_navigate_down.svg | 3 +- .../test_snapshot_mcp_refresh_shortcut.svg | 3 +- .../test_snapshot_mcp_server_arg.svg | 7 +- ..._snapshot_mcp_with_connectors_overview.svg | 3 +- .../test_snapshot_model_picker_initial.svg | 7 +- ...st_snapshot_model_picker_navigate_down.svg | 7 +- .../test_snapshot_narrator_speaking.svg | 3 +- .../test_snapshot_narrator_summarizing.svg | 3 +- ...ot_onboarding_api_key_with_valid_input.svg | 3 +- ..._onboarding_theme_selection[ansi-dark].svg | 29 +-- ...onboarding_theme_selection[ansi-light].svg | 27 ++- ...ot_onboarding_theme_selection[dracula].svg | 35 +-- ...est_snapshot_proxy_setup_initial_empty.svg | 3 +- ...apshot_proxy_setup_initial_with_values.svg | 3 +- ...ot_multi_question_answer_first_advance.svg | 5 +- ...ulti_question_first_answered_checkmark.svg | 5 +- .../test_snapshot_multi_question_initial.svg | 5 +- ...hot_multi_question_navigate_left_wraps.svg | 5 +- ...snapshot_multi_question_navigate_right.svg | 5 +- ..._snapshot_multi_question_tab_to_second.svg | 5 +- .../test_snapshot_multi_select_initial.svg | 5 +- ..._snapshot_multi_select_mixed_selection.svg | 7 +- ...apshot_multi_select_navigate_to_submit.svg | 5 +- ..._snapshot_multi_select_other_with_text.svg | 7 +- ...est_snapshot_multi_select_toggle_first.svg | 5 +- ..._snapshot_multi_select_toggle_multiple.svg | 5 +- .../test_snapshot_multi_select_untoggle.svg | 5 +- .../test_snapshot_question_app_initial.svg | 5 +- ...st_snapshot_question_app_navigate_down.svg | 5 +- ...napshot_question_app_navigate_to_other.svg | 7 +- ..._question_app_navigate_to_third_option.svg | 5 +- ...napshot_question_app_navigate_up_wraps.svg | 7 +- ...est_snapshot_question_app_other_typing.svg | 7 +- ...test_snapshot_rewind_error_shows_toast.svg | 3 +- .../test_snapshot_rewind_navigate_down.svg | 3 +- .../test_snapshot_rewind_navigate_up.svg | 3 +- .../test_snapshot_rewind_panel_shown.svg | 3 +- .../test_snapshot_session_picker_header.svg | 3 +- ...port_push_confirmation_cancel_selected.svg | 7 +- ...ort_push_confirmation_multiple_commits.svg | 7 +- ...leport_push_confirmation_single_commit.svg | 7 +- .../test_snapshot_trust_folder_dialog.svg | 9 +- ...napshot_trust_folder_dialog_many_files.svg | 9 +- ...hot_trust_folder_dialog_small_terminal.svg | 5 +- ...hot_trust_folder_dialog_untrusted_repo.svg | 9 +- ...snapshot_trust_folder_dialog_with_repo.svg | 9 +- ...apshot_update_prompt_continue_selected.svg | 7 +- .../test_snapshot_update_prompt_initial.svg | 7 +- .../test_snapshot_voice_enable.svg | 7 +- tests/test_mcp_auth_notices.py | 46 +++- uv.lock | 222 ++++++++++-------- vibe/__init__.py | 2 +- vibe/acp/acp_agent_loop.py | 4 +- vibe/cli/textual_ui/app.py | 10 +- vibe/cli/textual_ui/message_queue.py | 8 +- vibe/cli/textual_ui/quit_manager.py | 5 +- vibe/cli/textual_ui/shortcut_hints.py | 20 ++ vibe/cli/textual_ui/widgets/approval_app.py | 7 +- vibe/cli/textual_ui/widgets/config_app.py | 19 +- .../textual_ui/widgets/connector_auth_app.py | 22 +- vibe/cli/textual_ui/widgets/loading.py | 16 +- vibe/cli/textual_ui/widgets/mcp_app.py | 35 ++- vibe/cli/textual_ui/widgets/messages.py | 16 +- vibe/cli/textual_ui/widgets/model_picker.py | 7 +- .../cli/textual_ui/widgets/narrator_status.py | 7 +- .../cli/textual_ui/widgets/proxy_setup_app.py | 7 +- vibe/cli/textual_ui/widgets/question_app.py | 20 +- vibe/cli/textual_ui/widgets/rewind_app.py | 7 +- vibe/cli/textual_ui/widgets/session_picker.py | 12 +- vibe/cli/textual_ui/widgets/theme_picker.py | 7 +- .../cli/textual_ui/widgets/thinking_picker.py | 6 +- vibe/cli/textual_ui/widgets/voice_app.py | 7 +- vibe/core/config/_settings.py | 1 + vibe/core/config/layers/_base.py | 64 +++++ vibe/core/config/layers/project.py | 34 ++- vibe/core/config/layers/user.py | 50 +--- vibe/core/config/vibe_schema.py | 1 + vibe/core/skills/builtins/vibe.py | 7 +- vibe/core/teleport/git.py | 18 +- vibe/core/tools/builtins/ask_user_question.py | 10 +- .../builtins/prompts/ask_user_question.md | 4 +- vibe/core/tools/mcp/registry.py | 3 + vibe/setup/onboarding/screens/api_key.py | 3 +- vibe/setup/onboarding/screens/auth_method.py | 8 +- .../onboarding/screens/browser_sign_in.py | 16 +- .../onboarding/screens/theme_selection.py | 10 +- vibe/setup/onboarding/screens/welcome.py | 5 + .../trusted_folders/trust_folder_dialog.py | 6 +- .../update_prompt/update_prompt_dialog.py | 4 +- 130 files changed, 1042 insertions(+), 552 deletions(-) create mode 100644 vibe/cli/textual_ui/shortcut_hints.py create mode 100644 vibe/core/config/layers/_base.py diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index dc3b8bb..bd5e5ce 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -216,7 +216,7 @@ jobs: done - name: Attach binaries to release - uses: softprops/action-gh-release@2bb465e97f322d3cb2a965294d483e0d26a67aa9 # v3.0.1 + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: files: release-assets/*.zip env: diff --git a/.github/workflows/issue-labeler.yml b/.github/workflows/issue-labeler.yml index 2c08763..d0ad6d9 100644 --- a/.github/workflows/issue-labeler.yml +++ b/.github/workflows/issue-labeler.yml @@ -28,7 +28,7 @@ jobs: continue-on-error: true - name: Apply component label - uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: BUG_COMPONENT: ${{ steps.parse-bug.outputs.issueparser_component }} FEATURE_COMPONENT: ${{ steps.parse-feature.outputs.issueparser_component }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 54ff1ce..0817a1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,4 +49,4 @@ jobs: - name: Publish distribution to PyPI if: github.repository == 'mistralai/mistral-vibe' - uses: pypa/gh-action-pypi-publish@6733eb7d741f0b11ec6a39b58540dab7590f9b7d # v1.14.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/AGENTS.md b/AGENTS.md index 138f028..5ccd094 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -97,9 +97,15 @@ Always go through `uv` — never invoke bare `python` or `pip`. - Never use `git commit --amend`, `git push --force`, or `git push --force-with-lease`. - Always create new commits and push with a plain `git push`. -- If a push is rejected due to upstream changes, rebase onto the updated remote branch — never merge and never force-push. +- Reconciling with the upstream of the current branch (e.g. push rejected because `origin/` advanced): rebase the current branch onto its upstream — do not merge the upstream branch into the current one, never force-push. +- Reconciling with the base branch (e.g. `origin/main`) once the PR is open: merge the base branch into the current branch — do not rebase, since rebasing rewrites already-pushed history and would require a force-push. - Run git commands through `uv run` (e.g. `uv run git commit`, `uv run git push`) so pre-commit hooks resolve the project's venv — bare `git commit` fails pre-commit with `reportMissingImports` because pyright can't find third-party packages. +## CI / GitHub Actions + +- Pin every `uses:` to a full **commit SHA** with an exact version comment: `uses: owner/action@ # vX.Y.Z`. +- Resolve to the commit, not the annotated-tag object: take the `refs/tags/vX^{}` line from `git ls-remote --tags`, or `gh api repos///git/refs/tags/ --jq .object` peeled to a commit. Check with `git cat-file -t ` → `commit`, not `tag`. Never pin a moving major tag (`v9`). + ## Editor tip In Cursor / Pyright, the "Add import" quick fix is missing — use the workspace snippets `acpschema`, `acphelpers`, `vibetypes`, `vibeconfig` to insert the import line, then rename the symbol. diff --git a/CHANGELOG.md b/CHANGELOG.md index 71e4d51..11ff4b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.18.3] - 2026-06-30 + +### Added + +- `j`/`k` navigation in selectable lists across the TUI (questions, theme picker, rewind, voice, MCP panels) +- `ask_confirmation_on_exit` config option to prompt before quitting +- Project-level `.vibe/config.toml` now persists config option changes + +### Changed + +- Consistent styling and casing for keyboard shortcut hints across the TUI +- `ask_user_question` now supports more than 4 options and questions + +### Fixed + +- No longer prompts to log in for disabled MCP servers +- Teleport diff no longer mutates the real git index + + ## [2.18.2] - 2026-06-29 ### Added diff --git a/README.md b/README.md index 956dafd..97be884 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,7 @@ Simply run `vibe` to enter the interactive chat loop. - **Todo View Toggle**: Press `Ctrl+T` to toggle the todo list view. - **Debug Console**: Press `Ctrl+\` to toggle the debug console. - **Agent Selection**: Press `Shift+Tab` to cycle through agents (default, plan, ...). -- **Exit**: Type `/exit`, `exit`, `quit`, `:q`, or `:quit` in the input box, or press `Ctrl+C` / `Ctrl+D` twice within ~1 second. +- **Exit**: Type `/exit`, `exit`, `quit`, `:q`, or `:quit` in the input box, or press `Ctrl+C` / `Ctrl+D` twice within ~1 second. Set `ask_confirmation_on_exit = false` (or toggle it in `/config`) to make `Ctrl+D` quit on the first press; `Ctrl+C` always requires confirmation. You can start Vibe with a prompt using the following command: diff --git a/distribution/zed/extension.toml b/distribution/zed/extension.toml index abc64cd..2499bc9 100644 --- a/distribution/zed/extension.toml +++ b/distribution/zed/extension.toml @@ -1,7 +1,7 @@ id = "mistral-vibe" name = "Mistral Vibe" description = "Mistral's open-source coding assistant" -version = "2.18.2" +version = "2.18.3" schema_version = 1 authors = ["Mistral AI"] repository = "https://github.com/mistralai/mistral-vibe" @@ -11,21 +11,21 @@ name = "Mistral Vibe" icon = "./icons/mistral_vibe.svg" [agent_servers.mistral-vibe.targets.darwin-aarch64] -archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.2/vibe-acp-darwin-aarch64-2.18.2.zip" +archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.3/vibe-acp-darwin-aarch64-2.18.3.zip" cmd = "./vibe-acp" [agent_servers.mistral-vibe.targets.darwin-x86_64] -archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.2/vibe-acp-darwin-x86_64-2.18.2.zip" +archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.3/vibe-acp-darwin-x86_64-2.18.3.zip" cmd = "./vibe-acp" [agent_servers.mistral-vibe.targets.linux-aarch64] -archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.2/vibe-acp-linux-aarch64-2.18.2.zip" +archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.3/vibe-acp-linux-aarch64-2.18.3.zip" cmd = "./vibe-acp" [agent_servers.mistral-vibe.targets.linux-x86_64] -archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.2/vibe-acp-linux-x86_64-2.18.2.zip" +archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.3/vibe-acp-linux-x86_64-2.18.3.zip" cmd = "./vibe-acp" [agent_servers.mistral-vibe.targets.windows-x86_64] -archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.2/vibe-acp-windows-x86_64-2.18.2.zip" +archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.18.3/vibe-acp-windows-x86_64-2.18.3.zip" cmd = "./vibe-acp.exe" diff --git a/pyproject.toml b/pyproject.toml index de404d9..c065ac7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mistral-vibe" -version = "2.18.2" +version = "2.18.3" description = "Minimal CLI coding agent by Mistral" readme = "README.md" requires-python = ">=3.12" @@ -42,7 +42,7 @@ dependencies = [ "eval-type-backport==0.3.1", "gitdb==4.0.12", "gitpython==3.1.50", - "giturlparse==0.14.0", + "giturlparse==0.15.0", "google-auth==2.49.2", "googleapis-common-protos==1.74.0", "h11==0.16.0", @@ -119,7 +119,7 @@ dependencies = [ "uc-micro-py==2.0.0", "urllib3==2.7.0", "uvicorn==0.46.0 ; sys_platform != 'emscripten'", - "watchfiles==1.1.1", + "watchfiles==1.2.0", "websockets==16.0", "zipp==3.23.1", "zstandard==0.25.0", @@ -157,7 +157,7 @@ required-version = ">=0.8.0" [dependency-groups] dev = [ "cryptography>=44.0.0", - "debugpy>=1.8.19", + "debugpy>=1.8.21", "pre-commit>=4.2.0", "pyinstrument>=5.1.2", "pyright>=1.1.403", @@ -167,7 +167,7 @@ dev = [ "pytest-timeout>=2.4.0", "pytest-xdist>=3.8.0", "respx>=0.22.0", - "ruff>=0.15.13", + "ruff>=0.15.18", "tomlkit>=0.13.0", "twine>=5.0.0", "typos>=1.34.0", diff --git a/scripts/bump_version.py b/scripts/bump_version.py index 08c4250..2108d1b 100755 --- a/scripts/bump_version.py +++ b/scripts/bump_version.py @@ -144,7 +144,9 @@ Step 3 — Fill vibe/whats_new.md (reuse the same context, do NOT re-inspect git * Do not copy or paraphrase the full changelog.""" try: result = subprocess.run( - ["vibe", "-p", prompt], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + ["vibe", "-p", prompt, "--auto-approve"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, ) if result.returncode != 0: raise RuntimeError("Failed to auto-fill release notes") diff --git a/tests/acp/test_initialize.py b/tests/acp/test_initialize.py index e1db94a..4c24c59 100644 --- a/tests/acp/test_initialize.py +++ b/tests/acp/test_initialize.py @@ -72,7 +72,7 @@ class TestACPInitialize: ), ) assert response.agent_info == Implementation( - name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.18.2" + name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.18.3" ) assert response.auth_methods is not None @@ -172,7 +172,7 @@ class TestACPInitialize: ), ) assert response.agent_info == Implementation( - name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.18.2" + name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.18.3" ) assert response.auth_methods is not None diff --git a/tests/cli/test_connector_auth_app.py b/tests/cli/test_connector_auth_app.py index d22fba9..712d21b 100644 --- a/tests/cli/test_connector_auth_app.py +++ b/tests/cli/test_connector_auth_app.py @@ -4,9 +4,11 @@ from typing import Any, cast from unittest.mock import MagicMock, patch import pytest +from textual.content import Content from textual.widgets import OptionList from tests.stubs.fake_connector_registry import FakeConnectorRegistry +from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE from vibe.cli.textual_ui.widgets.connector_auth_app import ( ConnectorAuthApp, _AuthOptionId, @@ -167,13 +169,15 @@ class TestAuthActions: app._toggle_url() assert app._auth_url_visible is True text = detail.update.call_args.args[0] - assert "https://auth.example.com/oauth" in text - assert "Once authenticated" in text + assert isinstance(text, Content) + assert "https://auth.example.com/oauth" in text.plain + assert "Once authenticated" in text.plain app._toggle_url() assert app._auth_url_visible is False text = detail.update.call_args.args[0] - assert "https://auth.example.com/oauth" not in text + assert isinstance(text, Content) + assert "https://auth.example.com/oauth" not in text.plain def test_toggle_url_noop_without_url(self) -> None: app = _make_app() @@ -193,8 +197,10 @@ class TestDetailText: app._update_detail_text() text = detail.update.call_args.args[0] - assert "Once authenticated, press R to refresh" in text - assert "https://auth.example.com" not in text + assert isinstance(text, Content) + assert "Once authenticated, press r to refresh" in text.plain + assert "https://auth.example.com" not in text.plain + assert any(span.style == SHORTCUT_STYLE for span in text.spans) def test_with_url_visible(self) -> None: app = cast(Any, _make_app()) @@ -206,11 +212,13 @@ class TestDetailText: app._update_detail_text() text = detail.update.call_args.args[0] - assert "https://auth.example.com/oauth" in text - assert "Once authenticated, press R to refresh" in text + assert isinstance(text, Content) + assert "https://auth.example.com/oauth" in text.plain + assert "Once authenticated, press r to refresh" in text.plain + assert any(span.style == SHORTCUT_STYLE for span in text.spans) # URL should come before the footer - url_pos = text.index("https://auth.example.com/oauth") - footer_pos = text.index("Once authenticated") + url_pos = text.plain.index("https://auth.example.com/oauth") + footer_pos = text.plain.index("Once authenticated") assert url_pos < footer_pos diff --git a/tests/cli/test_mcp_app.py b/tests/cli/test_mcp_app.py index d23e3d2..7c13fb4 100644 --- a/tests/cli/test_mcp_app.py +++ b/tests/cli/test_mcp_app.py @@ -5,10 +5,12 @@ from typing import Any from unittest.mock import AsyncMock, MagicMock import pytest +from textual.content import Content from textual.widgets.option_list import Option from tests.stubs.fake_connector_registry import FakeConnectorRegistry from tests.stubs.fake_mcp_registry import FakeMCPRegistry +from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE from vibe.cli.textual_ui.widgets.mcp_app import ( _LIST_VIEW_HELP_AUTH, _LIST_VIEW_HELP_TOOLS, @@ -431,9 +433,10 @@ class TestConnectorAuthRequested: app.post_message.assert_not_called() option_list.add_option.assert_called_once() - assert "press R to refresh" in str( - option_list.add_option.call_args.args[0].prompt - ) + prompt = option_list.add_option.call_args.args[0].prompt + assert isinstance(prompt, Content) + assert "press r to refresh" in prompt.plain + assert any(span.style == SHORTCUT_STYLE for span in prompt.spans) def test_connected_connector_with_no_indexed_tools_shows_message(self) -> None: registry = MagicMock() diff --git a/tests/cli/test_question_app.py b/tests/cli/test_question_app.py index de0bf74..5358800 100644 --- a/tests/cli/test_question_app.py +++ b/tests/cli/test_question_app.py @@ -72,6 +72,31 @@ class TestQuestionAppState: assert len(app.answers) == 0 assert len(app.other_texts) == 0 + def test_more_than_four_options_accepted_and_rendered(self): + from vibe.cli.textual_ui.widgets.question_app import QuestionApp + + args = AskUserQuestionArgs( + questions=[ + Question( + question="Pick an analysis type", + header="Analysis", + options=[ + Choice(label="Financial"), + Choice(label="Operational"), + Choice(label="Strategic"), + Choice(label="Competitive"), + Choice(label="Multi-source"), + ], + ) + ] + ) + + app = QuestionApp(args) + + assert app.max_options == 5 + # 5 options + Other = 6 (no Submit for single-select) + assert app._total_options == 6 + def test_total_options_single_select(self, single_question_args): from vibe.cli.textual_ui.widgets.question_app import QuestionApp diff --git a/tests/cli/test_ui_skill_dispatch.py b/tests/cli/test_ui_skill_dispatch.py index 636f31f..d584099 100644 --- a/tests/cli/test_ui_skill_dispatch.py +++ b/tests/cli/test_ui_skill_dispatch.py @@ -42,7 +42,7 @@ async def _wait_for_error_message_containing( deadline = time.monotonic() + timeout while time.monotonic() < deadline: for error in vibe_app.query(ErrorMessage): - if text in error._error: + if text in str(error._error): return error await pilot.pause(0.05) raise TimeoutError( diff --git a/tests/cli/textual_ui/test_quit_confirmation.py b/tests/cli/textual_ui/test_quit_confirmation.py index 34cb242..d11a95d 100644 --- a/tests/cli/textual_ui/test_quit_confirmation.py +++ b/tests/cli/textual_ui/test_quit_confirmation.py @@ -9,7 +9,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from textual.app import WINDOWS -from tests.conftest import build_test_vibe_app +from tests.conftest import build_test_vibe_app, build_test_vibe_config from vibe.cli.textual_ui.app import VibeApp, _run_app_with_cleanup from vibe.cli.textual_ui.quit_manager import QUIT_CONFIRM_DELAY, QuitManager @@ -187,6 +187,19 @@ class TestActionDeleteRightOrQuit: "Ctrl+D", "1 queued message will be discarded" ) + def test_quits_immediately_when_confirmation_disabled(self) -> None: + app = build_test_vibe_app( + config=build_test_vibe_config(ask_confirmation_on_exit=False) + ) + with ( + patch.object(app, "_get_chat_input", return_value=None), + patch.object(app, "_force_quit") as mock_quit, + patch.object(app._quit_manager, "request_confirmation") as mock_confirm, + ): + app.action_delete_right_or_quit() + mock_quit.assert_called_once() + mock_confirm.assert_not_called() + @pytest.mark.asyncio async def test_shutdown_cleanup_cancels_in_flight_tasks(app: VibeApp) -> None: diff --git a/tests/cli/textual_ui/test_session_picker.py b/tests/cli/textual_ui/test_session_picker.py index 41ac8fb..99d4a24 100644 --- a/tests/cli/textual_ui/test_session_picker.py +++ b/tests/cli/textual_ui/test_session_picker.py @@ -7,6 +7,7 @@ import pytest from rich.text import Text from textual.widgets import OptionList +from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE from vibe.cli.textual_ui.widgets.session_picker import ( SessionPickerApp, _format_relative_time, @@ -170,7 +171,7 @@ class TestSessionPickerAppBindings: def test_has_delete_binding(self) -> None: assert "d" in self._get_binding_keys() - assert "D" in self._get_binding_keys() + assert "D" not in self._get_binding_keys() class TestSessionPickerSessionRemoval: @@ -192,9 +193,9 @@ class TestSessionPickerSessionRemoval: assert_delete_state(picker, kind="confirmation", option_id="session-a") assert option_list.replaced_prompts[-1].option_id == "session-a" - assert ( - "Press D again to delete" in option_list.replaced_prompts[-1].prompt.plain - ) + prompt = option_list.replaced_prompts[-1].prompt + assert "Press d again to delete" in prompt.plain + assert any(span.style == SHORTCUT_STYLE for span in prompt.spans) assert posted_messages == [] def test_second_delete_request_posts_delete_message( diff --git a/tests/core/test_config_orchestrator.py b/tests/core/test_config_orchestrator.py index 3983faa..a2dc8e8 100644 --- a/tests/core/test_config_orchestrator.py +++ b/tests/core/test_config_orchestrator.py @@ -665,14 +665,17 @@ async def test_apply_patch_end_to_end_routes_default_writes_to_project_layer( reason="update runtime defaults", ) - failure = assert_single_failure(result, NotImplementedError) - assert str(failure) == "ProjectConfigLayer patch persistence is not implemented yet" + assert result == [] with project_config_path.open("rb") as file: - assert tomllib.load(file) == {"default_agent": "plan"} + assert tomllib.load(file) == { + "default_agent": "auto-approve", + "active_model": "persisted-in-project-file", + } with user_config_path.open("rb") as file: assert tomllib.load(file) == {"default_agent": "accept-edits"} + # active_model stays env-model: the environment layer outranks the project file. assert orch.config.active_model == "env-model" - assert orch.config.default_agent == "plan" + assert orch.config.default_agent == "auto-approve" assert orch.config.enabled_tools == ["read"] diff --git a/tests/core/test_project_config_layer.py b/tests/core/test_project_config_layer.py index caa2c7a..115db31 100644 --- a/tests/core/test_project_config_layer.py +++ b/tests/core/test_project_config_layer.py @@ -1,11 +1,14 @@ from __future__ import annotations from pathlib import Path +import tomllib import pytest +from vibe.core.config.fingerprint import create_file_fingerprint from vibe.core.config.layer import UntrustedLayerError from vibe.core.config.layers.project import ProjectConfigLayer +from vibe.core.config.patch import AddOperationPatch, ConfigPatch, ReplaceOperationPatch from vibe.core.config.types import MISSING_BACKING_STORE_DATA_FINGERPRINT from vibe.core.paths._vibe_home import GlobalPath from vibe.core.trusted_folders import trusted_folders_manager @@ -300,3 +303,53 @@ async def test_walk_stops_at_vibe_home_parent( layer = ProjectConfigLayer(path=subdir) data = await layer.load() assert data.model_extra == {} + + +@pytest.mark.asyncio +async def test_apply_persists_to_discovered_file(tmp_working_directory: Path) -> None: + trusted_folders_manager.add_trusted(tmp_working_directory) + config_path = tmp_working_directory / ".vibe" / "config.toml" + config_path.parent.mkdir(parents=True, exist_ok=True) + config_path.write_text('active_model = "old"\n') + + layer = ProjectConfigLayer(path=tmp_working_directory) + await layer.load() + fingerprint = layer.fingerprint + assert isinstance(fingerprint, str) + + await layer.apply( + ConfigPatch( + ReplaceOperationPatch(path="/active_model", value="new"), + AddOperationPatch(path="/default_agent", value="plan"), + fingerprint=fingerprint, + ) + ) + + with config_path.open("rb") as file: + assert tomllib.load(file) == {"active_model": "new", "default_agent": "plan"} + assert layer.fingerprint == create_file_fingerprint(file) + + +@pytest.mark.asyncio +async def test_apply_creates_file_when_none_discovered( + tmp_working_directory: Path, +) -> None: + # No .vibe/config.toml exists; the layer is still trusted (nothing to distrust). + layer = ProjectConfigLayer(path=tmp_working_directory) + await layer.load() + assert layer.fingerprint == MISSING_BACKING_STORE_DATA_FINGERPRINT + + await layer.apply( + ConfigPatch( + AddOperationPatch(path="/active_model", value="created"), + fingerprint=MISSING_BACKING_STORE_DATA_FINGERPRINT, + ) + ) + + created_path = tmp_working_directory / ".vibe" / "config.toml" + with created_path.open("rb") as file: + assert tomllib.load(file) == {"active_model": "created"} + assert layer.fingerprint == create_file_fingerprint(file) + + assert layer.is_file_discovered + assert layer.config_file_path == created_path diff --git a/tests/core/test_teleport_git.py b/tests/core/test_teleport_git.py index 1643e4e..7ff0e09 100644 --- a/tests/core/test_teleport_git.py +++ b/tests/core/test_teleport_git.py @@ -3,6 +3,7 @@ from __future__ import annotations from pathlib import Path from unittest.mock import AsyncMock, MagicMock, patch +from git import Repo import pytest from vibe.core.teleport.errors import ( @@ -37,10 +38,21 @@ def make_mock_repo( mock.git.branch.return_value = "" mock.git.rev_list.return_value = "0" mock.git.rev_parse.return_value = "abc123" + mock.index.path = str(Path("/tmp/mock-index")) mock.remote.return_value = make_mock_remote(urls[0] if urls else "") return mock +def make_real_repo(workdir: Path) -> Repo: + repo = Repo.init(workdir, initial_branch="main") + repo.config_writer().set_value("user", "name", "Tester").release() + repo.config_writer().set_value("user", "email", "t@example.com").release() + (workdir / "tracked.txt").write_text("base\n") + repo.index.add(["tracked.txt"]) + repo.index.commit("initial") + return repo + + class TestGitRepositoryParseGithubUrl: def test_parse_ssh_url(self) -> None: result = GitRepository._parse_github_url("git@github.com:owner/repo.git") @@ -192,6 +204,22 @@ class TestGitRepositoryGetInfo: assert info.branch is None +class TestGitRepositoryGetDiff: + @pytest.mark.asyncio + async def test_includes_untracked_files_without_mutating_index( + self, tmp_path: Path + ) -> None: + source_repo = make_real_repo(tmp_path) + (tmp_path / "new.txt").write_text("hello\n") + status_before = source_repo.git.status("--short") + + async with GitRepository(tmp_path) as git_repo: + diff = await git_repo._get_diff(source_repo) + + assert "diff --git a/new.txt b/new.txt" in diff + assert source_repo.git.status("--short") == status_before + + class TestGitRepositoryIsCommitPushed: @pytest.fixture def repo(self, tmp_path: Path) -> GitRepository: diff --git a/tests/onboarding/test_ui_onboarding.py b/tests/onboarding/test_ui_onboarding.py index da95cc2..b945a6f 100644 --- a/tests/onboarding/test_ui_onboarding.py +++ b/tests/onboarding/test_ui_onboarding.py @@ -10,6 +10,7 @@ from typing import cast import keyring from keyring.errors import KeyringError import pytest +from textual.content import Content from textual.events import Resize from textual.geometry import Size from textual.pilot import Pilot @@ -22,6 +23,7 @@ from tests.browser_sign_in.stubs import ( build_sign_in_process, ) from tests.conftest import build_test_vibe_config +from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.config import ModelConfig, ProviderConfig, VibeConfig from vibe.core.config._settings import ( @@ -192,6 +194,12 @@ def _browser_sign_in_hint(screen: Screen) -> str: return str(screen.query_one("#browser-sign-in-hint", NoMarkupStatic).render()) +def _assert_browser_sign_in_shortcuts_styled(screen: Screen) -> None: + text = screen.query_one("#browser-sign-in-hint", NoMarkupStatic).render() + assert isinstance(text, Content) + assert any(span.style == SHORTCUT_STYLE for span in text.spans) + + def _browser_sign_in_url_text(screen: Screen) -> str: return str(screen.query_one("#browser-sign-in-url", Static).render()) @@ -445,11 +453,15 @@ async def test_ui_shows_browser_sign_in_url_copy_prompt_without_raw_url() -> Non lambda: "copy this URL" in _browser_sign_in_url_text(app.screen), pilot ) url_text = _browser_sign_in_url_text(app.screen) - assert "If your browser did not open, copy this URL (press C)" in url_text + assert "If your browser did not open, copy this URL (press c)" in url_text + url_render = app.screen.query_one("#browser-sign-in-url", Static).render() + assert isinstance(url_render, Content) + assert any(span.style == SHORTCUT_STYLE for span in url_render.spans) assert "process-1" not in url_text assert _browser_sign_in_hint(app.screen) == ( - "Press M to enter API key manually - Esc to cancel" + "Press m to enter API key manually - Esc to cancel" ) + _assert_browser_sign_in_shortcuts_styled(app.screen) @pytest.mark.asyncio @@ -603,10 +615,11 @@ async def test_ui_keeps_last_sign_in_url_copy_prompt_after_open_browser_failure( pilot, ) assert _browser_sign_in_hint(app.screen) == ( - "Press R to retry - Press M to enter API key manually - Esc to cancel" + "Press r to retry - Press m to enter API key manually - Esc to cancel" ) + _assert_browser_sign_in_shortcuts_styled(app.screen) url_text = _browser_sign_in_url_text(app.screen) - assert "If your browser did not open, copy this URL (press C)" in url_text + assert "If your browser did not open, copy this URL (press c)" in url_text assert "process-1" not in url_text @@ -686,8 +699,9 @@ async def test_ui_retry_hides_old_sign_in_url_and_uses_fresh_attempt_url() -> No assert "process-2" not in _browser_sign_in_url_text(app.screen) assert "process-1" not in _browser_sign_in_url_text(app.screen) assert _browser_sign_in_hint(app.screen) == ( - "Press M to enter API key manually - Esc to cancel" + "Press m to enter API key manually - Esc to cancel" ) + _assert_browser_sign_in_shortcuts_styled(app.screen) await pilot.press("c") assert copied_urls == [_expected_browser_sign_in_url(process_id="process-2")] @@ -748,7 +762,7 @@ async def test_ui_preserves_completed_browser_sign_in_during_success_delay() -> assert app.screen.state.variant == "success" hint = str(app.screen.query_one("#browser-sign-in-hint").render()) assert "Finishing setup..." in hint - assert "Press M to enter API key manually - Esc to cancel" not in hint + assert "Press m to enter API key manually - Esc to cancel" not in hint assert app.return_value is None assert "sk-browser-onboarding-test-key" in _saved_env_contents() await pilot.press("m", "escape") @@ -861,9 +875,10 @@ async def test_ui_shows_retryable_error_when_browser_sign_in_fails_unexpectedly( assert app.screen.state.variant == "error" assert _active_browser_sign_in_step_card(app.screen).has_class("active") assert ( - "Press R to retry - Press M to enter API key manually - Esc to cancel" + "Press r to retry - Press m to enter API key manually - Esc to cancel" in str(app.screen.query_one("#browser-sign-in-hint").render()) ) + _assert_browser_sign_in_shortcuts_styled(app.screen) assert app.return_value is None @@ -922,11 +937,12 @@ async def test_ui_waits_for_browser_sign_in_cleanup_before_retrying() -> None: ) await _wait_for( lambda: ( - "Press M to enter API key manually - Esc to cancel" + "Press m to enter API key manually - Esc to cancel" in str(hint_widget.render()) ), pilot, ) + _assert_browser_sign_in_shortcuts_styled(app.screen) await pilot.press("r") await _wait_for( @@ -940,7 +956,7 @@ async def test_ui_waits_for_browser_sign_in_cleanup_before_retrying() -> None: ) await _wait_for( lambda: ( - "Press M to enter API key manually - Esc to cancel" + "Press m to enter API key manually - Esc to cancel" in str(hint_widget.render()) ), pilot, diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_initial.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_initial.svg index 76e0abe..45912f9 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_initial.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_initial.svg @@ -40,6 +40,7 @@ .terminal-r6 { fill: #c5c8c6;font-weight: bold } .terminal-r7 { fill: #98a84b;font-weight: bold } .terminal-r8 { fill: #868887 } +.terminal-r9 { fill: #6b753d;font-weight: bold } @@ -161,7 +162,7 @@ - + @@ -176,27 +177,27 @@ - -  ⡠⣒⠄  ⡔⢄⠔⡄ - ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣ -  ⠉⠒⠣⠤⠵⠤⠬⠮⠆ - -Mistral Vibe v0.0.0 · devstral-latest[off] · [Subscription] Pro -1 model · 0 MCP servers · 0 skills -Type /help for more information - -Configuration opened... +  ⡠⣒⠄  ⡔⢄⠔⡄ + ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣ +  ⠉⠒⠣⠤⠵⠤⠬⠮⠆ + +Mistral Vibe v0.0.0 · devstral-latest[off] · [Subscription] Pro +1 model · 0 MCP servers · 0 skills +Type /help for more information + +Configuration opened... + - -┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -Settings - -Model: devstral-latest -Thinking: Off -Auto-copy: On -Autocomplete watcher (may delay first autocompletion): Off +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +Settings + +Model: devstral-latest +Thinking: Off +Auto-copy: On +Autocomplete watcher (may delay first autocompletion): Off +Confirm quit on Ctrl+D: On -↑↓/jk Navigate  Enter Select/Toggle  Esc Exit +↑↓/jk Navigate  Enter Select/Toggle  Esc Exit └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_navigate_down.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_navigate_down.svg index 29f67d5..9d8f474 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_navigate_down.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_navigate_down.svg @@ -40,6 +40,7 @@ .terminal-r6 { fill: #4b4e55;font-weight: bold } .terminal-r7 { fill: #98a84b;font-weight: bold } .terminal-r8 { fill: #868887 } +.terminal-r9 { fill: #6b753d;font-weight: bold } @@ -161,7 +162,7 @@ - + @@ -176,27 +177,27 @@ - -  ⡠⣒⠄  ⡔⢄⠔⡄ - ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣ -  ⠉⠒⠣⠤⠵⠤⠬⠮⠆ - -Mistral Vibe v0.0.0 · devstral-latest[off] · [Subscription] Pro -1 model · 0 MCP servers · 0 skills -Type /help for more information - -Configuration opened... +  ⡠⣒⠄  ⡔⢄⠔⡄ + ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣ +  ⠉⠒⠣⠤⠵⠤⠬⠮⠆ + +Mistral Vibe v0.0.0 · devstral-latest[off] · [Subscription] Pro +1 model · 0 MCP servers · 0 skills +Type /help for more information + +Configuration opened... + - -┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -Settings - -Model: devstral-latest -Thinking: Off -Auto-copy: On -Autocomplete watcher (may delay first autocompletion): Off +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +Settings + +Model: devstral-latest +Thinking: Off +Auto-copy: On +Autocomplete watcher (may delay first autocompletion): Off +Confirm quit on Ctrl+D: On -↑↓/jk Navigate  Enter Select/Toggle  Esc Exit +↑↓/jk Navigate  Enter Select/Toggle  Esc Exit └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_toggle_autocopy.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_toggle_autocopy.svg index ba331b4..3cc1d2b 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_toggle_autocopy.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_config_app/test_snapshot_config_toggle_autocopy.svg @@ -40,6 +40,8 @@ .terminal-r6 { fill: #4b4e55;font-weight: bold } .terminal-r7 { fill: #7b7e82;font-weight: bold } .terminal-r8 { fill: #868887 } +.terminal-r9 { fill: #98a84b;font-weight: bold } +.terminal-r10 { fill: #6b753d;font-weight: bold } @@ -161,7 +163,7 @@ - + @@ -176,27 +178,27 @@ - -  ⡠⣒⠄  ⡔⢄⠔⡄ - ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣ -  ⠉⠒⠣⠤⠵⠤⠬⠮⠆ - -Mistral Vibe v0.0.0 · devstral-latest[off] · [Subscription] Pro -1 model · 0 MCP servers · 0 skills -Type /help for more information - -Configuration opened... +  ⡠⣒⠄  ⡔⢄⠔⡄ + ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣ +  ⠉⠒⠣⠤⠵⠤⠬⠮⠆ + +Mistral Vibe v0.0.0 · devstral-latest[off] · [Subscription] Pro +1 model · 0 MCP servers · 0 skills +Type /help for more information + +Configuration opened... + - -┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -Settings - -Model: devstral-latest -Thinking: Off -Auto-copy: Off -Autocomplete watcher (may delay first autocompletion): Off +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +Settings + +Model: devstral-latest +Thinking: Off +Auto-copy: Off +Autocomplete watcher (may delay first autocompletion): Off +Confirm quit on Ctrl+D: On -↑↓/jk Navigate  Enter Select/Toggle  Esc Exit +↑↓/jk Navigate  Enter Select/Toggle  Esc Exit └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_after_resize.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_after_resize.svg index c90cb02..541a3e1 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_after_resize.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_after_resize.svg @@ -41,7 +41,8 @@ .terminal-r7 { fill: #292929 } .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #cc555a } -.terminal-r10 { fill: #868887 } +.terminal-r10 { fill: #6b753d;font-weight: bold } +.terminal-r11 { fill: #868887 } @@ -214,9 +215,9 @@   4. Deny -↑↓/jk navigate  Enter select  ESC reject +↑↓/jk navigate  Enter select  Esc reject └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_bottom_lines_hidden.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_bottom_lines_hidden.svg index 0d3b086..659800e 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_bottom_lines_hidden.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_long_content_bottom_lines_hidden.svg @@ -40,7 +40,8 @@ .terminal-r6 { fill: #d0b344 } .terminal-r7 { fill: #98a84b;font-weight: bold } .terminal-r8 { fill: #cc555a } -.terminal-r9 { fill: #868887 } +.terminal-r9 { fill: #6b753d;font-weight: bold } +.terminal-r10 { fill: #868887 } @@ -173,9 +174,9 @@   4. Deny -↑↓/jk navigate  Enter select  ESC reject +↑↓/jk navigate  Enter select  Esc reject └──────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_short_content.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_short_content.svg index 8c1fb9f..e31d017 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_short_content.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_diff_view_truncation/test_snapshot_write_approval_short_content.svg @@ -40,6 +40,7 @@ .terminal-r6 { fill: #d0b344 } .terminal-r7 { fill: #98a84b;font-weight: bold } .terminal-r8 { fill: #cc555a } +.terminal-r9 { fill: #6b753d;font-weight: bold } @@ -172,7 +173,7 @@   4. Deny -↑↓/jk navigate  Enter select  ESC reject +↑↓/jk navigate  Enter select  Esc reject └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff.svg index 57df59b..cbd27e8 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff.svg @@ -51,6 +51,7 @@ .terminal-r17 { fill: #b1b1b5 } .terminal-r18 { fill: #9ece6a;font-weight: bold } .terminal-r19 { fill: #f6768e } +.terminal-r20 { fill: #fe9e64;font-weight: bold } @@ -154,7 +155,7 @@ - +   ⡠⣒⠄  ⡔⢄⠔⡄ @@ -183,7 +184,7 @@   4. Deny -↑↓/jk navigate  Enter select  ESC reject +↑↓/jk navigate  Enter select  Esc reject └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_ansi.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_ansi.svg index 86f30ad..b4d7c1f 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_ansi.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_ansi.svg @@ -43,6 +43,7 @@ .terminal-r9 { fill: #d0b344 } .terminal-r10 { fill: #98a84b;font-weight: bold } .terminal-r11 { fill: #cc555a } +.terminal-r12 { fill: #6b753d;font-weight: bold } @@ -175,7 +176,7 @@   4. Deny -↑↓/jk navigate  Enter select  ESC reject +↑↓/jk navigate  Enter select  Esc reject └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_replace_all.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_replace_all.svg index 8af4c19..fb29537 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_replace_all.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_edit_diff/test_snapshot_edit_approval_diff_replace_all.svg @@ -50,6 +50,7 @@ .terminal-r16 { fill: #bede9c } .terminal-r17 { fill: #9ece6a;font-weight: bold } .terminal-r18 { fill: #f6768e } +.terminal-r19 { fill: #fe9e64;font-weight: bold } @@ -153,7 +154,7 @@ - +   ⠉⠒⠣⠤⠵⠤⠬⠮⠆ @@ -182,7 +183,7 @@   4. Deny -↑↓/jk navigate  Enter select  ESC reject +↑↓/jk navigate  Enter select  Esc reject └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_back_to_mcp.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_back_to_mcp.svg index 71e6b15..9407ce4 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_back_to_mcp.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_back_to_mcp.svg @@ -41,6 +41,7 @@ .terminal-r7 { fill: #7b7e82;font-weight: bold } .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } +.terminal-r10 { fill: #6b753d;font-weight: bold } @@ -197,7 +198,7 @@   alpha  [connector]  1 tool   connected   beta   [connector]  no tools needs auth   zeta   [connector]  no tools needs setup -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_opens_on_disconnected.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_opens_on_disconnected.svg index f52310f..aa64502 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_opens_on_disconnected.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_opens_on_disconnected.svg @@ -37,7 +37,9 @@ .terminal-r3 { fill: #68a0b3 } .terminal-r4 { fill: #608ab1;font-weight: bold } .terminal-r5 { fill: #4b4e55;font-weight: bold } -.terminal-r6 { fill: #868887 } +.terminal-r6 { fill: #98a84b;font-weight: bold } +.terminal-r7 { fill: #6b753d;font-weight: bold } +.terminal-r8 { fill: #868887 } @@ -159,7 +161,7 @@ - + @@ -188,15 +190,15 @@ This connector requires authentication -  Press enter to open auth in your browser +  Press Enter to open auth in your browser   Copy URL to clipboard   Manually show the URL -Once authenticated, press R to refresh +Once authenticated, press r to refresh -Backspace Back +Backspace Back └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_show_url.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_show_url.svg index 385b905..50a14e9 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_show_url.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_connector_auth_show_url.svg @@ -36,8 +36,10 @@ .terminal-r2 { fill: #ff8205;font-weight: bold } .terminal-r3 { fill: #68a0b3 } .terminal-r4 { fill: #608ab1;font-weight: bold } -.terminal-r5 { fill: #4b4e55;font-weight: bold } -.terminal-r6 { fill: #868887 } +.terminal-r5 { fill: #98a84b;font-weight: bold } +.terminal-r6 { fill: #4b4e55;font-weight: bold } +.terminal-r7 { fill: #6b753d;font-weight: bold } +.terminal-r8 { fill: #868887 } @@ -186,17 +188,17 @@ This connector requires authentication -  Press enter to open auth in your browser +  Press Enter to open auth in your browser   Copy URL to clipboard -  Manually show the URL +  Manually show the URL https://fake-auth.example.com/beta -Once authenticated, press R to refresh +Once authenticated, press r to refresh -Backspace Back +Backspace Back └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_backspace_returns_to_overview.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_backspace_returns_to_overview.svg index c0d1f13..b92f00f 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_backspace_returns_to_overview.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_backspace_returns_to_overview.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } .terminal-r10 { fill: #98a84b } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@ Local MCP Servers   filesystem  [stdio]  1 tool enabled   search      [http]   1 tool enabled -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_broken_server.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_broken_server.svg index 45c337d..52323d1 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_broken_server.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_broken_server.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } .terminal-r10 { fill: #98a84b } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@   filesystem     [stdio]  1 tool   enabled   broken-server  [stdio]  no tools enabled   search         [http]   1 tool   enabled -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connector_back_to_overview.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connector_back_to_overview.svg index 60bfd3b..502a45d 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connector_back_to_overview.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connector_back_to_overview.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } .terminal-r10 { fill: #98a84b } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@ Workspace Connectors   gmail  [connector]  3 tools connected   slack  [connector]  2 tools connected -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_only.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_only.svg index 39ad81a..4a6f92c 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_only.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_only.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } .terminal-r10 { fill: #98a84b } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@ Workspace Connectors   gmail  [connector]  3 tools connected   slack  [connector]  2 tools connected -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_sorted_by_status.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_sorted_by_status.svg index a77c050..c8c93a9 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_sorted_by_status.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_connectors_sorted_by_status.svg @@ -41,6 +41,7 @@ .terminal-r7 { fill: #7b7e82;font-weight: bold } .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } +.terminal-r10 { fill: #6b753d;font-weight: bold } @@ -197,7 +198,7 @@   alpha  [connector]  1 tool   connected   beta   [connector]  no tools needs auth   zeta   [connector]  no tools needs setup -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_drill_into_connector.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_drill_into_connector.svg index 541ed68..9df32f8 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_drill_into_connector.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_drill_into_connector.svg @@ -38,7 +38,8 @@ .terminal-r4 { fill: #608ab1;font-weight: bold } .terminal-r5 { fill: #4b4e55;font-weight: bold } .terminal-r6 { fill: #c5c8c6;font-weight: bold } -.terminal-r7 { fill: #868887 } +.terminal-r7 { fill: #6b753d;font-weight: bold } +.terminal-r8 { fill: #868887 } @@ -195,9 +196,9 @@ search_messages  -  Search Slack messages send_message  -  Send a Slack message -↑↓/jk Navigate  D Disable  E Enable  Backspace Back  R Refresh  Esc Close +↑↓/jk Navigate  d Disable  e Enable  Backspace Back  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_enter_drills_into_server.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_enter_drills_into_server.svg index 9cb0eb1..53e2b00 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_enter_drills_into_server.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_enter_drills_into_server.svg @@ -37,7 +37,8 @@ .terminal-r3 { fill: #68a0b3 } .terminal-r4 { fill: #608ab1;font-weight: bold } .terminal-r5 { fill: #4b4e55;font-weight: bold } -.terminal-r6 { fill: #868887 } +.terminal-r6 { fill: #6b753d;font-weight: bold } +.terminal-r7 { fill: #868887 } @@ -194,9 +195,9 @@ MCP Server: filesystem fake_tool  -  A fake tool for filesystem -↑↓/jk Navigate  D Disable  E Enable  Backspace Back  R Refresh  Esc Close +↑↓/jk Navigate  d Disable  e Enable  Backspace Back  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_help_bar_shows_connect.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_help_bar_shows_connect.svg index 88439f4..7a57ed5 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_help_bar_shows_connect.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_help_bar_shows_connect.svg @@ -41,6 +41,7 @@ .terminal-r7 { fill: #98a84b } .terminal-r8 { fill: #4b4e55;font-weight: bold } .terminal-r9 { fill: #7b7e82;font-weight: bold } +.terminal-r10 { fill: #6b753d;font-weight: bold } @@ -197,7 +198,7 @@   alpha  [connector]  1 tool   connected   beta   [connector]  no tools needs auth   zeta   [connector]  no tools needs setup -↑↓/jk Navigate  Enter Connect  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Connect  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview.svg index 74b3d20..c855d88 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } .terminal-r10 { fill: #98a84b } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@ Local MCP Servers   filesystem  [stdio]  1 tool enabled   search      [http]   1 tool enabled -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview_navigate_down.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview_navigate_down.svg index f34f3cb..7d3885b 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview_navigate_down.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_overview_navigate_down.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #4b4e55;font-weight: bold } .terminal-r9 { fill: #7b7e82;font-weight: bold } .terminal-r10 { fill: #98a84b;font-weight: bold } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@ Local MCP Servers   filesystem  [stdio]  1 tool enabled   search      [http]   1 tool enabled -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_refresh_shortcut.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_refresh_shortcut.svg index 554c6b5..0037baa 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_refresh_shortcut.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_refresh_shortcut.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } .terminal-r10 { fill: #98a84b } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@ Local MCP Servers   filesystem  [stdio]  1 tool enabled   search      [http]   1 tool enabled -Refreshed.  ↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +Refreshed.  ↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_server_arg.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_server_arg.svg index 504ebb1..3740991 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_server_arg.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_server_arg.svg @@ -37,7 +37,8 @@ .terminal-r3 { fill: #68a0b3 } .terminal-r4 { fill: #608ab1;font-weight: bold } .terminal-r5 { fill: #4b4e55;font-weight: bold } -.terminal-r6 { fill: #868887 } +.terminal-r6 { fill: #6b753d;font-weight: bold } +.terminal-r7 { fill: #868887 } @@ -194,9 +195,9 @@ MCP Server: filesystem fake_tool  -  A fake tool for filesystem -↑↓/jk Navigate  D Disable  E Enable  Backspace Back  R Refresh  Esc Close +↑↓/jk Navigate  d Disable  e Enable  Backspace Back  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_with_connectors_overview.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_with_connectors_overview.svg index 60bfd3b..502a45d 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_with_connectors_overview.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_mcp_command/test_snapshot_mcp_with_connectors_overview.svg @@ -42,6 +42,7 @@ .terminal-r8 { fill: #98a84b;font-weight: bold } .terminal-r9 { fill: #868887 } .terminal-r10 { fill: #98a84b } +.terminal-r11 { fill: #6b753d;font-weight: bold } @@ -198,7 +199,7 @@ Workspace Connectors   gmail  [connector]  3 tools connected   slack  [connector]  2 tools connected -↑↓/jk Navigate  Enter Show tools  D Disable  E Enable  R Refresh  Esc Close +↑↓/jk Navigate  Enter Show tools  d Disable  e Enable  r Refresh  Esc Close └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_initial.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_initial.svg index ca0ea8b..f2a7b1a 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_initial.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_initial.svg @@ -38,7 +38,8 @@ .terminal-r4 { fill: #608ab1;font-weight: bold } .terminal-r5 { fill: #98a84b;font-weight: bold } .terminal-r6 { fill: #4b4e55;font-weight: bold } -.terminal-r7 { fill: #868887 } +.terminal-r7 { fill: #6b753d;font-weight: bold } +.terminal-r8 { fill: #868887 } @@ -195,9 +196,9 @@   mistral-small   local -↑↓/jk Navigate  Enter Select  Esc Cancel +↑↓/jk Navigate  Enter Select  Esc Cancel └──────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_navigate_down.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_navigate_down.svg index 1efcfe7..e81039f 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_navigate_down.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_model_picker/test_snapshot_model_picker_navigate_down.svg @@ -39,7 +39,8 @@ .terminal-r5 { fill: #98a84b } .terminal-r6 { fill: #c5c8c6;font-weight: bold } .terminal-r7 { fill: #4b4e55;font-weight: bold } -.terminal-r8 { fill: #868887 } +.terminal-r8 { fill: #6b753d;font-weight: bold } +.terminal-r9 { fill: #868887 } @@ -196,9 +197,9 @@   mistral-small   local -↑↓/jk Navigate  Enter Select  Esc Cancel +↑↓/jk Navigate  Enter Select  Esc Cancel └──────────────────────────────────────────────────────────────────────────────────────────────────┘ -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_speaking.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_speaking.svg index 57e2f30..23e30e6 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_speaking.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_speaking.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #c5c8c6;font-weight: bold } .terminal-r5 { fill: #868887 } .terminal-r6 { fill: #ffa500;font-weight: bold } +.terminal-r7 { fill: #6b753d;font-weight: bold } @@ -190,7 +191,7 @@ Hello! I can help you. -▂▅▇ speaking Esc/Ctrl+C to stop +▂▅▇ speaking Esc/Ctrl+C to stop ────────────────────────────────────────────────────────────────────────────────────────────────────────────── default ─ > diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_summarizing.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_summarizing.svg index c8489c6..873689f 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_summarizing.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_narrator_flow/test_snapshot_narrator_summarizing.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #c5c8c6;font-weight: bold } .terminal-r5 { fill: #868887 } .terminal-r6 { fill: #ffa500;font-weight: bold } +.terminal-r7 { fill: #6b753d;font-weight: bold } @@ -190,7 +191,7 @@ Hello! I can help you. - summarizing Esc/Ctrl+C to stop + summarizing Esc/Ctrl+C to stop ────────────────────────────────────────────────────────────────────────────────────────────────────────────── default ─ > diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_api_key/test_snapshot_onboarding_api_key_with_valid_input.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_api_key/test_snapshot_onboarding_api_key_with_valid_input.svg index 17e78cb..cc50471 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_api_key/test_snapshot_onboarding_api_key_with_valid_input.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_api_key/test_snapshot_onboarding_api_key_with_valid_input.svg @@ -39,6 +39,7 @@ .terminal-r5 { fill: #98a84b } .terminal-r6 { fill: #4b4e55 } .terminal-r7 { fill: #868887 } +.terminal-r8 { fill: #6b753d;font-weight: bold } @@ -142,7 +143,7 @@ ••••••••••••••• └────────────────────────────────────────────────────────────────────────┘ -Press Enter to submit ↵ +Press Enter to submit ↵ Learn more about Vibe configurations https://github.com/mistralai/mistral-vibe?tab=readme-ov-file#configuration diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-dark].svg b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-dark].svg index 560dd3a..27fd564 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-dark].svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-dark].svg @@ -34,12 +34,13 @@ .terminal-r1 { fill: #c5c8c6 } .terminal-r2 { fill: #868887 } -.terminal-r3 { fill: #c5c8c6;font-weight: bold } -.terminal-r4 { fill: #292929 } -.terminal-r5 { fill: #608ab1 } -.terminal-r6 { fill: #c5c8c6;font-style: italic; } -.terminal-r7 { fill: #d0b344 } -.terminal-r8 { fill: #4b4e55 } +.terminal-r3 { fill: #98a84b;font-weight: bold } +.terminal-r4 { fill: #c5c8c6;font-weight: bold } +.terminal-r5 { fill: #292929 } +.terminal-r6 { fill: #608ab1 } +.terminal-r7 { fill: #c5c8c6;font-style: italic; } +.terminal-r8 { fill: #d0b344 } +.terminal-r9 { fill: #4b4e55 } @@ -153,7 +154,7 @@ solarized-light textual-light ╭────────────────────────────╮ -Navigate ↑ ↓ ansi-dark Press Enter ↵ +Navigate ↑↓ ansi-dark Press Enter ↵ ╰────────────────────────────╯ atom-one-dark catppuccin-frappe @@ -162,15 +163,15 @@ ╭───────────────────────────── Preview ──────────────────────────────╮ -Heading +Heading -Bolditalic, and inline code. -▄▄ -• Bullet point -• Another bullet point +Bolditalic, and inline code. +▄▄ +• Bullet point +• Another bullet point - 1. First item - 2. Second item + 1. First item + 2. Second item ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-light].svg b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-light].svg index b6cb2fd..83d4786 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-light].svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[ansi-light].svg @@ -34,12 +34,13 @@ .terminal-r1 { fill: #c5c8c6 } .terminal-r2 { fill: #868887 } -.terminal-r3 { fill: #c5c8c6;font-weight: bold } -.terminal-r4 { fill: #292929 } -.terminal-r5 { fill: #608ab1 } -.terminal-r6 { fill: #c5c8c6;font-style: italic; } -.terminal-r7 { fill: #cc555a } -.terminal-r8 { fill: #68a0b3 } +.terminal-r3 { fill: #98729f;font-weight: bold } +.terminal-r4 { fill: #c5c8c6;font-weight: bold } +.terminal-r5 { fill: #292929 } +.terminal-r6 { fill: #608ab1 } +.terminal-r7 { fill: #c5c8c6;font-style: italic; } +.terminal-r8 { fill: #cc555a } +.terminal-r9 { fill: #68a0b3 } @@ -153,7 +154,7 @@ solarized-light textual-light ╭────────────────────────────╮ -Navigate ↑ ↓ ansi-dark Press Enter ↵ +Navigate ↑↓ ansi-dark Press Enter ↵ ╰────────────────────────────╯ atom-one-dark catppuccin-frappe @@ -162,15 +163,15 @@ ╭───────────────────────────── Preview ──────────────────────────────╮ -Heading +Heading -Bolditalic, and inline code. +Bolditalic, and inline code. ▄▄ -• Bullet point -• Another bullet point +• Bullet point +• Another bullet point - 1. First item - 2. Second item + 1. First item + 2. Second item ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[dracula].svg b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[dracula].svg index 0db5576..2809eed 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[dracula].svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_onboarding_theme/test_snapshot_onboarding_theme_selection[dracula].svg @@ -39,14 +39,15 @@ .terminal-r5 { fill: #5c5d65 } .terminal-r6 { fill: #909194 } .terminal-r7 { fill: #a4a5a6 } -.terminal-r8 { fill: #e3e3e4;font-weight: bold } -.terminal-r9 { fill: #282a36 } -.terminal-r10 { fill: #bd93f9;font-weight: bold } -.terminal-r11 { fill: #f8f8f2;font-weight: bold } -.terminal-r12 { fill: #f8f8f2;font-style: italic; } -.terminal-r13 { fill: #f5c898 } -.terminal-r14 { fill: #181a25 } -.terminal-r15 { fill: #d3b7fb } +.terminal-r8 { fill: #ff79c6;font-weight: bold } +.terminal-r9 { fill: #e3e3e4;font-weight: bold } +.terminal-r10 { fill: #282a36 } +.terminal-r11 { fill: #bd93f9;font-weight: bold } +.terminal-r12 { fill: #f8f8f2;font-weight: bold } +.terminal-r13 { fill: #f8f8f2;font-style: italic; } +.terminal-r14 { fill: #f5c898 } +.terminal-r15 { fill: #181a25 } +.terminal-r16 { fill: #d3b7fb } @@ -150,7 +151,7 @@ - + Select your preferred theme @@ -160,7 +161,7 @@ solarized-light textual-light ╭────────────────────────────╮ -Navigate ↑ ↓ ansi-dark Press Enter ↵ +Navigate ↑↓ ansi-dark Press Enter ↵ ╰────────────────────────────╯ atom-one-dark catppuccin-frappe @@ -169,15 +170,15 @@ ╭───────────────────────────── Preview ──────────────────────────────╮ -Heading +Heading -Bolditalic, and inline code. -▄▄ -• Bullet point -• Another bullet point +Bolditalic, and inline code. +▄▄ +• Bullet point +• Another bullet point - 1. First item - 2. Second item + 1. First item + 2. Second item ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg index f504f8a..d3d26c6 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_empty.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #608ab1;font-weight: bold } .terminal-r5 { fill: #7b7e82 } .terminal-r6 { fill: #868887 } +.terminal-r7 { fill: #6b753d;font-weight: bold } @@ -194,7 +195,7 @@ Path to custom SSL certificate file SSL_CERT_DIR Path to directory containing SSL certificates -↑↓ navigate  Enter save & exit  ESC cancel +↑↓ navigate  Enter save & exit  Esc cancel └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg index 1ac5d20..5da5429 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_proxy_setup/test_snapshot_proxy_setup_initial_with_values.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #608ab1;font-weight: bold } .terminal-r5 { fill: #4b4e55 } .terminal-r6 { fill: #868887 } +.terminal-r7 { fill: #6b753d;font-weight: bold } @@ -194,7 +195,7 @@ Path to custom SSL certificate file SSL_CERT_DIR Path to directory containing SSL certificates -↑↓ navigate  Enter save & exit  ESC cancel +↑↓ navigate  Enter save & exit  Esc cancel └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg index ba6bd55..252a733 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_answer_first_advance.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #0178d4;font-weight: bold } .terminal-r5 { fill: #e0e0e0 } .terminal-r6 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } @@ -111,7 +112,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐  DB ✓   [Framework] @@ -122,7 +123,7 @@   2. Django   3. Type your answer... -←→ questions  ↑↓/jk navigate  Enter select  Esc cancel +←→ questions  ↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg index ba6bd55..252a733 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_first_answered_checkmark.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #0178d4;font-weight: bold } .terminal-r5 { fill: #e0e0e0 } .terminal-r6 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } @@ -111,7 +112,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐  DB ✓   [Framework] @@ -122,7 +123,7 @@   2. Django   3. Type your answer... -←→ questions  ↑↓/jk navigate  Enter select  Esc cancel +←→ questions  ↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg index 59723ec..543dce5 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_initial.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #0178d4;font-weight: bold } .terminal-r5 { fill: #e0e0e0 } .terminal-r6 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } @@ -111,7 +112,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ [DB]   Framework  @@ -122,7 +123,7 @@   2. MongoDB   3. Type your answer... -←→ questions  ↑↓/jk navigate  Enter select  Esc cancel +←→ questions  ↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg index ae75c4b..75de6b9 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_left_wraps.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #0178d4;font-weight: bold } .terminal-r5 { fill: #e0e0e0 } .terminal-r6 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } @@ -111,7 +112,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐  DB   [Framework] @@ -122,7 +123,7 @@   2. Django   3. Type your answer... -←→ questions  ↑↓/jk navigate  Enter select  Esc cancel +←→ questions  ↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg index ae75c4b..75de6b9 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_navigate_right.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #0178d4;font-weight: bold } .terminal-r5 { fill: #e0e0e0 } .terminal-r6 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } @@ -111,7 +112,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐  DB   [Framework] @@ -122,7 +123,7 @@   2. Django   3. Type your answer... -←→ questions  ↑↓/jk navigate  Enter select  Esc cancel +←→ questions  ↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg index 59723ec..543dce5 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_question_tab_to_second.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #0178d4;font-weight: bold } .terminal-r5 { fill: #e0e0e0 } .terminal-r6 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } @@ -111,7 +112,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ [DB]   Framework  @@ -122,7 +123,7 @@   2. MongoDB   3. Type your answer... -←→ questions  ↑↓/jk navigate  Enter select  Esc cancel +←→ questions  ↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg index a9db7cc..9e823be 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_initial.svg @@ -37,6 +37,7 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } @@ -110,7 +111,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which features do you want to enable? @@ -122,7 +123,7 @@      Submit → -↑↓/jk navigate  Enter toggle  Esc cancel +↑↓/jk navigate  Enter toggle  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg index 7747fe6..e13a343 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_mixed_selection.svg @@ -38,7 +38,8 @@ .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #e0e0e0;font-weight: bold } .terminal-r6 { fill: #121212 } -.terminal-r7 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } +.terminal-r8 { fill: #a0a0a0 } @@ -112,7 +113,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which features do you want to enable? @@ -124,7 +125,7 @@      Submit → -↑↓/jk navigate  Enter toggle  Esc cancel +↑↓/jk navigate  Enter toggle  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg index 6e37cdf..5ce835f 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_navigate_to_submit.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } .terminal-r6 { fill: #e0e0e0;font-weight: bold } +.terminal-r7 { fill: #fea62b;font-weight: bold } @@ -111,7 +112,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which features do you want to enable? @@ -123,7 +124,7 @@ ›    Submit → -↑↓/jk navigate  Enter toggle  Esc cancel +↑↓/jk navigate  Enter toggle  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg index aa15886..775f90b 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_other_with_text.svg @@ -38,7 +38,8 @@ .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #e0e0e0;font-weight: bold } .terminal-r6 { fill: #121212 } -.terminal-r7 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } +.terminal-r8 { fill: #a0a0a0 } @@ -112,7 +113,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which features do you want to enable? @@ -124,7 +125,7 @@      Submit → -↑↓/jk navigate  Enter toggle  Esc cancel +↑↓/jk navigate  Enter toggle  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg index 5f7abc0..b184521 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_first.svg @@ -37,6 +37,7 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } @@ -110,7 +111,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which features do you want to enable? @@ -122,7 +123,7 @@      Submit → -↑↓/jk navigate  Enter toggle  Esc cancel +↑↓/jk navigate  Enter toggle  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg index 4f9ac9f..d8a9e7d 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_toggle_multiple.svg @@ -37,6 +37,7 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } @@ -110,7 +111,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which features do you want to enable? @@ -122,7 +123,7 @@      Submit → -↑↓/jk navigate  Enter toggle  Esc cancel +↑↓/jk navigate  Enter toggle  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg index a9db7cc..9e823be 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_multi_select_untoggle.svg @@ -37,6 +37,7 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } @@ -110,7 +111,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which features do you want to enable? @@ -122,7 +123,7 @@      Submit → -↑↓/jk navigate  Enter toggle  Esc cancel +↑↓/jk navigate  Enter toggle  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg index 4e54af2..0f4b588 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_initial.svg @@ -37,6 +37,7 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } @@ -110,7 +111,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which database should we use for this project? @@ -120,7 +121,7 @@   3. Redis - In-memory store   4. Type your answer... -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg index af29bdd..47bc467 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_down.svg @@ -37,6 +37,7 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } @@ -110,7 +111,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which database should we use for this project? @@ -120,7 +121,7 @@   3. Redis - In-memory store   4. Type your answer... -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg index f32582e..f74212e 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_other.svg @@ -39,7 +39,8 @@ .terminal-r5 { fill: #e0e0e0;font-weight: bold } .terminal-r6 { fill: #121212 } .terminal-r7 { fill: #6c6c6c } -.terminal-r8 { fill: #a0a0a0 } +.terminal-r8 { fill: #fea62b;font-weight: bold } +.terminal-r9 { fill: #a0a0a0 } @@ -113,7 +114,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which database should we use for this project? @@ -123,7 +124,7 @@   3. Redis - In-memory store › 4. Type your answer... -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg index a18c477..da70223 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_to_third_option.svg @@ -37,6 +37,7 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } @@ -110,7 +111,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which database should we use for this project? @@ -120,7 +121,7 @@ › 3. Redis - In-memory store   4. Type your answer... -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg index f32582e..f74212e 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_navigate_up_wraps.svg @@ -39,7 +39,8 @@ .terminal-r5 { fill: #e0e0e0;font-weight: bold } .terminal-r6 { fill: #121212 } .terminal-r7 { fill: #6c6c6c } -.terminal-r8 { fill: #a0a0a0 } +.terminal-r8 { fill: #fea62b;font-weight: bold } +.terminal-r9 { fill: #a0a0a0 } @@ -113,7 +114,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which database should we use for this project? @@ -123,7 +124,7 @@   3. Redis - In-memory store › 4. Type your answer... -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg index 4103d83..4043862 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_question_app/test_snapshot_question_app_other_typing.svg @@ -38,7 +38,8 @@ .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #e0e0e0;font-weight: bold } .terminal-r6 { fill: #121212 } -.terminal-r7 { fill: #a0a0a0 } +.terminal-r7 { fill: #fea62b;font-weight: bold } +.terminal-r8 { fill: #a0a0a0 } @@ -112,7 +113,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ Which database should we use for this project? @@ -122,7 +123,7 @@   3. Redis - In-memory store › 4. SQLite -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_error_shows_toast.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_error_shows_toast.svg index bcebe68..77c1d83 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_error_shows_toast.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_error_shows_toast.svg @@ -40,6 +40,7 @@ .terminal-r6 { fill: #292929;font-weight: bold } .terminal-r7 { fill: #cc555a } .terminal-r8 { fill: #608ab1;font-weight: bold } +.terminal-r9 { fill: #6b753d;font-weight: bold } @@ -196,7 +197,7 @@ › 1. Edit & restore files to this point   2. Edit without restoring files -Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  ESC cancel +Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  Esc cancel └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir15/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_down.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_down.svg index 178d95b..2c1642e 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_down.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_down.svg @@ -39,6 +39,7 @@ .terminal-r5 { fill: #868887 } .terminal-r6 { fill: #292929;font-weight: bold } .terminal-r7 { fill: #608ab1;font-weight: bold } +.terminal-r8 { fill: #6b753d;font-weight: bold } @@ -195,7 +196,7 @@ › 1. Edit & restore files to this point   2. Edit without restoring files -Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  ESC cancel +Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  Esc cancel └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir15/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_up.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_up.svg index f5d97f3..924e932 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_up.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_navigate_up.svg @@ -39,6 +39,7 @@ .terminal-r5 { fill: #868887 } .terminal-r6 { fill: #292929;font-weight: bold } .terminal-r7 { fill: #608ab1;font-weight: bold } +.terminal-r8 { fill: #6b753d;font-weight: bold } @@ -195,7 +196,7 @@ › 1. Edit & restore files to this point   2. Edit without restoring files -Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  ESC cancel +Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  Esc cancel └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir15/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_panel_shown.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_panel_shown.svg index 178d95b..2c1642e 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_panel_shown.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_rewind/test_snapshot_rewind_panel_shown.svg @@ -39,6 +39,7 @@ .terminal-r5 { fill: #868887 } .terminal-r6 { fill: #292929;font-weight: bold } .terminal-r7 { fill: #608ab1;font-weight: bold } +.terminal-r8 { fill: #6b753d;font-weight: bold } @@ -195,7 +196,7 @@ › 1. Edit & restore files to this point   2. Edit without restoring files -Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  ESC cancel +Alt+↑↓ or Ctrl+P/N browse messages  ↑↓/jk pick option  Enter confirm  Esc cancel └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir15/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_session_picker/test_snapshot_session_picker_header.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_session_picker/test_snapshot_session_picker_header.svg index 8943e2e..4288fb4 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_session_picker/test_snapshot_session_picker_header.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_session_picker/test_snapshot_session_picker_header.svg @@ -38,6 +38,7 @@ .terminal-r4 { fill: #868887 } .terminal-r5 { fill: #7b7e82;font-weight: bold } .terminal-r6 { fill: #4b4e55;font-weight: bold } +.terminal-r7 { fill: #6b753d;font-weight: bold } @@ -194,7 +195,7 @@ unknown   local-se  Refactor the auth module unknown   local-se  Add unit tests for the API -↑↓/jk Navigate  Enter Select  D Delete  Esc Cancel +↑↓/jk Navigate  Enter Select  d Delete  Esc Cancel └──────────────────────────────────────────────────────────────────────────────────────────────────┘ /test/workdir0/200k tokens (0%) diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg index 093ca97..0d6e4a5 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_cancel_selected.svg @@ -36,7 +36,8 @@ .terminal-r2 { fill: #c5c8c6 } .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } -.terminal-r5 { fill: #a0a0a0 } +.terminal-r5 { fill: #fea62b;font-weight: bold } +.terminal-r6 { fill: #a0a0a0 } @@ -86,7 +87,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ You have 5 unpushed commits. Push to continue? @@ -94,7 +95,7 @@   1. Push and continue › 2. Cancel -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg index 0fccbcd..119f292 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_multiple_commits.svg @@ -36,7 +36,8 @@ .terminal-r2 { fill: #c5c8c6 } .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } -.terminal-r5 { fill: #a0a0a0 } +.terminal-r5 { fill: #fea62b;font-weight: bold } +.terminal-r6 { fill: #a0a0a0 } @@ -86,7 +87,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ You have 5 unpushed commits. Push to continue? @@ -94,7 +95,7 @@ › 1. Push and continue   2. Cancel -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg index b95c65e..4d3509f 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_teleport/test_snapshot_teleport_push_confirmation_single_commit.svg @@ -36,7 +36,8 @@ .terminal-r2 { fill: #c5c8c6 } .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } -.terminal-r5 { fill: #a0a0a0 } +.terminal-r5 { fill: #fea62b;font-weight: bold } +.terminal-r6 { fill: #a0a0a0 } @@ -86,7 +87,7 @@ - + ┌──────────────────────────────────────────────────────────────────────────────┐ You have 1 unpushed commit. Push to continue? @@ -94,7 +95,7 @@ › 1. Push and continue   2. Cancel -↑↓/jk navigate  Enter select  Esc cancel +↑↓/jk navigate  Enter select  Esc cancel └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog.svg index 0519c37..d7aeef8 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog.svg @@ -38,7 +38,8 @@ .terminal-r4 { fill: #608ab1 } .terminal-r5 { fill: #868887;font-weight: bold } .terminal-r6 { fill: #c5c8c6;font-weight: bold } -.terminal-r7 { fill: #868887 } +.terminal-r7 { fill: #6b753d;font-weight: bold } +.terminal-r8 { fill: #868887 } @@ -201,10 +202,10 @@   Trust folder› Don't trust -← → navigate  Enter select +←→ navigate  Enter select -Setting will be saved in: -/home/user/.vibe/trusted_folders.toml +Setting will be saved in: +/home/user/.vibe/trusted_folders.toml ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_many_files.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_many_files.svg index 6347d37..88f15cf 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_many_files.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_many_files.svg @@ -39,7 +39,8 @@ .terminal-r5 { fill: #292929 } .terminal-r6 { fill: #868887;font-weight: bold } .terminal-r7 { fill: #c5c8c6;font-weight: bold } -.terminal-r8 { fill: #868887 } +.terminal-r8 { fill: #6b753d;font-weight: bold } +.terminal-r9 { fill: #868887 } @@ -203,10 +204,10 @@   Trust folder› Don't trust -← → navigate  Enter select +←→ navigate  Enter select -Setting will be saved in: -/home/user/.vibe/trusted_folders.toml +Setting will be saved in: +/home/user/.vibe/trusted_folders.toml ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_small_terminal.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_small_terminal.svg index 8f040bf..e313d0c 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_small_terminal.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_small_terminal.svg @@ -39,7 +39,8 @@ .terminal-r5 { fill: #608ab1 } .terminal-r6 { fill: #868887;font-weight: bold } .terminal-r7 { fill: #c5c8c6;font-weight: bold } -.terminal-r8 { fill: #868887 } +.terminal-r8 { fill: #6b753d;font-weight: bold } +.terminal-r9 { fill: #868887 } @@ -147,7 +148,7 @@   Trust folder› Don't trust -← → navigate  Enter select +←→ navigate  Enter select ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_untrusted_repo.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_untrusted_repo.svg index 6ee4197..ae6ed64 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_untrusted_repo.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_untrusted_repo.svg @@ -39,7 +39,8 @@ .terminal-r5 { fill: #d0b344;font-style: italic; } .terminal-r6 { fill: #868887;font-weight: bold } .terminal-r7 { fill: #c5c8c6;font-weight: bold } -.terminal-r8 { fill: #868887 } +.terminal-r8 { fill: #6b753d;font-weight: bold } +.terminal-r9 { fill: #868887 } @@ -202,10 +203,10 @@   Trust folder› Don't trust -← → navigate  Enter select +←→ navigate  Enter select -Setting will be saved in: -/home/user/.vibe/trusted_folders.toml +Setting will be saved in: +/home/user/.vibe/trusted_folders.toml ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_with_repo.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_with_repo.svg index 5e65cd7..a6fe375 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_with_repo.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_trust_folder_dialog/test_snapshot_trust_folder_dialog_with_repo.svg @@ -40,7 +40,8 @@ .terminal-r6 { fill: #292929 } .terminal-r7 { fill: #868887;font-weight: bold } .terminal-r8 { fill: #c5c8c6;font-weight: bold } -.terminal-r9 { fill: #868887 } +.terminal-r9 { fill: #6b753d;font-weight: bold } +.terminal-r10 { fill: #868887 } @@ -204,10 +205,10 @@   Trust full repo  Trust folder› Don't trust -← → navigate  Enter select +←→ navigate  Enter select -Setting will be saved in: -/home/user/.vibe/trusted_folders.toml +Setting will be saved in: +/home/user/.vibe/trusted_folders.toml ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_continue_selected.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_continue_selected.svg index 762501d..a41c5d3 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_continue_selected.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_continue_selected.svg @@ -37,7 +37,8 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #e0e0e0;font-weight: bold } -.terminal-r6 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } +.terminal-r7 { fill: #a0a0a0 } @@ -111,7 +112,7 @@ - + @@ -125,7 +126,7 @@   Update now› Continue with current version -← → navigate  Enter select +←→ navigate  Enter select ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_initial.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_initial.svg index e1d7ab9..b1bd57b 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_initial.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_update_prompt/test_snapshot_update_prompt_initial.svg @@ -37,7 +37,8 @@ .terminal-r3 { fill: #0178d4;font-weight: bold } .terminal-r4 { fill: #e0e0e0 } .terminal-r5 { fill: #e0e0e0;font-weight: bold } -.terminal-r6 { fill: #a0a0a0 } +.terminal-r6 { fill: #fea62b;font-weight: bold } +.terminal-r7 { fill: #a0a0a0 } @@ -111,7 +112,7 @@ - + @@ -125,7 +126,7 @@ › Update now  Continue with current version -← → navigate  Enter select +←→ navigate  Enter select ╰────────────────────────────────────────────────────────────────────╯ diff --git a/tests/snapshots/__snapshots__/test_ui_snapshot_voice_mode/test_snapshot_voice_enable.svg b/tests/snapshots/__snapshots__/test_ui_snapshot_voice_mode/test_snapshot_voice_enable.svg index c706c3b..f7ef935 100644 --- a/tests/snapshots/__snapshots__/test_ui_snapshot_voice_mode/test_snapshot_voice_enable.svg +++ b/tests/snapshots/__snapshots__/test_ui_snapshot_voice_mode/test_snapshot_voice_enable.svg @@ -35,7 +35,8 @@ .terminal-r1 { fill: #c5c8c6 } .terminal-r2 { fill: #ff8205;font-weight: bold } .terminal-r3 { fill: #68a0b3 } -.terminal-r4 { fill: #868887 } +.terminal-r4 { fill: #c5c8c6;font-weight: bold } +.terminal-r5 { fill: #868887 } @@ -186,7 +187,7 @@ / voice Voice settings opened... -Voice mode enabled. Press ctrl+r to start recording. +Voice mode enabled. Press Ctrl+R to start recording. ────────────────────────────────────────────────────────────────────────────────────────────────────────────── default ─ @@ -194,7 +195,7 @@ ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── -/test/workdir0/200k tokens (0%) +/test/workdir0/200k tokens (0%) diff --git a/tests/test_mcp_auth_notices.py b/tests/test_mcp_auth_notices.py index a973763..df6a466 100644 --- a/tests/test_mcp_auth_notices.py +++ b/tests/test_mcp_auth_notices.py @@ -16,7 +16,7 @@ from vibe.core.config import MCPOAuth, MCPStreamableHttp from vibe.core.tools.mcp import MCPRegistry -def _registry_with_uncached_oauth(alias: str) -> MCPRegistry: +def _registry_with_uncached_oauth(alias: str, *, disabled: bool = False) -> MCPRegistry: registry = MCPRegistry() registry.sync_active_servers([ MCPStreamableHttp( @@ -24,6 +24,7 @@ def _registry_with_uncached_oauth(alias: str) -> MCPRegistry: transport="streamable-http", url="https://mcp.example.com/mcp", auth=MCPOAuth(type="oauth", scopes=["read"]), + disabled=disabled, ) ]) assert registry.needs_auth == set() @@ -53,6 +54,49 @@ async def test_tui_mcp_auth_notice_uses_status_for_uncached_oauth() -> None: assert "sentry" in message._content +@pytest.mark.asyncio +async def test_tui_mcp_auth_notice_skips_disabled_servers() -> None: + mount = AsyncMock() + app = cast( + VibeApp, + SimpleNamespace( + agent_loop=SimpleNamespace( + mcp_registry=_registry_with_uncached_oauth("sentry", disabled=True) + ), + _mount_and_scroll=mount, + ), + ) + + await VibeApp._show_mcp_auth_required_notice(app) + + mount.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_acp_mcp_auth_notice_skips_disabled_servers() -> None: + agent = VibeAcpAgentLoop() + client = FakeClient() + agent.on_connect(client) + session = cast( + AcpSessionLoop, + SimpleNamespace( + id="session-id", + agent_loop=SimpleNamespace( + mcp_registry=_registry_with_uncached_oauth("sentry", disabled=True) + ), + ), + ) + + await agent._notify_mcp_auth_required(session) + + messages = [ + notification.update + for notification in client._session_updates + if isinstance(notification.update, AgentMessageChunk) + ] + assert messages == [] + + @pytest.mark.asyncio async def test_acp_mcp_auth_notice_uses_status_for_uncached_oauth() -> None: agent = VibeAcpAgentLoop() diff --git a/uv.lock b/uv.lock index d14d0b1..166d395 100644 --- a/uv.lock +++ b/uv.lock @@ -310,23 +310,23 @@ wheels = [ [[package]] name = "debugpy" -version = "1.8.20" +version = "1.8.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207, upload-time = "2026-01-29T23:03:28.199Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/aa/12037145b7a56eaa5b29b41872f7a21b538e807e13f32c4d3c46e59be084/debugpy-1.8.21.tar.gz", hash = "sha256:a3c53278e84c94e11bd87c53970ec391d1a67396c8b22609fcac576520e611a6", size = 1697577, upload-time = "2026-06-01T19:30:35.156Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", size = 2550686, upload-time = "2026-01-29T23:03:42.023Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", size = 4310588, upload-time = "2026-01-29T23:03:43.314Z" }, - { url = "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", size = 5331372, upload-time = "2026-01-29T23:03:45.526Z" }, - { url = "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", size = 5372835, upload-time = "2026-01-29T23:03:47.245Z" }, - { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560, upload-time = "2026-01-29T23:03:48.738Z" }, - { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272, upload-time = "2026-01-29T23:03:50.169Z" }, - { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208, upload-time = "2026-01-29T23:03:51.547Z" }, - { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930, upload-time = "2026-01-29T23:03:53.585Z" }, - { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066, upload-time = "2026-01-29T23:03:54.999Z" }, - { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425, upload-time = "2026-01-29T23:03:56.518Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407, upload-time = "2026-01-29T23:03:58.481Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521, upload-time = "2026-01-29T23:03:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658, upload-time = "2026-01-29T23:04:17.404Z" }, + { url = "https://files.pythonhosted.org/packages/a2/df/bf625547431a9cadc9f4cbfeda38866e2b17f6aed147b625377e87834449/debugpy-1.8.21-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:9f96713896f39c3dff0ee841f47320c3f2983d33c341e009361bb0ebc79adc4e", size = 2483609, upload-time = "2026-06-01T19:30:50.794Z" }, + { url = "https://files.pythonhosted.org/packages/bf/09/59324b903599031ff9faaec1758292409f6561a0ec2492fe4b703327705a/debugpy-1.8.21-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:c193d474f0a211191f2b4449d2d06157c689013035bd952f3b617e0ef422b176", size = 3968900, upload-time = "2026-06-01T19:30:52.341Z" }, + { url = "https://files.pythonhosted.org/packages/14/cd/27f65b805d7fe005c44e1a36b9183ecdfbcdbf9d3e721a5115d461ecc7ee/debugpy-1.8.21-cp312-cp312-win32.whl", hash = "sha256:4743373c1cac7f9e74a1b9915bf1dbe0e900eca657ffb170ae07ac8363205ae9", size = 5336340, upload-time = "2026-06-01T19:30:54.047Z" }, + { url = "https://files.pythonhosted.org/packages/77/1d/c84e30c0c674184948b66f076ab271c01d940618a2824c23cd035a27bc20/debugpy-1.8.21-cp312-cp312-win_amd64.whl", hash = "sha256:bd7ba9dd3daa7c2f942c6ca8d4695a16bf9ac16b63615261c7982bc74f7ed20c", size = 5374751, upload-time = "2026-06-01T19:30:55.891Z" }, + { url = "https://files.pythonhosted.org/packages/77/6b/d817e1f8cc77aa055d37fba092e0febfdff40fe652d8d53d4cd7a86ad98d/debugpy-1.8.21-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:13678151fc401e2d68c9880b91e28714f797d40422994572b24560ef80910a88", size = 2477398, upload-time = "2026-06-01T19:30:57.644Z" }, + { url = "https://files.pythonhosted.org/packages/48/57/412421516afc3055fa577516f00beec3d663f9b0ab330639547ae6c57720/debugpy-1.8.21-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:ecbd158386c31ffe71d46f72d44d56e66331ab9b16cad649156d514368f23ab2", size = 3962096, upload-time = "2026-06-01T19:30:59.235Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2c616337cf6ba7b07ebbc97f02c6c945a8e2f76b365e33ee809c32ee36d1/debugpy-1.8.21-cp313-cp313-win32.whl", hash = "sha256:2c2ae706dec41d99a9ca1f7ebc987a83e65578363be6f6b3ac9067504917fae1", size = 5336288, upload-time = "2026-06-01T19:31:00.79Z" }, + { url = "https://files.pythonhosted.org/packages/f8/99/9175103392f84c4b1bf7622888cdc68da07f0ff7d9e581266428f6776033/debugpy-1.8.21-cp313-cp313-win_amd64.whl", hash = "sha256:aa648733047443eb1d07682c4ef287d36a54507b643ffdf38b09a3ef002c72a0", size = 5376567, upload-time = "2026-06-01T19:31:02.56Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3d/f4bbb323a548bfab2af3d6b4ffd9bf22636e55956a1285d317a1de643aad/debugpy-1.8.21-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9bb2a685287a2ac9b181cde89edcec64845cb51de7faaa75badb9a698bc24782", size = 2477209, upload-time = "2026-06-01T19:31:04.157Z" }, + { url = "https://files.pythonhosted.org/packages/8c/2d/6e7ec524984a1702777868de49a4c53202bddac2a432a76a093469587750/debugpy-1.8.21-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:3d6922439bf33fd38a3e2c447869ebc7b97da5cd3d329ff1ef9bc06c4903437e", size = 3927115, upload-time = "2026-06-01T19:31:05.863Z" }, + { url = "https://files.pythonhosted.org/packages/97/47/d1aa6d64005a98a9144647d99306b419396f9ad7bf1d73c119e17a81fb4d/debugpy-1.8.21-cp314-cp314-win32.whl", hash = "sha256:15d4963bd5ffa48f0da0947fd06757fa7621945048a14ad7705431566d3c0e7c", size = 5336724, upload-time = "2026-06-01T19:31:07.711Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/b905b90d163af11878c1af8abafa4a25206335e112e284e413454543a6da/debugpy-1.8.21-cp314-cp314-win_amd64.whl", hash = "sha256:fe0744a12353406de0ae8ccff0d0a4a666f00801a3db8fd04e7a5f761cd520e8", size = 5373803, upload-time = "2026-06-01T19:31:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/95/51/67e7cf11a53e40694f720457d5b3a1cdaaa3d5a9a633e482f225456b93ff/debugpy-1.8.21-py2.py3-none-any.whl", hash = "sha256:b1e37d333663c8851516a47364ef473da127f9caebe4417e6df6f5825a7e9a92", size = 5352888, upload-time = "2026-06-01T19:31:25.186Z" }, ] [[package]] @@ -400,11 +400,11 @@ wheels = [ [[package]] name = "giturlparse" -version = "0.14.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/7f25a604a406be7d7d0f849bfcbc1603df084e9e58fe6170980c231138e4/giturlparse-0.14.0.tar.gz", hash = "sha256:0a13208cb3f60e067ee3d09d28e01f9c936065986004fa2d5cd6db7758e9f6e6", size = 15637, upload-time = "2025-10-22T09:21:11.674Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8a/b70b84cc78f9059d627f09560c81a11e8f046570d220a24e169489cad6c9/giturlparse-0.15.0.tar.gz", hash = "sha256:9af3f1fd5c4a0cac94ddb283593635005646393ee0debbe330d1bdff8866bf2c", size = 16138, upload-time = "2026-06-16T07:28:56.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/f9/9ff5a301459f804a885f237453ba81564bc6ee54740e9f2676c2642043f6/giturlparse-0.14.0-py2.py3-none-any.whl", hash = "sha256:04fd9c262ca9a4db86043d2ef32b2b90bfcbcdefc4f6a260fd9402127880931d", size = 16299, upload-time = "2025-10-22T09:21:10.818Z" }, + { url = "https://files.pythonhosted.org/packages/c7/96/147a2771ab655b9353781fb2f95c94eaf1d8576dccc991c1a61d0d355067/giturlparse-0.15.0-py2.py3-none-any.whl", hash = "sha256:76d2e6983b037356ab99b30683e533ac3db96409b68e2163a20fc3aff6446f10", size = 16683, upload-time = "2026-06-16T07:28:55.184Z" }, ] [[package]] @@ -831,7 +831,7 @@ wheels = [ [[package]] name = "mistral-vibe" -version = "2.18.2" +version = "2.18.3" source = { editable = "." } dependencies = [ { name = "agent-client-protocol" }, @@ -979,7 +979,7 @@ requires-dist = [ { name = "eval-type-backport", specifier = "==0.3.1" }, { name = "gitdb", specifier = "==4.0.12" }, { name = "gitpython", specifier = "==3.1.50" }, - { name = "giturlparse", specifier = "==0.14.0" }, + { name = "giturlparse", specifier = "==0.15.0" }, { name = "google-auth", specifier = "==2.49.2" }, { name = "googleapis-common-protos", specifier = "==1.74.0" }, { name = "h11", specifier = "==0.16.0" }, @@ -1056,7 +1056,7 @@ requires-dist = [ { name = "uc-micro-py", specifier = "==2.0.0" }, { name = "urllib3", specifier = "==2.7.0" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'", specifier = "==0.46.0" }, - { name = "watchfiles", specifier = "==1.1.1" }, + { name = "watchfiles", specifier = "==1.2.0" }, { name = "websockets", specifier = "==16.0" }, { name = "zipp", specifier = "==3.23.1" }, { name = "zstandard", specifier = "==0.25.0" }, @@ -1075,7 +1075,7 @@ build = [ ] dev = [ { name = "cryptography", specifier = ">=44.0.0" }, - { name = "debugpy", specifier = ">=1.8.19" }, + { name = "debugpy", specifier = ">=1.8.21" }, { name = "pre-commit", specifier = ">=4.2.0" }, { name = "pyinstrument", specifier = ">=5.1.2" }, { name = "pyright", specifier = ">=1.1.403" }, @@ -1085,7 +1085,7 @@ dev = [ { name = "pytest-timeout", specifier = ">=2.4.0" }, { name = "pytest-xdist", specifier = ">=3.8.0" }, { name = "respx", specifier = ">=0.22.0" }, - { name = "ruff", specifier = ">=0.15.13" }, + { name = "ruff", specifier = ">=0.15.18" }, { name = "tomlkit", specifier = ">=0.13.0" }, { name = "twine", specifier = ">=5.0.0" }, { name = "typos", specifier = ">=1.34.0" }, @@ -1949,27 +1949,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.13" +version = "0.15.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/21/a7d5c126d5b557715ef81098f3db2fe20f622a039ff2e626af28d674ab80/ruff-0.15.13.tar.gz", hash = "sha256:f9d89f17f7ba7fb2ed42921f0df75da797a9a5d71bc39049e2c687cf2baf44b7", size = 4678180, upload-time = "2026-05-14T13:44:37.869Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/98/1295ad5a5aa9bc85bdcdfa5d82fe7b49c61af5657df4f227637ff9de0da6/ruff-0.15.18.tar.gz", hash = "sha256:2698a964c70e8bf402dcb99c8810472d270d141e7aa8c4e13599fd52033a2f33", size = 4761437, upload-time = "2026-06-18T18:25:39.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/61/11d458dc6ac22504fd8e237b29dfd40504c7fbbcc8930402cfe51a8e63ed/ruff-0.15.13-py3-none-linux_armv6l.whl", hash = "sha256:444b580fc72fd6887e650acd3e575e18cdc79dbcf42fb4030b491057921f61f8", size = 10738279, upload-time = "2026-05-14T13:44:18.7Z" }, - { url = "https://files.pythonhosted.org/packages/86/ca/caa871ee7be718c45256fada4e16a218ee3e33f0c4a46b729a60a24912e6/ruff-0.15.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6590d009e7cb7ebf36f83dbdd44a3fa48a0994ff6f1cdc1b08006abe58f98dc7", size = 11124798, upload-time = "2026-05-14T13:44:06.427Z" }, - { url = "https://files.pythonhosted.org/packages/d3/19/43f5f2e568dddde567fc41f8471f9432c09563e19d3e617a48cfa52f8f0a/ruff-0.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1c26d2f66163deeb6e08d8b39fbbe983ce3c71cea06a6d7591cfd1421793c629", size = 10460761, upload-time = "2026-05-14T13:44:04.375Z" }, - { url = "https://files.pythonhosted.org/packages/99/df/cf938cd6de3003178f03ad7c1ea2a6c099468c03a35037985070b37e76be/ruff-0.15.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbd6f94b434f896308e4d57fb7bfde0d02b99f7a64b3bdab0fdfa6a864203a5", size = 10804451, upload-time = "2026-05-14T13:44:25.221Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7d/5d0973129b154ded2225729169d7068f26b467760b146493fde138415f23/ruff-0.15.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3259f3be4d181bda591da5db2571aed6853c6a048157756448020bc6c5cd22", size = 10534285, upload-time = "2026-05-14T13:44:08.888Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e3/6b999bbc66cd51e5f073842bc2a3995e99c5e0e72e16b15e7261f7abf57a/ruff-0.15.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae9c17e5eb4430c154e76abc25d79a318190f5a997f38fb6b114416c5319ffc9", size = 11312063, upload-time = "2026-05-14T13:44:11.274Z" }, - { url = "https://files.pythonhosted.org/packages/af/5a/642639e9f5db04f1e97fbd6e091c6fd20725bdf072fb114d00eefb9e6eb8/ruff-0.15.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e2e39bff6c341f4b577a21b801326fab0b11847f48fcaa83f00a113c9b3cb55", size = 12183079, upload-time = "2026-05-14T13:44:01.634Z" }, - { url = "https://files.pythonhosted.org/packages/19/4c/7585735f6b53b0f12de13618b2f7d250a844f018822efc899df2e7b8295f/ruff-0.15.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d9a8e08013542e94d3220bc5b62cc3e5ef87c5f74bff367d3fac14fab013e6", size = 11440833, upload-time = "2026-05-14T13:43:59.043Z" }, - { url = "https://files.pythonhosted.org/packages/e8/31/bf1a0803d077e679cfeee5f2f67290a0fa79c7385b5d9a8c17b9db2c48f0/ruff-0.15.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc411dfebe5eebe55ce041c6ae080eb7668955e866daa2fbb16692a784f1c4ca", size = 11434486, upload-time = "2026-05-14T13:44:27.761Z" }, - { url = "https://files.pythonhosted.org/packages/e1/4e/62c9b999875d4f14db80f277c030578f5e249c9852d65b7ac7ad0b43c041/ruff-0.15.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:768494eb08b9cee54e2fd27969966f74db5a57f6eaa7a90fcb3306af34dfc4bd", size = 11385189, upload-time = "2026-05-14T13:44:13.704Z" }, - { url = "https://files.pythonhosted.org/packages/fc/89/7e959047a104df3eb12863447c110140191fc5b6c4f379ea2e803fcdb0e4/ruff-0.15.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb75f9a3a7e42ffe117d734494e6c5e5cb3565d66e12612cb63d0e572a41a5b6", size = 10781380, upload-time = "2026-05-14T13:43:56.734Z" }, - { url = "https://files.pythonhosted.org/packages/ff/52/5fd18f3b88cab63e88aa11516b3b4e1e5f720e5c330f8dbe5c26210f41f8/ruff-0.15.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8cb74dd33bb2f6613faf7fc03b660053b5ac4f80e706d5788c6335e2a8048d51", size = 10540605, upload-time = "2026-05-14T13:44:20.748Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e0/9e35f338990d3e41a82875ff7053ffe97541dae81c9d02143177f381d572/ruff-0.15.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7ef823f817fcd191dc934e984be9cf4094f808effa16f2542ad8e821ba02bbf2", size = 11036554, upload-time = "2026-05-14T13:44:16.256Z" }, - { url = "https://files.pythonhosted.org/packages/c2/13/070fb048c24080fba188f66371e2a92785be257ad02242066dc7255ac6e9/ruff-0.15.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f345a13937bd7f09f6f5d19fa0721b0c103e00e7f62bc67089a8e5e037719e0b", size = 11528133, upload-time = "2026-05-14T13:44:22.808Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8c/b1e1666aef7fc6555094d73ae6cd981701781ae85b97ceefc0eebd0b4668/ruff-0.15.13-py3-none-win32.whl", hash = "sha256:4044f94208b3b05ba0fc4a4abd0558cf4d6459bd18325eead7fd8cc66f909b41", size = 10721455, upload-time = "2026-05-14T13:44:35.697Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a6/870a3e8a50590bb92be184ad928c2922f088b00d9dc5c5ec7b924ee08c22/ruff-0.15.13-py3-none-win_amd64.whl", hash = "sha256:7064884d442b7d477b4e7473d12da7f08851d2b1982763c5d3f388a19468a1a4", size = 11900409, upload-time = "2026-05-14T13:44:30.389Z" }, - { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d0/686e984941269621e2be72612d5c1e461f8f7b38415a2a7d7a81c8ae6715/ruff-0.15.18-py3-none-linux_armv6l.whl", hash = "sha256:8b6850172348c8381b8b3084c5915a4393c2373b9b54cd5b5e1ea15812bc10df", size = 10887308, upload-time = "2026-06-18T18:25:03.062Z" }, + { url = "https://files.pythonhosted.org/packages/ed/21/bc4123e3f5515ee99f8ce1eb93a14a0628fe4d1678663cd08f933ac16931/ruff-0.15.18-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3fccc153a85417dcd976883160cacce486997b0a0058dd18f54b8aaaac7d1ce2", size = 11281305, upload-time = "2026-06-18T18:25:30.026Z" }, + { url = "https://files.pythonhosted.org/packages/51/93/4769464c25cf7ab2acb3c7dda9cad3d867eb41c59565b3e2a9d17249c90c/ruff-0.15.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08d4c86a68f2c3ec2c9d56380a71fb4a4f65373055cbb8caabd645e9102f38d4", size = 10641215, upload-time = "2026-06-18T18:25:15.802Z" }, + { url = "https://files.pythonhosted.org/packages/6c/42/56926d17120db2c208d76bf60a1a019644dd9e91dc27f0f95c9caddb1366/ruff-0.15.18-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37e5108745c2c0705da916d7d4de533ddf547051ef45f62888c31bae73f66318", size = 10957224, upload-time = "2026-06-18T18:25:36.955Z" }, + { url = "https://files.pythonhosted.org/packages/22/4f/d43fab8d8189afde803103022d000a8ef9f230616d436d52a8b2b8d63b50/ruff-0.15.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56949a6ce8b3abde54c0bcb22cebfe57e8771cadc84b407ae8b8eaf67ebdcd43", size = 10699024, upload-time = "2026-06-18T18:25:05.707Z" }, + { url = "https://files.pythonhosted.org/packages/63/42/1e3e4c68bd408b9768cf3e439acbe2c78245225faef253f7028a0cdb63e0/ruff-0.15.18-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01a754cd6a1b630d3f97e33eb452cf7a98040482318e870f8bc52a5a30e62657", size = 11491458, upload-time = "2026-06-18T18:25:20.275Z" }, + { url = "https://files.pythonhosted.org/packages/20/77/47a3484bea8521e14a203d98c389c5c97846675e4f02734672da4a69b52a/ruff-0.15.18-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ba7a07e03a44dbf10bb086ee06705b173625014ec99f73a7e6836a5e5590a0c", size = 12383752, upload-time = "2026-06-18T18:25:22.535Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ca/054159590787023d83b658a1a1819c4c8910114e7015069340b71c0961cb/ruff-0.15.18-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a2c40a41a4cadbcf5897b548ab29dfe248b20c540961c0247d98a3973c70403", size = 11577923, upload-time = "2026-06-18T18:25:10.702Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ff/d353d6b7bbd73cc0ec37f4463d7540e45e894338abdd9964eee0de332708/ruff-0.15.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f0480ce690cbb6c4db6e5d08f19fce98e10ba131a8b60c1bcdac42771e3ae2d", size = 11583925, upload-time = "2026-06-18T18:25:32.391Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4a/891f89b9c296ed3e5f3ece1a5629badc989d9a8fdaa30431aaf4774bc1c2/ruff-0.15.18-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2330215f1f393fa8733f55edce04fcf94c36a2c460fcde31f78cc84e4951e9b1", size = 11582834, upload-time = "2026-06-18T18:25:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/32/a3/ed9e370154bf85de360b93c03026157f02d4943b2d01ff4945f4429f8e8a/ruff-0.15.18-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6aa6a3d979e48ae617578183674bf264fbe7d0114a796a26bd678d67963c7ff", size = 10927328, upload-time = "2026-06-18T18:25:34.676Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d1/5cf5909329fedb5d39d555ee818ba5cf4638e1a301b89785d34f2905bfcb/ruff-0.15.18-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a81beadbbff2c9c245561ae3f77b16709d87f35eec650d0501679239d3449b22", size = 10693187, upload-time = "2026-06-18T18:25:08.245Z" }, + { url = "https://files.pythonhosted.org/packages/fd/44/ff6c635cf2c4f4e7b618b6640da057376baa36014695487d88aed4794268/ruff-0.15.18-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2186d9e940ae332ab293623a75b5f4fe49565f449954d50a72a046683aa6b809", size = 11208721, upload-time = "2026-06-18T18:25:41.327Z" }, + { url = "https://files.pythonhosted.org/packages/88/d9/5baa2a30861adfb7022cf33c1e35b2fc18085b08c16f83eff4c7b99a5f48/ruff-0.15.18-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5c2abf140438032bc77b2284a6c9944ecd8a19e5f1c7b52b1b8e4a0a80d19a7a", size = 11678599, upload-time = "2026-06-18T18:25:13.607Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1a/0725a7cfdc32ff769efb96ee782bec882e16448c5d9e3be947ec4c04ce27/ruff-0.15.18-py3-none-win32.whl", hash = "sha256:02299e6e9fa5b297a3f6d5d10d7bcd655c925b028bb8b9d4588214549c6b9ec4", size = 10901903, upload-time = "2026-06-18T18:25:24.755Z" }, + { url = "https://files.pythonhosted.org/packages/f3/51/805d9f6fb7970505c3504794a5ec350f605361b807fef4dcf214ebd35e72/ruff-0.15.18-py3-none-win_amd64.whl", hash = "sha256:dac80dc8d26b2257dbefabed62f5d255c3937b4ccb122da1fc634794fa3578b3", size = 12041189, upload-time = "2026-06-18T18:25:17.915Z" }, + { url = "https://files.pythonhosted.org/packages/29/4c/67bb45e41609eb4726f1bfeb59e083cf91d14c696d4bd14c234a980be93d/ruff-0.15.18-py3-none-win_arm64.whl", hash = "sha256:b2c9257fcbd4a3e5b977a1904e6facca016bafe2edc17df24db67cfaee03b4e4", size = 11329958, upload-time = "2026-06-18T18:25:43.686Z" }, ] [[package]] @@ -2355,72 +2355,88 @@ wheels = [ [[package]] name = "watchfiles" -version = "1.1.1" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2f/e42c992d2afda3108ea1c02acecc991b9f31d05c14adc2a7cee9ee211fc4/watchfiles-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26", size = 400115, upload-time = "2026-05-18T04:32:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8f/6af2ea19065c91d8b0ea3516fdfc8c0d349f407e8e9fbf4e5a17360de8ad/watchfiles-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c", size = 393659, upload-time = "2026-05-18T04:30:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/b32a967c56fb3e3e5be3db52c3d3b87fa4513aa367d8ed1ad96d42952e5f/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc", size = 453207, upload-time = "2026-05-18T04:31:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/97557a812180338cb1abd32e1cffcc4588f59b5f23e0cb006b2ba95ba64a/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0", size = 459273, upload-time = "2026-05-18T04:31:50.377Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a8/b4b08dcb7653b8087c6586f7ce649505900e866bbcfe40dc9587af02e686/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c", size = 489927, upload-time = "2026-05-18T04:31:42.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/3dceea03545d2e5ddfd839f0ddd5e1cecbf1697b5a428d5ba11cef6af95d/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01", size = 570476, upload-time = "2026-05-18T04:31:03.071Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f2/d39a5450c3532092b91f81d274360e613c2371bc874a89c7a1a3c5e8d138/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8", size = 465650, upload-time = "2026-05-18T04:30:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/22/24/ed72f68cbc1333ca9b9f2200aa048bb6658ae41709bc1caad4310f4bdffd/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5", size = 456398, upload-time = "2026-05-18T04:30:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/0d/64/982ef4a4e5bab5b6e5b6becc8cd5e732f6130a78b855f0abec6439a9a135/watchfiles-1.2.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d", size = 465140, upload-time = "2026-05-18T04:31:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/95282abf4ed680b6096010bcfc30c5fa7a041fc5aa5a2ad17a2cc6c75bba/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c", size = 630259, upload-time = "2026-05-18T04:31:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/607c1de1530c4bdcf2cf1d1ecc2505ddba5d96bd43ba9f2b0e79876f850f/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906", size = 659859, upload-time = "2026-05-18T04:30:24.333Z" }, + { url = "https://files.pythonhosted.org/packages/fa/08/d9e2e0f9e8e6791d33aefc694ad7eefa7f901f63caff84a81ded38692f9c/watchfiles-1.2.0-cp312-cp312-win32.whl", hash = "sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898", size = 275480, upload-time = "2026-05-18T04:30:31.307Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/9d42569c0102645cc8cea5d8c7d8a1e9d4ada2cb7f05f75e554b8aa2202a/watchfiles-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379", size = 288718, upload-time = "2026-05-18T04:32:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/0a/26/88e0dc6ee3898169d7fa22bb6a69cabf2502d2ee25cb8c876d1262d204f8/watchfiles-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5", size = 281026, upload-time = "2026-05-18T04:30:22.23Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/362702539275019a54dd2e94511b31a9b89c5f9e6a21966de7eb692549fc/watchfiles-1.2.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374", size = 400109, upload-time = "2026-05-18T04:31:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/71d5ba62db781e5587bded1d944c675374bc4aa37ff33d5018d98e8b6538/watchfiles-1.2.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65", size = 392167, upload-time = "2026-05-18T04:31:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/c66dd95d0423fe30d31820e2d1d5bda773764131bbb6ac0cb1cf303ac328/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69", size = 452372, upload-time = "2026-05-18T04:31:00.836Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/2fe99557e72f85627c6a8eed50d889e8d101623e060a22ad75b875cb932d/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579", size = 459596, upload-time = "2026-05-18T04:31:34.96Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/d4acfa0023367428ed48351b3b9b267893037b6cadae55620c61c24bcfd4/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7", size = 490869, upload-time = "2026-05-18T04:31:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/3164cbdce06c9fb95c4f7b9e2f9760b5e2797af43a9ecc317ef42a23a278/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2", size = 571641, upload-time = "2026-05-18T04:32:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/85d3731c55e65cd7690f3f803d24c139588aaf863e4bf2148fe7a7fa1a19/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6", size = 464444, upload-time = "2026-05-18T04:30:34.298Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/562641012b8b09872742c3b8adf9629ec479fd78f8d68ae4a0c13da8add6/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4", size = 453593, upload-time = "2026-05-18T04:31:23.464Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/cb8ef3d6f929d14158fdaaad9925985b7310abc9384dcd4d82dd0016fb59/watchfiles-1.2.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488", size = 465096, upload-time = "2026-05-18T04:31:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/80908e835e100527a9267147b08c0eee1fa6ab0ffec15edc04d1d44885f7/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb", size = 630638, upload-time = "2026-05-18T04:30:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/95ab2f256bb4af3cb2eb23b9317bda984ee6e0f11733a5c004a6c95b06e3/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377", size = 657684, upload-time = "2026-05-18T04:31:32.027Z" }, ] [[package]] diff --git a/vibe/__init__.py b/vibe/__init__.py index 2c66307..78f9928 100644 --- a/vibe/__init__.py +++ b/vibe/__init__.py @@ -3,4 +3,4 @@ from __future__ import annotations from pathlib import Path VIBE_ROOT = Path(__file__).parent -__version__ = "2.18.2" +__version__ = "2.18.3" diff --git a/vibe/acp/acp_agent_loop.py b/vibe/acp/acp_agent_loop.py index b5d0873..5e74fa1 100644 --- a/vibe/acp/acp_agent_loop.py +++ b/vibe/acp/acp_agent_loop.py @@ -748,14 +748,16 @@ class VibeAcpAgentLoop(AcpAgent): pass async def _notify_mcp_auth_required(self, session: AcpSessionLoop) -> None: + """Show a notice if any enabled MCP servers require OAuth authentication.""" registry = session.agent_loop.mcp_registry if registry is None: return statuses = registry.status() + disabled = registry.disabled_aliases() aliases = sorted( alias for alias, status in statuses.items() - if status is AuthStatus.NEEDS_AUTH + if status is AuthStatus.NEEDS_AUTH and alias not in disabled ) if not aliases: return diff --git a/vibe/cli/textual_ui/app.py b/vibe/cli/textual_ui/app.py index 466f52a..c31b213 100644 --- a/vibe/cli/textual_ui/app.py +++ b/vibe/cli/textual_ui/app.py @@ -819,16 +819,18 @@ class VibeApp(App): # noqa: PLR0904 pass async def _show_mcp_auth_required_notice(self) -> None: + """Show a notice if any enabled MCP servers require OAuth authentication.""" registry = self.agent_loop.mcp_registry if registry is None: return from vibe.core.tools.mcp import AuthStatus statuses = registry.status() + disabled = registry.disabled_aliases() aliases = sorted( alias for alias, status in statuses.items() - if status is AuthStatus.NEEDS_AUTH + if status is AuthStatus.NEEDS_AUTH and alias not in disabled ) if not aliases: return @@ -1215,7 +1217,7 @@ class VibeApp(App): # noqa: PLR0904 if desired: await self._mount_and_scroll( UserCommandMessage( - "Voice mode enabled. Press ctrl+r to start recording." + "Voice mode enabled. Press **Ctrl+R** to start recording." ) ) else: @@ -3487,6 +3489,10 @@ class VibeApp(App): # noqa: PLR0904 container.input_widget.action_delete_right() return + if not self.config.ask_confirmation_on_exit: + self._force_quit() + return + if self._quit_manager.is_confirmed("Ctrl+D"): self._force_quit() return diff --git a/vibe/cli/textual_ui/message_queue.py b/vibe/cli/textual_ui/message_queue.py index 6d06702..a074f82 100644 --- a/vibe/cli/textual_ui/message_queue.py +++ b/vibe/cli/textual_ui/message_queue.py @@ -11,6 +11,7 @@ from uuid import uuid4 from textual.widget import Widget +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.messages import ( BashOutputMessage, ErrorMessage, @@ -378,8 +379,11 @@ class QueueController: await self._ensure_header() await self._ports.mount_and_scroll( ErrorMessage( - f"Model `{active_model.alias}` does not support images. " - f"Switch with /model, then press Enter to resume the queue.", + shortcut_hint( + f"Model `{active_model.alias}` does not support images. " + f"Switch with /model, then press {shortcut('Enter')} " + "to resume the queue." + ), show_border=False, ) ) diff --git a/vibe/cli/textual_ui/quit_manager.py b/vibe/cli/textual_ui/quit_manager.py index 89845cb..db2b45a 100644 --- a/vibe/cli/textual_ui/quit_manager.py +++ b/vibe/cli/textual_ui/quit_manager.py @@ -6,6 +6,7 @@ from typing import Literal from textual.app import App from textual.timer import Timer +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.path_display import PathDisplay QuitConfirmKey = Literal["Ctrl+C", "Ctrl+D"] @@ -37,12 +38,12 @@ class QuitManager: self._confirm_timer = None self._confirm_time = time.monotonic() self._confirm_key = key - prompt = f"Press {key} again to quit" + prompt = f"Press {shortcut(key)} again to quit" if extra: prompt = f"{prompt} ({extra})" try: path_display = self._app.query_one(PathDisplay) - path_display.update(prompt) + path_display.update(shortcut_hint(prompt)) except Exception: pass self._confirm_timer = self._app.set_timer( diff --git a/vibe/cli/textual_ui/shortcut_hints.py b/vibe/cli/textual_ui/shortcut_hints.py new file mode 100644 index 0000000..ab59f20 --- /dev/null +++ b/vibe/cli/textual_ui/shortcut_hints.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from rich.markup import escape +from textual.content import Content + +SHORTCUT_STYLE = "b $accent" + + +def shortcut(key: str) -> str: + return f"[{SHORTCUT_STYLE}]{escape(key)}[/]" + + +def shortcut_hint(markup: str) -> Content: + return Content.from_markup(markup) + + +def with_status(status: str | None, hint: Content) -> Content: + if not status: + return hint + return Content(status + " ") + hint diff --git a/vibe/cli/textual_ui/widgets/approval_app.py b/vibe/cli/textual_ui/widgets/approval_app.py index e63ebed..3bf82a5 100644 --- a/vibe/cli/textual_ui/widgets/approval_app.py +++ b/vibe/cli/textual_ui/widgets/approval_app.py @@ -12,6 +12,7 @@ from textual.message import Message from textual.widget import Widget from textual.widgets import Static +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.cli.textual_ui.widgets.tool_widgets import get_approval_widget from vibe.cli.textual_ui.widgets.vim_navigation import VimNavigationMixin @@ -114,7 +115,11 @@ class ApprovalApp(VimNavigationMixin, Container): self.option_widgets.append(widget) yield widget self.help_widget = NoMarkupStatic( - "↑↓/jk navigate Enter select ESC reject", classes="approval-help" + shortcut_hint( + f"{shortcut('↑↓/jk')} navigate {shortcut('Enter')} select " + f"{shortcut('Esc')} reject" + ), + classes="approval-help", ) yield self.help_widget diff --git a/vibe/cli/textual_ui/widgets/config_app.py b/vibe/cli/textual_ui/widgets/config_app.py index 1ac29d1..18d4c0f 100644 --- a/vibe/cli/textual_ui/widgets/config_app.py +++ b/vibe/cli/textual_ui/widgets/config_app.py @@ -7,11 +7,13 @@ from rich.text import Text from textual.app import ComposeResult from textual.binding import Binding, BindingType from textual.containers import Container, Vertical +from textual.content import Content from textual.events import DescendantBlur from textual.message import Message from textual.widgets import OptionList from textual.widgets.option_list import Option +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.navigable_option_list import NavigableOptionList from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic @@ -72,6 +74,7 @@ class ConfigApp(Container): "file_watcher_for_autocomplete", "Autocomplete watcher (may delay first autocompletion)", ), + ("ask_confirmation_on_exit", "Confirm quit on Ctrl+D"), ] def _get_current_model(self) -> str: @@ -103,15 +106,11 @@ class ConfigApp(Container): text.append(self._get_current_thinking().capitalize(), style="bold") return text - def _toggle_prompt(self, key: str, label: str) -> Text: + def _toggle_prompt(self, key: str, label: str) -> Content: value = self._get_toggle_value(key) - text = Text(no_wrap=True) - text.append(f"{label}: ") if value == "On": - text.append("On", style="green bold") - else: - text.append("Off", style="dim") - return text + return Content.assemble(f"{label}: ", ("On", "bold $success")) + return Content.assemble(f"{label}: ", ("Off", "dim")) def compose(self) -> ComposeResult: options: list[Option] = [ @@ -129,7 +128,11 @@ class ConfigApp(Container): yield NavigableOptionList(*options, id="config-options") yield NoMarkupStatic("") yield NoMarkupStatic( - "↑↓/jk Navigate Enter Select/Toggle Esc Exit", classes="settings-help" + shortcut_hint( + f"{shortcut('↑↓/jk')} Navigate {shortcut('Enter')} Select/Toggle " + f"{shortcut('Esc')} Exit" + ), + classes="settings-help", ) def on_mount(self) -> None: diff --git a/vibe/cli/textual_ui/widgets/connector_auth_app.py b/vibe/cli/textual_ui/widgets/connector_auth_app.py index 983d4d8..60c68c9 100644 --- a/vibe/cli/textual_ui/widgets/connector_auth_app.py +++ b/vibe/cli/textual_ui/widgets/connector_auth_app.py @@ -4,6 +4,7 @@ from enum import StrEnum, auto from typing import TYPE_CHECKING, ClassVar import webbrowser +from rich.markup import escape from rich.text import Text from textual.app import ComposeResult from textual.binding import Binding, BindingType @@ -15,13 +16,14 @@ from textual.widgets.option_list import Option from textual.worker import Worker from vibe.cli.clipboard import copy_text_to_clipboard +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint, with_status from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.tools.connectors import ConnectorRegistry if TYPE_CHECKING: from vibe.core.tools.manager import ToolManager -_HELP = "Backspace Back" +_HELP = f"{shortcut('Backspace')} Back" _OPTION_PADDING = " " @@ -145,9 +147,9 @@ class ConnectorAuthApp(Container): option_list.add_option(Option("", disabled=True)) option_list.add_option( Option( - Text( - f"{_OPTION_PADDING}Press enter to open auth in your browser", - no_wrap=True, + shortcut_hint( + f"{_OPTION_PADDING}Press {shortcut('Enter')} " + "to open auth in your browser" ), id=_AuthOptionId.OPEN, ) @@ -216,12 +218,12 @@ class ConnectorAuthApp(Container): detail = self.query_one("#connectorauth-detail", NoMarkupStatic) parts: list[str] = [] if self._auth_url_visible and self._auth_url: - parts.append(self._auth_url) + parts.append(escape(self._auth_url)) parts.append("") - parts.append("Once authenticated, press R to refresh") - detail.update("\n".join(parts)) + parts.append(f"Once authenticated, press {shortcut('r')} to refresh") + detail.update(shortcut_hint("\n".join(parts))) def _set_help_text(self, text: str) -> None: - if self._status_message: - text = f"{self._status_message} {text}" - self.query_one("#connectorauth-help", NoMarkupStatic).update(text) + self.query_one("#connectorauth-help", NoMarkupStatic).update( + with_status(self._status_message, shortcut_hint(text)) + ) diff --git a/vibe/cli/textual_ui/widgets/loading.py b/vibe/cli/textual_ui/widgets/loading.py index 4d1e822..f45ba59 100644 --- a/vibe/cli/textual_ui/widgets/loading.py +++ b/vibe/cli/textual_ui/widgets/loading.py @@ -12,6 +12,7 @@ from textual.containers import Horizontal from textual.widgets import Static from vibe.cli.textual_ui.constants import MistralColors +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.cli.textual_ui.widgets.spinner import SpinnerMixin, SpinnerType @@ -143,16 +144,18 @@ class LoadingWidget(SpinnerMixin, Static): return self._queued_count = count if self.hint_widget is not None: - self.hint_widget.update(self._format_hint(max(self._last_elapsed, 0))) + self.hint_widget.update( + shortcut_hint(self._format_hint(max(self._last_elapsed, 0))) + ) def _format_hint(self, elapsed: int) -> str: elapsed_str = _format_elapsed(elapsed) if self._queued_count > 0: return ( - f"({elapsed_str} Esc to interrupt · " - "Ctrl+C to cancel last queued message)" + f"({elapsed_str} {shortcut('Esc')} to interrupt · " + f"{shortcut('Ctrl+C')} to cancel last queued message)" ) - return f"({elapsed_str} Esc/Ctrl+C to interrupt)" + return f"({elapsed_str} {shortcut('Esc/Ctrl+C')} to interrupt)" def compose(self) -> ComposeResult: with Horizontal(classes="loading-container"): @@ -166,7 +169,8 @@ class LoadingWidget(SpinnerMixin, Static): if self._show_hint: self.hint_widget = NoMarkupStatic( - "(0s Esc/Ctrl+C to interrupt)", classes="loading-hint" + shortcut_hint(f"(0s {shortcut('Esc/Ctrl+C')} to interrupt)"), + classes="loading-hint", ) yield self.hint_widget @@ -232,7 +236,7 @@ class LoadingWidget(SpinnerMixin, Static): elapsed = int(time() - self.start_time - paused) if elapsed != self._last_elapsed: self._last_elapsed = elapsed - self.hint_widget.update(self._format_hint(elapsed)) + self.hint_widget.update(shortcut_hint(self._format_hint(elapsed))) @contextmanager diff --git a/vibe/cli/textual_ui/widgets/mcp_app.py b/vibe/cli/textual_ui/widgets/mcp_app.py index 0154db7..e0fe78a 100644 --- a/vibe/cli/textual_ui/widgets/mcp_app.py +++ b/vibe/cli/textual_ui/widgets/mcp_app.py @@ -14,6 +14,7 @@ from textual.widgets import OptionList from textual.widgets.option_list import Option, OptionDoesNotExist from textual.worker import Worker +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint, with_status from vibe.cli.textual_ui.widgets.navigable_option_list import NavigableOptionList from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.config import ConnectorConfig, VibeConfig @@ -65,15 +66,24 @@ def collect_mcp_tool_index( _LIST_VIEW_HELP_TOOLS = ( - "↑↓/jk Navigate Enter Show tools D Disable E Enable R Refresh Esc Close" + f"{shortcut('↑↓/jk')} Navigate {shortcut('Enter')} Show tools " + f"{shortcut('d')} Disable {shortcut('e')} Enable " + f"{shortcut('r')} Refresh {shortcut('Esc')} Close" ) _LIST_VIEW_HELP_AUTH = ( - "↑↓/jk Navigate Enter Connect D Disable E Enable R Refresh Esc Close" + f"{shortcut('↑↓/jk')} Navigate {shortcut('Enter')} Connect " + f"{shortcut('d')} Disable {shortcut('e')} Enable " + f"{shortcut('r')} Refresh {shortcut('Esc')} Close" ) _DETAIL_VIEW_HELP = ( - "↑↓/jk Navigate D Disable E Enable Backspace Back R Refresh Esc Close" + f"{shortcut('↑↓/jk')} Navigate {shortcut('d')} Disable " + f"{shortcut('e')} Enable {shortcut('Backspace')} Back " + f"{shortcut('r')} Refresh {shortcut('Esc')} Close" +) +_DETAIL_VIEW_HELP_NO_TOOLS = ( + f"{shortcut('↑↓/jk')} Navigate {shortcut('Backspace')} Back " + f"{shortcut('r')} Refresh {shortcut('Esc')} Close" ) -_DETAIL_VIEW_HELP_NO_TOOLS = "↑↓/jk Navigate Backspace Back R Refresh Esc Close" class MCPApp(Container): @@ -274,9 +284,9 @@ class MCPApp(Container): return _LIST_VIEW_HELP_TOOLS def _set_help_text(self, text: str) -> None: - if self._status_message: - text = f"{self._status_message} {text}" - self.query_one("#mcp-help", NoMarkupStatic).update(text) + self.query_one("#mcp-help", NoMarkupStatic).update( + with_status(self._status_message, shortcut_hint(text)) + ) def _sync_mcp_registry(self) -> None: if self._mcp_registry is None: @@ -630,8 +640,10 @@ class MCPApp(Container): case ConnectorAuthAction.CREDENTIALS_SETUP: option_list.add_option( Option( - "Set up credentials in the Mistral dashboard, " - "then press R to refresh.", + shortcut_hint( + "Set up credentials in the Mistral dashboard, " + f"then press {shortcut('r')} to refresh." + ), disabled=True, ) ) @@ -646,7 +658,10 @@ class MCPApp(Container): case _: option_list.add_option( Option( - "Connector unavailable; press R to refresh.", + shortcut_hint( + f"Connector unavailable; press {shortcut('r')} " + "to refresh." + ), disabled=True, ) ) diff --git a/vibe/cli/textual_ui/widgets/messages.py b/vibe/cli/textual_ui/widgets/messages.py index da075fb..65ba84d 100644 --- a/vibe/cli/textual_ui/widgets/messages.py +++ b/vibe/cli/textual_ui/widgets/messages.py @@ -24,6 +24,7 @@ from textual.widgets import Link, Markdown, Static from textual.widgets._markdown import MarkdownStream from watchfiles import awatch +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.collapsible import ( ClickWithoutDragMixin, CollapsibleSection, @@ -187,7 +188,7 @@ class UserMessage(Static): class QueueHeaderMessage(Static): DEFAULT_LABEL = "» Queued" - PAUSED_LABEL = "» Queued — press Enter to send, type to add" + PAUSED_LABEL = f"» Queued — press {shortcut('Enter')} to send, type to add" def __init__(self, *, paused: bool = False) -> None: super().__init__() @@ -198,7 +199,7 @@ class QueueHeaderMessage(Static): def compose(self) -> ComposeResult: with Vertical(classes="queue-header-container"): self._label_widget = NoMarkupStatic( - self._current_label(), classes="queue-header-content" + shortcut_hint(self._current_label()), classes="queue-header-content" ) yield self._label_widget yield ExpandingSeparator(classes="queue-header-separator") @@ -208,7 +209,7 @@ class QueueHeaderMessage(Static): return self._paused = paused if self._label_widget is not None: - self._label_widget.update(self._current_label()) + self._label_widget.update(shortcut_hint(self._current_label())) def _current_label(self) -> str: return self.PAUSED_LABEL if self._paused else self.DEFAULT_LABEL @@ -619,7 +620,7 @@ class BashOutputMessage(ClickWithoutDragMixin, SpinnerMixin, Static): class ErrorMessage(Static): def __init__( - self, error: str, collapsed: bool = False, show_border: bool = True + self, error: str | Content, collapsed: bool = False, show_border: bool = True ) -> None: super().__init__() self.add_class("error-message") @@ -632,7 +633,12 @@ class ErrorMessage(Static): with Horizontal(classes="error-container"): if self._show_border: yield ExpandingBorder(classes="error-border") - text = f"Error: {self._error}" if self._show_border else self._error + error = ( + self._error + if isinstance(self._error, Content) + else Content(self._error) + ) + text = Content("Error: ") + error if self._show_border else error self._content_widget = NoMarkupStatic(text, classes="error-content") yield self._content_widget diff --git a/vibe/cli/textual_ui/widgets/model_picker.py b/vibe/cli/textual_ui/widgets/model_picker.py index 0e6d71d..ce56be1 100644 --- a/vibe/cli/textual_ui/widgets/model_picker.py +++ b/vibe/cli/textual_ui/widgets/model_picker.py @@ -10,6 +10,7 @@ from textual.message import Message from textual.widgets import OptionList from textual.widgets.option_list import Option +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.navigable_option_list import NavigableOptionList from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic @@ -56,7 +57,11 @@ class ModelPickerApp(Container): yield NoMarkupStatic("Select Model", classes="modelpicker-title") yield NavigableOptionList(*options, id="modelpicker-options") yield NoMarkupStatic( - "↑↓/jk Navigate Enter Select Esc Cancel", classes="modelpicker-help" + shortcut_hint( + f"{shortcut('↑↓/jk')} Navigate {shortcut('Enter')} Select " + f"{shortcut('Esc')} Cancel" + ), + classes="modelpicker-help", ) def on_mount(self) -> None: diff --git a/vibe/cli/textual_ui/widgets/narrator_status.py b/vibe/cli/textual_ui/widgets/narrator_status.py index 3c70963..84b0532 100644 --- a/vibe/cli/textual_ui/widgets/narrator_status.py +++ b/vibe/cli/textual_ui/widgets/narrator_status.py @@ -11,6 +11,7 @@ from vibe.cli.narrator_manager.narrator_manager_port import ( NarratorManagerPort, NarratorState, ) +from vibe.cli.textual_ui.shortcut_hints import shortcut SHRINK_FRAMES = "█▇▆▅▄▃▂▁" BAR_FRAMES = ["▂▅▇", "▃▆▅", "▅▃▇", "▇▂▅", "▅▇▃", "▃▅▆"] @@ -50,12 +51,14 @@ class NarratorStatus(NarratorManagerListener, Static): case NarratorState.SUMMARIZING: char = SHRINK_FRAMES[self._frame % len(SHRINK_FRAMES)] self.update( - f"[bold orange]{char}[/bold orange] summarizing [dim]Esc/Ctrl+C to stop[/dim]" + f"[bold orange]{char}[/bold orange] summarizing " + f"[dim]{shortcut('Esc/Ctrl+C')} to stop[/dim]" ) case NarratorState.SPEAKING: bars = BAR_FRAMES[self._frame % len(BAR_FRAMES)] self.update( - f"[bold orange]{bars}[/bold orange] speaking [dim]Esc/Ctrl+C to stop[/dim]" + f"[bold orange]{bars}[/bold orange] speaking " + f"[dim]{shortcut('Esc/Ctrl+C')} to stop[/dim]" ) self._frame += 1 diff --git a/vibe/cli/textual_ui/widgets/proxy_setup_app.py b/vibe/cli/textual_ui/widgets/proxy_setup_app.py index ab496a1..a885a1f 100644 --- a/vibe/cli/textual_ui/widgets/proxy_setup_app.py +++ b/vibe/cli/textual_ui/widgets/proxy_setup_app.py @@ -9,6 +9,7 @@ from textual.containers import Container, Vertical from textual.message import Message from textual.widgets import Input, Static +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.cli.textual_ui.widgets.vscode_compat import VscodeCompatInput from vibe.core.proxy_setup import ( @@ -59,7 +60,11 @@ class ProxySetupApp(Container): yield input_widget yield NoMarkupStatic( - "↑↓ navigate Enter save & exit ESC cancel", classes="settings-help" + shortcut_hint( + f"{shortcut('↑↓')} navigate {shortcut('Enter')} save & exit " + f"{shortcut('Esc')} cancel" + ), + classes="settings-help", ) def focus(self, scroll_visible: bool = True) -> ProxySetupApp: diff --git a/vibe/cli/textual_ui/widgets/question_app.py b/vibe/cli/textual_ui/widgets/question_app.py index 6cfe2f8..3857a66 100644 --- a/vibe/cli/textual_ui/widgets/question_app.py +++ b/vibe/cli/textual_ui/widgets/question_app.py @@ -12,6 +12,7 @@ from textual.message import Message from textual.reactive import reactive from textual.widgets import Input +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.cli.textual_ui.widgets.vim_navigation import VimNavigationMixin from vibe.cli.textual_ui.widgets.vscode_compat import VscodeCompatInput @@ -29,8 +30,6 @@ from vibe.core.tools.builtins.ask_user_question import Answer class QuestionApp(VimNavigationMixin, Container): - MAX_OPTIONS: ClassVar[int] = 4 - can_focus = True can_focus_children = False @@ -56,6 +55,7 @@ class QuestionApp(VimNavigationMixin, Container): super().__init__(id="question-app") self.args = args self.questions = args.questions + self.max_options = max(len(q.options) for q in self.questions) self.answers: dict[int, tuple[str, bool]] = {} self.multi_selections: dict[int, set[int]] = {} @@ -122,7 +122,7 @@ class QuestionApp(VimNavigationMixin, Container): self.title_widget = NoMarkupStatic("", classes="question-title") yield self.title_widget - for _ in range(self.MAX_OPTIONS): + for _ in range(self.max_options): widget = NoMarkupStatic("", classes="question-option") self.option_widgets.append(widget) yield widget @@ -308,12 +308,18 @@ class QuestionApp(VimNavigationMixin, Container): if not self.help_widget: return if self._current_question.multi_select: - help_text = "↑↓/jk navigate Enter toggle Esc cancel" + help_text = ( + f"{shortcut('↑↓/jk')} navigate {shortcut('Enter')} toggle " + f"{shortcut('Esc')} cancel" + ) else: - help_text = "↑↓/jk navigate Enter select Esc cancel" + help_text = ( + f"{shortcut('↑↓/jk')} navigate {shortcut('Enter')} select " + f"{shortcut('Esc')} cancel" + ) if len(self.questions) > 1: - help_text = "←→ questions " + help_text - self.help_widget.update(help_text) + help_text = f"{shortcut('←→')} questions {help_text}" + self.help_widget.update(shortcut_hint(help_text)) def _store_other_text(self) -> None: if self.other_input: diff --git a/vibe/cli/textual_ui/widgets/rewind_app.py b/vibe/cli/textual_ui/widgets/rewind_app.py index 1524a45..198aa6f 100644 --- a/vibe/cli/textual_ui/widgets/rewind_app.py +++ b/vibe/cli/textual_ui/widgets/rewind_app.py @@ -11,6 +11,7 @@ from textual.message import Message from textual.widgets import Static from vibe.cli.commands import ALT_KEY +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.cli.textual_ui.widgets.vim_navigation import VimNavigationMixin @@ -86,7 +87,11 @@ class RewindApp(VimNavigationMixin, Container): yield widget yield NoMarkupStatic("") yield NoMarkupStatic( - f"{ALT_KEY}+↑↓ or Ctrl+P/N browse messages ↑↓/jk pick option Enter confirm ESC cancel", + shortcut_hint( + f"{shortcut(f'{ALT_KEY}+↑↓')} or {shortcut('Ctrl+P/N')} " + f"browse messages {shortcut('↑↓/jk')} pick option " + f"{shortcut('Enter')} confirm {shortcut('Esc')} cancel" + ), classes="rewind-help", ) diff --git a/vibe/cli/textual_ui/widgets/session_picker.py b/vibe/cli/textual_ui/widgets/session_picker.py index 83c3b9f..08314c1 100644 --- a/vibe/cli/textual_ui/widgets/session_picker.py +++ b/vibe/cli/textual_ui/widgets/session_picker.py @@ -12,6 +12,7 @@ from textual.message import Message from textual.widgets import OptionList from textual.widgets.option_list import Option +from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE, shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.navigable_option_list import NavigableOptionList from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.session.resume_sessions import ResumeSessionInfo, short_session_id @@ -79,7 +80,7 @@ class SessionPickerApp(Container): BINDINGS: ClassVar[list[BindingType]] = [ Binding("escape", "cancel", "Cancel", show=False), - Binding("d,D", "request_delete", "Delete", show=False), + Binding("d", "request_delete", "Delete", show=False), ] class SessionSelected(Message): @@ -164,7 +165,9 @@ class SessionPickerApp(Container): def _delete_confirmation_option_text(self, session: ResumeSessionInfo) -> Text: text = _build_option_text(session, "") - text.append("Press D again to delete") + text.append("Press ") + text.append("d", style=SHORTCUT_STYLE) + text.append(" again to delete") return text def _delete_feedback_option_text(self, session: ResumeSessionInfo) -> Text: @@ -282,7 +285,10 @@ class SessionPickerApp(Container): ) yield NavigableOptionList(*options, id="sessionpicker-options") yield NoMarkupStatic( - "↑↓/jk Navigate Enter Select D Delete Esc Cancel", + shortcut_hint( + f"{shortcut('↑↓/jk')} Navigate {shortcut('Enter')} Select " + f"{shortcut('d')} Delete {shortcut('Esc')} Cancel" + ), classes="sessionpicker-help", ) diff --git a/vibe/cli/textual_ui/widgets/theme_picker.py b/vibe/cli/textual_ui/widgets/theme_picker.py index 73ce2ce..e081987 100644 --- a/vibe/cli/textual_ui/widgets/theme_picker.py +++ b/vibe/cli/textual_ui/widgets/theme_picker.py @@ -12,6 +12,7 @@ from textual.timer import Timer from textual.widgets import OptionList from textual.widgets.option_list import Option +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.navigable_option_list import NavigableOptionList from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic @@ -72,7 +73,11 @@ class ThemePickerApp(Container): yield NoMarkupStatic("Select Theme", classes="themepicker-title") yield NavigableOptionList(*options, id="themepicker-options") yield NoMarkupStatic( - "↑↓/jk Preview Enter Select Esc Cancel", classes="themepicker-help" + shortcut_hint( + f"{shortcut('↑↓/jk')} Preview {shortcut('Enter')} Select " + f"{shortcut('Esc')} Cancel" + ), + classes="themepicker-help", ) def on_mount(self) -> None: diff --git a/vibe/cli/textual_ui/widgets/thinking_picker.py b/vibe/cli/textual_ui/widgets/thinking_picker.py index 2033d31..f5183f6 100644 --- a/vibe/cli/textual_ui/widgets/thinking_picker.py +++ b/vibe/cli/textual_ui/widgets/thinking_picker.py @@ -10,6 +10,7 @@ from textual.message import Message from textual.widgets import OptionList from textual.widgets.option_list import Option +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.navigable_option_list import NavigableOptionList from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.config._settings import ThinkingLevel @@ -59,7 +60,10 @@ class ThinkingPickerApp(Container): ) yield NavigableOptionList(*options, id="thinkingpicker-options") yield NoMarkupStatic( - "↑↓/jk Navigate Enter Select Esc Cancel", + shortcut_hint( + f"{shortcut('↑↓/jk')} Navigate {shortcut('Enter')} Select " + f"{shortcut('Esc')} Cancel" + ), classes="thinkingpicker-help", ) diff --git a/vibe/cli/textual_ui/widgets/voice_app.py b/vibe/cli/textual_ui/widgets/voice_app.py index 590d41c..6c014a3 100644 --- a/vibe/cli/textual_ui/widgets/voice_app.py +++ b/vibe/cli/textual_ui/widgets/voice_app.py @@ -9,6 +9,7 @@ from textual.containers import Container, Vertical from textual.message import Message from textual.widgets import Static +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.cli.textual_ui.widgets.vim_navigation import VimNavigationMixin @@ -87,7 +88,11 @@ class VoiceApp(VimNavigationMixin, Container): yield NoMarkupStatic("") self.help_widget = NoMarkupStatic( - "↑↓/jk navigate Space/Enter toggle ESC exit", classes="settings-help" + shortcut_hint( + f"{shortcut('↑↓/jk')} navigate {shortcut('Space/Enter')} toggle " + f"{shortcut('Esc')} exit" + ), + classes="settings-help", ) yield self.help_widget diff --git a/vibe/core/config/_settings.py b/vibe/core/config/_settings.py index cba0cbf..a210437 100644 --- a/vibe/core/config/_settings.py +++ b/vibe/core/config/_settings.py @@ -242,6 +242,7 @@ class VibeConfig(BaseSettings): disable_welcome_banner_animation: bool = False autocopy_to_clipboard: bool = True file_watcher_for_autocomplete: bool = False + ask_confirmation_on_exit: bool = True displayed_workdir: str = "" context_warnings: bool = False voice_mode_enabled: bool = False diff --git a/vibe/core/config/layers/_base.py b/vibe/core/config/layers/_base.py new file mode 100644 index 0000000..990e9dc --- /dev/null +++ b/vibe/core/config/layers/_base.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from abc import abstractmethod +import os +from pathlib import Path +import tempfile +import tomllib + +import tomli_w + +from vibe.core.config.fingerprint import capture_stable_file, create_file_fingerprint +from vibe.core.config.layer import ConfigLayer, RawConfig +from vibe.core.config.types import EMPTY_CONFIG_SNAPSHOT, LayerConfigSnapshot + + +class BaseTomlConfigLayer(ConfigLayer[RawConfig]): + """Shared read/write logic for TOML file-backed config layers. + + Subclasses only resolve ``_target_path``; this base reads the file into a + snapshot and persists patches atomically. + """ + + @property + @abstractmethod + def _target_path(self) -> Path: + """The TOML file this layer reads from and writes to.""" + ... + + async def _build_config_snapshot(self) -> LayerConfigSnapshot: + path = self._target_path + if not path.exists(): + return EMPTY_CONFIG_SNAPSHOT + + with capture_stable_file(path) as (file, fingerprint): + data = tomllib.load(file) + + return LayerConfigSnapshot(data=data, fingerprint=fingerprint) + + async def _save_to_store(self, next_config: RawConfig) -> str: + path = self._target_path + path.parent.mkdir(parents=True, exist_ok=True) + + tmp_path: Path | None = None + try: + with tempfile.NamedTemporaryFile( + mode="wb", + dir=path.parent, + prefix=f".{path.name}.", + suffix=".tmp", + delete=False, + ) as tmp_file: + tmp_path = Path(tmp_file.name) + tomli_w.dump(next_config.model_dump(), tmp_file) + tmp_file.flush() # Flush Python buffers. + os.fsync(tmp_file.fileno()) # Flush OS buffers. + fingerprint = create_file_fingerprint(tmp_file) + + tmp_path.replace(path) + tmp_path = None + finally: + if tmp_path is not None: + tmp_path.unlink(missing_ok=True) + + return fingerprint diff --git a/vibe/core/config/layers/project.py b/vibe/core/config/layers/project.py index 807ee44..37a8405 100644 --- a/vibe/core/config/layers/project.py +++ b/vibe/core/config/layers/project.py @@ -2,16 +2,14 @@ from __future__ import annotations import asyncio from pathlib import Path -import tomllib -from vibe.core.config.fingerprint import capture_stable_file -from vibe.core.config.layer import ConfigLayer, RawConfig -from vibe.core.config.types import EMPTY_CONFIG_SNAPSHOT, LayerConfigSnapshot +from vibe.core.config.layer import RawConfig +from vibe.core.config.layers._base import BaseTomlConfigLayer from vibe.core.paths._vibe_home import VIBE_HOME from vibe.core.trusted_folders import trusted_folders_manager -class ProjectConfigLayer(ConfigLayer[RawConfig]): +class ProjectConfigLayer(BaseTomlConfigLayer): """Reads a project-level TOML config file. If no file is found in the current working directory, walks up parent directories until a trusted .vibe/config.toml is found. @@ -32,6 +30,18 @@ class ProjectConfigLayer(ConfigLayer[RawConfig]): def is_file_discovered(self) -> bool: return self._is_set and self._config_file_path is not None + @property + def _target_path(self) -> Path: + return self._config_file_path or (self._root / ".vibe" / "config.toml") + + async def _save_to_store(self, next_config: RawConfig) -> str: + fingerprint = await super()._save_to_store(next_config) + # A persist may have created the file we hadn't discovered; adopt it so + # discovery state and trust transitions track the on-disk file. + self._config_file_path = self._target_path + self._is_set = True + return fingerprint + async def _check_trust(self) -> bool: await self._find_config_file() @@ -40,15 +50,6 @@ class ProjectConfigLayer(ConfigLayer[RawConfig]): return bool(trusted_folders_manager.is_trusted(self._config_file_path.parent)) - async def _build_config_snapshot(self) -> LayerConfigSnapshot: - if self._config_file_path is None or not self._config_file_path.exists(): - return EMPTY_CONFIG_SNAPSHOT - - with capture_stable_file(self._config_file_path) as (file, fingerprint): - data = tomllib.load(file) - - return LayerConfigSnapshot(data=data, fingerprint=fingerprint) - async def _on_trust_changed(self, old: bool | None, new: bool | None) -> None: if new is None or self._config_file_path is None: return @@ -72,11 +73,6 @@ class ProjectConfigLayer(ConfigLayer[RawConfig]): await super().revoke_trust() - async def _save_to_store(self, _next_config: RawConfig) -> str: - raise NotImplementedError( - "ProjectConfigLayer patch persistence is not implemented yet" - ) - async def _find_config_file(self) -> None: async with self._find_lock: if self._is_set: diff --git a/vibe/core/config/layers/user.py b/vibe/core/config/layers/user.py index 2b7c26b..5369590 100644 --- a/vibe/core/config/layers/user.py +++ b/vibe/core/config/layers/user.py @@ -1,19 +1,12 @@ from __future__ import annotations -import os from pathlib import Path -import tempfile -import tomllib -import tomli_w - -from vibe.core.config.fingerprint import capture_stable_file, create_file_fingerprint -from vibe.core.config.layer import ConfigLayer, RawConfig -from vibe.core.config.types import EMPTY_CONFIG_SNAPSHOT, LayerConfigSnapshot +from vibe.core.config.layers._base import BaseTomlConfigLayer from vibe.core.paths._vibe_home import VIBE_HOME -class UserConfigLayer(ConfigLayer[RawConfig]): +class UserConfigLayer(BaseTomlConfigLayer): """Reads the user-level TOML config file. Always trusted. Defaults to ``~/.vibe/config.toml`` (via VIBE_HOME). @@ -24,40 +17,9 @@ class UserConfigLayer(ConfigLayer[RawConfig]): super().__init__(name=name) self._path = path or (VIBE_HOME.path / "config.toml") + @property + def _target_path(self) -> Path: + return self._path + async def _check_trust(self) -> bool: return True - - async def _build_config_snapshot(self) -> LayerConfigSnapshot: - if not self._path.exists(): - return EMPTY_CONFIG_SNAPSHOT - - with capture_stable_file(self._path) as (file, fingerprint): - data = tomllib.load(file) - - return LayerConfigSnapshot(data=data, fingerprint=fingerprint) - - async def _save_to_store(self, next_config: RawConfig) -> str: - self._path.parent.mkdir(parents=True, exist_ok=True) - - tmp_path: Path | None = None - try: - with tempfile.NamedTemporaryFile( - mode="wb", - dir=self._path.parent, - prefix=f".{self._path.name}.", - suffix=".tmp", - delete=False, - ) as tmp_file: - tmp_path = Path(tmp_file.name) - tomli_w.dump(next_config.model_dump(), tmp_file) - tmp_file.flush() # Flush Python buffers. - os.fsync(tmp_file.fileno()) # Flush OS buffers. - fingerprint = create_file_fingerprint(tmp_file) - - tmp_path.replace(self._path) - tmp_path = None - finally: - if tmp_path is not None: - tmp_path.unlink(missing_ok=True) - - return fingerprint diff --git a/vibe/core/config/vibe_schema.py b/vibe/core/config/vibe_schema.py index a297770..be7cd3a 100644 --- a/vibe/core/config/vibe_schema.py +++ b/vibe/core/config/vibe_schema.py @@ -261,6 +261,7 @@ class VibeConfigSchema(ConfigSchema): disable_welcome_banner_animation: Annotated[bool, WithReplaceMerge()] = False autocopy_to_clipboard: Annotated[bool, WithReplaceMerge()] = True file_watcher_for_autocomplete: Annotated[bool, WithReplaceMerge()] = False + ask_confirmation_on_exit: Annotated[bool, WithReplaceMerge()] = True displayed_workdir: Annotated[str, WithReplaceMerge()] = "" context_warnings: Annotated[bool, WithReplaceMerge()] = False voice_mode_enabled: Annotated[bool, WithReplaceMerge()] = False diff --git a/vibe/core/skills/builtins/vibe.py b/vibe/core/skills/builtins/vibe.py index 85cf216..26c0091 100644 --- a/vibe/core/skills/builtins/vibe.py +++ b/vibe/core/skills/builtins/vibe.py @@ -63,7 +63,9 @@ When in a trusted folder, Vibe also looks for project-local configuration: Chat input (case-insensitive): `/exit`, `exit`, `quit`, `:q`, `:quit`. Keyboard: `Ctrl+C` / `Ctrl+D` — press twice within ~1s to quit. For `Ctrl+C`, the first press instead interrupts the running job or clears the input if either -is present. `Ctrl+Z` suspends on POSIX (resume with `fg`). +is present. Set `ask_confirmation_on_exit = false` to make `Ctrl+D` quit on the +first press (also toggleable in `/config`); `Ctrl+C` always requires a second +press. `Ctrl+Z` suspends on POSIX (resume with `fg`). ### Update @@ -114,6 +116,7 @@ active_model = "mistral-medium-3.5" # Model alias to use (see [[models]]) disable_welcome_banner_animation = false autocopy_to_clipboard = true file_watcher_for_autocomplete = false +ask_confirmation_on_exit = true # Require a second Ctrl+D to quit (Ctrl+C always confirms) # Behavior bypass_tool_permissions = false # Skip tool approval prompts @@ -603,7 +606,7 @@ Custom agents are TOML files in `~/.vibe/agents/NAME.toml`. - `/mcp logout ` - Log out from an MCP server and delete stored OAuth secrets - `/resume` (or `/continue`) - Browse and resume past sessions for the current - folder. The picker header shows the folder being listed. Press `D` twice to + folder. The picker header shows the folder being listed. Press `d` twice to delete a saved session; the active session cannot be deleted here. - `/rewind` - Rewind to a previous message - `/loop ` - Schedule a recurring prompt (e.g. `/loop 30s ping`). diff --git a/vibe/core/teleport/git.py b/vibe/core/teleport/git.py index c1b2718..1e98ac2 100644 --- a/vibe/core/teleport/git.py +++ b/vibe/core/teleport/git.py @@ -2,6 +2,8 @@ from __future__ import annotations from dataclasses import dataclass from pathlib import Path +import shutil +import tempfile from git import InvalidGitRepositoryError, Repo from git.exc import GitCommandError @@ -154,13 +156,21 @@ class GitRepository: async def _get_diff(self, repo: Repo) -> str: def get_full_diff() -> str: - # Mark untracked files as intent-to-add so they appear in diff - repo.git.add("-N", ".") - return repo.git.diff("HEAD", binary=True) + temporary_dir = Path(tempfile.mkdtemp(prefix="vibe-teleport-index-")) + temporary_index = temporary_dir / "index" + try: + index_path = Path(repo.index.path) + if index_path.exists(): + shutil.copy2(index_path, temporary_index) + with repo.git.custom_environment(GIT_INDEX_FILE=str(temporary_index)): + repo.git.add("-N", ".") + return repo.git.diff("HEAD", binary=True) + finally: + shutil.rmtree(temporary_dir, ignore_errors=True) try: return await self._executor.run(get_full_diff) - except (TimeoutError, GitCommandError): + except (TimeoutError, GitCommandError, OSError): return "" async def _branch_contains(self, repo: Repo, commit: str, remote: str) -> bool: diff --git a/vibe/core/tools/builtins/ask_user_question.py b/vibe/core/tools/builtins/ask_user_question.py index 6c4744f..1c1f0eb 100644 --- a/vibe/core/tools/builtins/ask_user_question.py +++ b/vibe/core/tools/builtins/ask_user_question.py @@ -29,13 +29,12 @@ class Question(BaseModel): question: str = Field(description="The question text") header: str = Field( default="", - description="Short header for the question (1-2 words, e.g. 'Auth', 'Database')", - max_length=12, + description="Short chip/tag label for the question (max 20 chars)", + max_length=20, ) options: list[Choice] = Field( - description="Available options (2-4, not including 'Other'). An 'Other' option for free text is automatically added.", + description="Available options (2-4 recommended, not including 'Other'). An 'Other' option for free text is automatically added.", min_length=2, - max_length=4, ) multi_select: bool = Field( default=False, description="If true, user can select multiple options" @@ -47,9 +46,8 @@ class Question(BaseModel): class AskUserQuestionArgs(BaseModel): questions: list[Question] = Field( - description="Questions to ask (1-4). Displayed as tabs if multiple.", + description="Questions to ask (1-4 recommended). Displayed as tabs if multiple.", min_length=1, - max_length=4, ) footer_note: str | None = Field( default=None, diff --git a/vibe/core/tools/builtins/prompts/ask_user_question.md b/vibe/core/tools/builtins/prompts/ask_user_question.md index dc7636f..c40587a 100644 --- a/vibe/core/tools/builtins/prompts/ask_user_question.md +++ b/vibe/core/tools/builtins/prompts/ask_user_question.md @@ -13,7 +13,7 @@ Use `ask_user_question` to gather information from the user when you need clarif Each question has these fields: - `question`: The full question text (be specific and clear) -- `header`: A short label displayed as a chip (max 12 characters, e.g., "Auth", "Database", "Approach") +- `header`: A short label displayed as a chip (max 20 characters, e.g., "Auth", "Database", "Approach") - `options`: 2-4 choices (an "Other" option is automatically added for free text) - `multi_select`: Set to `true` if user can pick multiple options (default: `false`) @@ -70,7 +70,7 @@ Each option has: ## Key Constraints -- **Header max length**: 12 characters (keeps UI clean) +- **Header max length**: 20 characters (keeps UI clean) - **Options count**: 2-4 per question (plus automatic "Other") - **Questions count**: 1-4 per call - **Label length**: Keep to 1-5 words for readability diff --git a/vibe/core/tools/mcp/registry.py b/vibe/core/tools/mcp/registry.py index cdfd372..1dcb12e 100644 --- a/vibe/core/tools/mcp/registry.py +++ b/vibe/core/tools/mcp/registry.py @@ -334,6 +334,9 @@ class MCPRegistry: self._oauth_locks[alias] = asyncio.Lock() return self._oauth_locks[alias] + def disabled_aliases(self) -> set[str]: + return {alias for alias, srv in self._servers_by_alias.items() if srv.disabled} + def status(self) -> dict[str, AuthStatus]: statuses: dict[str, AuthStatus] = {} for alias, srv in self._servers_by_alias.items(): diff --git a/vibe/setup/onboarding/screens/api_key.py b/vibe/setup/onboarding/screens/api_key.py index e72aa7b..3a604ee 100644 --- a/vibe/setup/onboarding/screens/api_key.py +++ b/vibe/setup/onboarding/screens/api_key.py @@ -10,6 +10,7 @@ from textual.validation import Length from textual.widgets import Input, Link from vibe.cli.clipboard import copy_selection_to_clipboard +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.banner.petit_chat import PetitChat from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.config import DEFAULT_VIBE_BASE_URL, ProviderConfig @@ -110,7 +111,7 @@ class ApiKeyScreen(OnboardingScreen): feedback.remove_class("error", "success") if event.validation_result.is_valid: - feedback.update("Press Enter to submit ↵") + feedback.update(shortcut_hint(f"Press {shortcut('Enter')} to submit ↵")) feedback.add_class("success") input_box.add_class("valid") return diff --git a/vibe/setup/onboarding/screens/auth_method.py b/vibe/setup/onboarding/screens/auth_method.py index 3f69d5a..437074c 100644 --- a/vibe/setup/onboarding/screens/auth_method.py +++ b/vibe/setup/onboarding/screens/auth_method.py @@ -7,6 +7,7 @@ from textual.app import ComposeResult from textual.binding import Binding, BindingType from textual.containers import Center, Horizontal, Vertical +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.banner.petit_chat import PetitChat from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.config import ProviderConfig @@ -120,4 +121,9 @@ class AuthMethodScreen(OnboardingScreen): marker.add_class("selected") widget.add_class("selected") - self._help_widget.update("Use arrows to navigate - Enter Select - Esc Cancel") + self._help_widget.update( + shortcut_hint( + f"Use {shortcut('↑↓')} to navigate - {shortcut('Enter')} Select - " + f"{shortcut('Esc')} Cancel" + ) + ) diff --git a/vibe/setup/onboarding/screens/browser_sign_in.py b/vibe/setup/onboarding/screens/browser_sign_in.py index 9fd829d..3f48a7a 100644 --- a/vibe/setup/onboarding/screens/browser_sign_in.py +++ b/vibe/setup/onboarding/screens/browser_sign_in.py @@ -16,6 +16,7 @@ from textual.timer import Timer from textual.widgets import Static from textual.worker import Worker +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.banner.petit_chat import PetitChat from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.config import ProviderConfig @@ -37,12 +38,17 @@ from vibe.setup.auth.api_key_persistence import ( from vibe.setup.onboarding.base import OnboardingScreen from vibe.setup.onboarding.gradient_text import GRADIENT_COLORS, append_gradient_text -PENDING_HINT = "Press M to enter API key manually - Esc to cancel" -ERROR_HINT = "Press R to retry - Press M to enter API key manually - Esc to cancel" +PENDING_HINT = ( + f"Press {shortcut('m')} to enter API key manually - {shortcut('Esc')} to cancel" +) +ERROR_HINT = ( + f"Press {shortcut('r')} to retry - Press {shortcut('m')} " + f"to enter API key manually - {shortcut('Esc')} to cancel" +) SUCCESS_HINT = "Finishing setup..." SIGN_IN_URL_HELP_PREFIX = "If your browser did not open, " SIGN_IN_URL_COPY_LABEL = "copy this URL" -SIGN_IN_URL_HELP_SUFFIX = " (press C)." +SIGN_IN_URL_HELP_SUFFIX = f" (press {shortcut('c')})." SIGN_IN_URL_REVEAL_PREFIX = "Copy failed. Open this URL manually:" SUCCESS_EXIT_DELAY_SECONDS: float = 2.0 SIGN_IN_URL_HELP_DELAY_SECONDS: float = 4.0 @@ -389,7 +395,7 @@ class BrowserSignInScreen(OnboardingScreen): if not self.is_mounted: return - self._hint_widget.update(state.hint) + self._hint_widget.update(shortcut_hint(state.hint)) self._url_widget.update(self._build_url_text(state)) for index, (widgets, (title, pending_detail, done_detail)) in enumerate( @@ -487,7 +493,7 @@ class BrowserSignInScreen(OnboardingScreen): help_text = ( f"{escape(SIGN_IN_URL_HELP_PREFIX)}" f"[@click='screen.copy_url']{escape(SIGN_IN_URL_COPY_LABEL)}[/]" - f"{escape(SIGN_IN_URL_HELP_SUFFIX)}" + f"{SIGN_IN_URL_HELP_SUFFIX}" ) if not state.reveal_sign_in_url: return help_text diff --git a/vibe/setup/onboarding/screens/theme_selection.py b/vibe/setup/onboarding/screens/theme_selection.py index 2de054c..90512c0 100644 --- a/vibe/setup/onboarding/screens/theme_selection.py +++ b/vibe/setup/onboarding/screens/theme_selection.py @@ -8,6 +8,7 @@ from textual.containers import Center, Container, Horizontal, Vertical from textual.events import Resize from textual.widgets import Markdown, Static +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.theme_picker import sorted_theme_names from vibe.core.config import MissingAPIKeyError, VibeConfig from vibe.core.logger import logger @@ -73,9 +74,14 @@ class ThemeSelectionScreen(OnboardingScreen): yield Static("Select your preferred theme", id="theme-title") yield Center( Horizontal( - Static("Navigate ↑ ↓", id="nav-hint"), + Static( + shortcut_hint(f"Navigate {shortcut('↑↓')}"), id="nav-hint" + ), Vertical(*self._compose_theme_list(), id="theme-list"), - Static("Press Enter ↵", id="enter-hint"), + Static( + shortcut_hint(f"Press {shortcut('Enter')} ↵"), + id="enter-hint", + ), id="theme-row", ) ) diff --git a/vibe/setup/onboarding/screens/welcome.py b/vibe/setup/onboarding/screens/welcome.py index a1dda46..10cf33f 100644 --- a/vibe/setup/onboarding/screens/welcome.py +++ b/vibe/setup/onboarding/screens/welcome.py @@ -8,6 +8,7 @@ from textual.containers import Center, Vertical from textual.timer import Timer from textual.widgets import Static +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.setup.onboarding.base import OnboardingScreen from vibe.setup.onboarding.gradient_text import GRADIENT_COLORS, gradient_markup @@ -21,6 +22,7 @@ HIGHLIGHT_START = len(WELCOME_PREFIX) HIGHLIGHT_END = HIGHLIGHT_START + len(WELCOME_HIGHLIGHT) BUTTON_TEXT = "Press Enter ↵" +BUTTON_TEXT_MARKUP = f"Press {shortcut('Enter')} ↵" class WelcomeScreen(OnboardingScreen): @@ -106,6 +108,9 @@ class WelcomeScreen(OnboardingScreen): self._button_typing_timer.stop() return self._button_char_index += 1 + if self._button_char_index >= len(BUTTON_TEXT): + self._enter_hint.update(shortcut_hint(BUTTON_TEXT_MARKUP)) + return self._enter_hint.update(BUTTON_TEXT[: self._button_char_index]) def _animate_gradient(self) -> None: diff --git a/vibe/setup/trusted_folders/trust_folder_dialog.py b/vibe/setup/trusted_folders/trust_folder_dialog.py index ebb855e..64e4b64 100644 --- a/vibe/setup/trusted_folders/trust_folder_dialog.py +++ b/vibe/setup/trusted_folders/trust_folder_dialog.py @@ -16,6 +16,7 @@ from textual.containers import ( from textual.message import Message from textual.widgets import Static +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.core.paths import TRUSTED_FOLDERS_FILE from vibe.core.trusted_folders import WorkspaceTrustDecision @@ -166,7 +167,10 @@ class TrustFolderDialog(CenterMiddle): yield widget yield NoMarkupStatic( - "← → navigate Enter select", classes="trust-dialog-help" + shortcut_hint( + f"{shortcut('←→')} navigate {shortcut('Enter')} select" + ), + classes="trust-dialog-help", ) yield NoMarkupStatic( diff --git a/vibe/setup/update_prompt/update_prompt_dialog.py b/vibe/setup/update_prompt/update_prompt_dialog.py index 01c88fb..c798c79 100644 --- a/vibe/setup/update_prompt/update_prompt_dialog.py +++ b/vibe/setup/update_prompt/update_prompt_dialog.py @@ -11,6 +11,7 @@ from textual.binding import Binding, BindingType from textual.containers import CenterMiddle, Horizontal from textual.message import Message +from vibe.cli.textual_ui.shortcut_hints import shortcut, shortcut_hint from vibe.cli.textual_ui.widgets.banner.petit_chat import PetitChat from vibe.cli.textual_ui.widgets.no_markup_static import NoMarkupStatic from vibe.cli.update_notifier.update import do_update @@ -97,7 +98,8 @@ class UpdatePromptDialog(CenterMiddle): yield widget yield NoMarkupStatic( - "← → navigate Enter select", classes="update-dialog-help" + shortcut_hint(f"{shortcut('←→')} navigate {shortcut('Enter')} select"), + classes="update-dialog-help", ) yield PetitChat(id="update-dialog-spinner")