usage-cli 0.32.0__tar.gz → 0.32.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {usage_cli-0.32.0 → usage_cli-0.32.2}/PKG-INFO +31 -3
- {usage_cli-0.32.0 → usage_cli-0.32.2}/README.md +30 -2
- {usage_cli-0.32.0 → usage_cli-0.32.2}/i18n.json +60 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/installer/claude_pane.py +28 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/main.py +5 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/pyproject.toml +1 -1
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_claude_pane.py +53 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_main.py +13 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_cli.py +36 -1
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_wintray.py +88 -1
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_cli.egg-info/PKG-INFO +31 -3
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_cli.py +14 -7
- {usage_cli-0.32.0 → usage_cli-0.32.2}/wintray/app.py +61 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/wintray/menu.py +2 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/LICENSE +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/agy.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/claude.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/codex.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/grok.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/muse.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/rate_limits.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/registry.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/adapters/types.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/aggregator.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/blocks.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/diagnoser.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/dispatch_ledger.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/insights.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/persona_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/reporter.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/subscription.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/analyzer/usage_snapshot.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/doctor.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/fsevents_watch.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/i18n.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/installer/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/installer/login_item.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/installer/session_hooks.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/installer/setup_hook.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/installer/statusline_settings.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/agy_disk_cache.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/agy_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/agy_quota_probe.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/cache_quarantine.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/claude_paths.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/codex_disk_cache.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/codex_events.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/codex_fork_replay.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/codex_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/codex_paths.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/disk_cache_common.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/disk_cache_lifecycle.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/grok_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/grok_quota_probe.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/history_disk_cache.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/history_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/jsonl_limits.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/jsonl_utils.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/loaders/muse_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/actions.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/agy.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/app.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/chrome.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/grok.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/menu.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/new_badge.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/notify.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/popover.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/prefs.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/refresh.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/state.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/title.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/menubar/update.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/base.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/dynamic_height.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/panel_scale.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/panel_window.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/panel_window_state.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/payload.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/web_panel.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/panels/window_drag.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/prefs.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/pricing.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/project_resolver.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/quota/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/quota/agy_window_keeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/quota/burn_rate.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/quota/codex_window_keeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/quota/usage_rate.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/quota/window_keeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/service_status.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/setup.cfg +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/setup_app.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_adapters_claude.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_adapters_codex.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_adapters_rate_limits.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_aggregator.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_agy_adapter.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_agy_disk_cache.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_agy_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_agy_quota_probe.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_agy_window_keeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_analyzer_blocks.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_analyzer_pipeline.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_burn_rate.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_cache_quarantine.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_claude_paths.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_codex_consistency.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_codex_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_codex_window_keeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_diagnoser.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_disk_cache_shards.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_dispatch_ledger.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_doctor.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_dynamic_height.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_fsevents_watch.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_grok_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_grok_quota_probe.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_history_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_hook_scripts_are_standalone.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_html_report_snapshot.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_i18n_format_fallback.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_i18n_key_parity.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_i18n_packaged_path.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_insights.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_jsonl_utils.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_login_item.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_menubar.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_menubar_agy.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_menubar_grok.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_menubar_notify.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_menubar_prefs.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_menubar_state.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_muse_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_new_badge.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_packaged_resources.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_packaging.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_panel_footer_layout.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_panel_scale.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_panel_window_state.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_panels.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_panels_lazy_import.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_persona_loader.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_pricing.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_project_resolver.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_report_daily_chart.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_report_filter_js.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_reporter_build_data.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_service_status.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_session_resume.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_setup_app.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_setup_hook.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_setup_hook_coexistence.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_setup_hook_resume.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_setup_hook_terse.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_subprocess_utils.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_subscription.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_time_utils.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_tui.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_tui_sprite.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_ui_tables.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_update_checker.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_update_gate.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_update_release_notes.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_client.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_diagnosis_snapshot.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_dir_sweeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_lang.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_logging.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_notifications.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_rate.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_snapshot.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_snapshot_replay.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_statusline.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_statusline_agy.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_statusline_forwarder.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_statusline_grok.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_terse_mode.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_usage_terse_reminder.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_web_panel_payload.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_wheel_packaging.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_win_login_item.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_window_keeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tests/test_windows_manifest.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tui/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tui/app.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/tui/sprite.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/html_report.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/report_charts.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/report_daily_chart.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/report_filter.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/report_ledger.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/report_scripts.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/report_styles.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/ui/tables.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/updates/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/updates/checker.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/updates/gate.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/updates/release_notes.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_cli.egg-info/SOURCES.txt +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_cli.egg-info/dependency_links.txt +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_cli.egg-info/entry_points.txt +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_cli.egg-info/requires.txt +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_cli.egg-info/top_level.txt +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_client.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_common/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_common/subprocess_utils.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_common/time_utils.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_common/usage_dir_sweeper.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_common/usage_lang.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_common/usage_logging.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_diagnosis_snapshot.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_notifications.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_session_resume.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_statusline.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_statusline_agy.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_statusline_forwarder.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_statusline_grok.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_terse_mode.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/usage_terse_reminder.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/wintray/__init__.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/wintray/login_item.py +0 -0
- {usage_cli-0.32.0 → usage_cli-0.32.2}/wintray/watch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usage-cli
|
|
3
|
-
Version: 0.32.
|
|
3
|
+
Version: 0.32.2
|
|
4
4
|
Summary: usage — 在 macOS menu bar 與 Windows 系統匣顯示 Claude Code、Codex、Antigravity 用量的小工具(也提供終端機 TUI)
|
|
5
5
|
License-Expression: AGPL-3.0-only
|
|
6
6
|
Requires-Python: >=3.13
|
|
@@ -57,7 +57,7 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
57
57
|
|
|
58
58
|
**Not on macOS?** `uvx usage-cli` runs the terminal interface anywhere, Linux included — no install, no menu bar.
|
|
59
59
|
|
|
60
|
-
**Jump to:** [What You Get](#what-you-get) · [Privacy](#privacy--data-sources) · [Requirements](#requirements) · [Install](#install) · [Status Line](#first-launch-set-up-the-status-line) · [Windows](#windows-support) · [Themes](#theme-gallery) · [Troubleshooting](#troubleshooting) · [Comparison](#comparison) · [Not a Fit?](#when-usage-isnt-the-right-fit) · [Development](#development)
|
|
60
|
+
**Jump to:** [What You Get](#what-you-get) · [Privacy](#privacy--data-sources) · [Requirements](#requirements) · [Install](#install) · [Status Line](#first-launch-set-up-the-status-line) · [Claude Code Side Pane](#claude-code-side-pane) · [Windows](#windows-support) · [Themes](#theme-gallery) · [Troubleshooting](#troubleshooting) · [Comparison](#comparison) · [Not a Fit?](#when-usage-isnt-the-right-fit) · [Development](#development)
|
|
61
61
|
|
|
62
62
|
## What You Get
|
|
63
63
|
|
|
@@ -76,7 +76,7 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
76
76
|
|
|
77
77
|
- **Progress Concierge:** Open a new Claude Code session and `usage` hands your last progress straight to the AI, including your last request, uncommitted changes, and unfinished todos. No `/resume`, no recap. When you do `/resume` a conversation that sat long enough for its cache to expire, it warns you how many tokens the next message will re-send and suggests `/compact` first. Off by default.
|
|
78
78
|
- **Token Saver:** A menu-bar toggle asks Claude Code and Codex to answer more tersely and in plainer language, saving output tokens while keeping code and error messages byte-exact. A light reminder keeps long conversations from drifting back to verbose — in an A/B test on real sessions, late replies stayed ~40% shorter instead of drifting 84% longer.
|
|
79
|
-
- **Claude Code side pane (macOS):**
|
|
79
|
+
- **Claude Code side pane (macOS and Windows):** Quotas, conversations, and background jobs inside Claude Code. [See the side pane](#claude-code-side-pane).
|
|
80
80
|
- **Auto-start 5-hour Session:** Off by default. Turn it on and, right after a 5-hour quota resets, `usage` sends one tiny message to each tool (Claude with Haiku, Antigravity with Gemini 3.5 Flash Low, Codex with its cheapest model) so the next 5-hour window starts counting right away. Those messages do use a little quota, but the amount is negligible. Checking your quota never sends a message; only this switch does.
|
|
81
81
|
- **Terminal Integration:** `usage status --json` hands your Claude Code, Codex, Antigravity, and Grok quota to any tool that can run a command — Starship, tmux, or your own scripts. Reads the same local files as the menu bar. [Ready-made snippets](docs/DEVELOPMENT.md#quota-status-for-other-tools-usage-status).
|
|
82
82
|
- **Token-waste Health Check:** A daily background diagnosis scans your logs for waste, including repeated file reads, polluter directories, and noisy Bash output. If it finds issues, a one-line heads-up appears; say "show me" and the AI walks you through fixes.
|
|
@@ -147,6 +147,34 @@ Once set up, the bottom of the Claude Code window will show a status line like t
|
|
|
147
147
|
<img src="docs/statusline.en.gif" alt="Claude Code statusLine display (English)" width="900">
|
|
148
148
|
</p>
|
|
149
149
|
|
|
150
|
+
## Claude Code Side Pane
|
|
151
|
+
|
|
152
|
+
See your quota, other conversations, and background jobs without leaving Claude Code. Available on macOS and Windows.
|
|
153
|
+
|
|
154
|
+
<p align="center"><img src="docs/side-pane.png" alt="Claude Code side pane showing quotas, conversations, and background jobs" width="637"></p>
|
|
155
|
+
|
|
156
|
+
**What you’ll see**
|
|
157
|
+
|
|
158
|
+
- **Quotas:** Your 5-hour and weekly limits.
|
|
159
|
+
- **Claude conversations:** Conversations waiting for your permission or MCP input are marked in yellow.
|
|
160
|
+
- **Background jobs:** Includes subagents started by Claude with the Agent tool and their status.
|
|
161
|
+
|
|
162
|
+
**How to enable**
|
|
163
|
+
|
|
164
|
+
1. In the usage menu bar menu (macOS) or the system-tray panel menu (Windows), check **Claude Code side pane**.
|
|
165
|
+
2. Open a new conversation or run `/reload-plugins`.
|
|
166
|
+
3. At terminal widths ≥144 columns, the pane opens on the right automatically. In narrower terminals, enter `/usage-dash`.
|
|
167
|
+
|
|
168
|
+
<details>
|
|
169
|
+
<summary>Compatibility and updates</summary>
|
|
170
|
+
|
|
171
|
+
- Requires a recent Claude Code with mod support; tested with 2.1.289.
|
|
172
|
+
- The pane docks on the right only in Claude Code's fullscreen layout; otherwise it appears above the prompt. On Windows, enabling the pane turns the fullscreen layout on as well, and turning the pane off switches it back.
|
|
173
|
+
- The built-in macOS Terminal supports only 256 colors and may show a gray background. Select an ANSI dark theme in `/config`.
|
|
174
|
+
- When the usage app starts, it automatically updates an enabled pane to the bundled version.
|
|
175
|
+
|
|
176
|
+
</details>
|
|
177
|
+
|
|
150
178
|
## Windows Support
|
|
151
179
|
|
|
152
180
|
Windows has the full core experience: the system-tray UI, Claude Code status-line hook, and Codex history parsing all work natively. Download `usage-windows.zip` from the [latest GitHub Release](https://github.com/aqua5230/usage/releases/latest), unzip it, then run `usage.exe`—no installer is needed. If SmartScreen shows **Windows protected your PC** on first launch, click **More info** → **Run anyway**. The tray UI requires Microsoft Edge WebView2 Runtime, which is normally included with Windows 10 and 11.
|
|
@@ -37,7 +37,7 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
37
37
|
|
|
38
38
|
**Not on macOS?** `uvx usage-cli` runs the terminal interface anywhere, Linux included — no install, no menu bar.
|
|
39
39
|
|
|
40
|
-
**Jump to:** [What You Get](#what-you-get) · [Privacy](#privacy--data-sources) · [Requirements](#requirements) · [Install](#install) · [Status Line](#first-launch-set-up-the-status-line) · [Windows](#windows-support) · [Themes](#theme-gallery) · [Troubleshooting](#troubleshooting) · [Comparison](#comparison) · [Not a Fit?](#when-usage-isnt-the-right-fit) · [Development](#development)
|
|
40
|
+
**Jump to:** [What You Get](#what-you-get) · [Privacy](#privacy--data-sources) · [Requirements](#requirements) · [Install](#install) · [Status Line](#first-launch-set-up-the-status-line) · [Claude Code Side Pane](#claude-code-side-pane) · [Windows](#windows-support) · [Themes](#theme-gallery) · [Troubleshooting](#troubleshooting) · [Comparison](#comparison) · [Not a Fit?](#when-usage-isnt-the-right-fit) · [Development](#development)
|
|
41
41
|
|
|
42
42
|
## What You Get
|
|
43
43
|
|
|
@@ -56,7 +56,7 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
56
56
|
|
|
57
57
|
- **Progress Concierge:** Open a new Claude Code session and `usage` hands your last progress straight to the AI, including your last request, uncommitted changes, and unfinished todos. No `/resume`, no recap. When you do `/resume` a conversation that sat long enough for its cache to expire, it warns you how many tokens the next message will re-send and suggests `/compact` first. Off by default.
|
|
58
58
|
- **Token Saver:** A menu-bar toggle asks Claude Code and Codex to answer more tersely and in plainer language, saving output tokens while keeping code and error messages byte-exact. A light reminder keeps long conversations from drifting back to verbose — in an A/B test on real sessions, late replies stayed ~40% shorter instead of drifting 84% longer.
|
|
59
|
-
- **Claude Code side pane (macOS):**
|
|
59
|
+
- **Claude Code side pane (macOS and Windows):** Quotas, conversations, and background jobs inside Claude Code. [See the side pane](#claude-code-side-pane).
|
|
60
60
|
- **Auto-start 5-hour Session:** Off by default. Turn it on and, right after a 5-hour quota resets, `usage` sends one tiny message to each tool (Claude with Haiku, Antigravity with Gemini 3.5 Flash Low, Codex with its cheapest model) so the next 5-hour window starts counting right away. Those messages do use a little quota, but the amount is negligible. Checking your quota never sends a message; only this switch does.
|
|
61
61
|
- **Terminal Integration:** `usage status --json` hands your Claude Code, Codex, Antigravity, and Grok quota to any tool that can run a command — Starship, tmux, or your own scripts. Reads the same local files as the menu bar. [Ready-made snippets](docs/DEVELOPMENT.md#quota-status-for-other-tools-usage-status).
|
|
62
62
|
- **Token-waste Health Check:** A daily background diagnosis scans your logs for waste, including repeated file reads, polluter directories, and noisy Bash output. If it finds issues, a one-line heads-up appears; say "show me" and the AI walks you through fixes.
|
|
@@ -127,6 +127,34 @@ Once set up, the bottom of the Claude Code window will show a status line like t
|
|
|
127
127
|
<img src="docs/statusline.en.gif" alt="Claude Code statusLine display (English)" width="900">
|
|
128
128
|
</p>
|
|
129
129
|
|
|
130
|
+
## Claude Code Side Pane
|
|
131
|
+
|
|
132
|
+
See your quota, other conversations, and background jobs without leaving Claude Code. Available on macOS and Windows.
|
|
133
|
+
|
|
134
|
+
<p align="center"><img src="docs/side-pane.png" alt="Claude Code side pane showing quotas, conversations, and background jobs" width="637"></p>
|
|
135
|
+
|
|
136
|
+
**What you’ll see**
|
|
137
|
+
|
|
138
|
+
- **Quotas:** Your 5-hour and weekly limits.
|
|
139
|
+
- **Claude conversations:** Conversations waiting for your permission or MCP input are marked in yellow.
|
|
140
|
+
- **Background jobs:** Includes subagents started by Claude with the Agent tool and their status.
|
|
141
|
+
|
|
142
|
+
**How to enable**
|
|
143
|
+
|
|
144
|
+
1. In the usage menu bar menu (macOS) or the system-tray panel menu (Windows), check **Claude Code side pane**.
|
|
145
|
+
2. Open a new conversation or run `/reload-plugins`.
|
|
146
|
+
3. At terminal widths ≥144 columns, the pane opens on the right automatically. In narrower terminals, enter `/usage-dash`.
|
|
147
|
+
|
|
148
|
+
<details>
|
|
149
|
+
<summary>Compatibility and updates</summary>
|
|
150
|
+
|
|
151
|
+
- Requires a recent Claude Code with mod support; tested with 2.1.289.
|
|
152
|
+
- The pane docks on the right only in Claude Code's fullscreen layout; otherwise it appears above the prompt. On Windows, enabling the pane turns the fullscreen layout on as well, and turning the pane off switches it back.
|
|
153
|
+
- The built-in macOS Terminal supports only 256 colors and may show a gray background. Select an ANSI dark theme in `/config`.
|
|
154
|
+
- When the usage app starts, it automatically updates an enabled pane to the bundled version.
|
|
155
|
+
|
|
156
|
+
</details>
|
|
157
|
+
|
|
130
158
|
## Windows Support
|
|
131
159
|
|
|
132
160
|
Windows has the full core experience: the system-tray UI, Claude Code status-line hook, and Codex history parsing all work natively. Download `usage-windows.zip` from the [latest GitHub Release](https://github.com/aqua5230/usage/releases/latest), unzip it, then run `usage.exe`—no installer is needed. If SmartScreen shows **Windows protected your PC** on first launch, click **More info** → **Run anyway**. The tray UI requires Microsoft Edge WebView2 Runtime, which is normally included with Windows 10 and 11.
|
|
@@ -431,8 +431,19 @@
|
|
|
431
431
|
"claude_pane_week": "週",
|
|
432
432
|
"claude_pane_period": "期間",
|
|
433
433
|
"claude_pane_sessions": "Claude 對話",
|
|
434
|
+
"claude_pane_session_done": "完成:{title}",
|
|
435
|
+
"claude_pane_session_waiting": "在等你:{title}",
|
|
434
436
|
"claude_pane_busy": "忙",
|
|
435
437
|
"claude_pane_waiting": "等你",
|
|
438
|
+
"claude_pane_unavailable_not_signed_in": "未登入 · 在它的 CLI 登入一次",
|
|
439
|
+
"claude_pane_unavailable_no_data": "還沒有資料 · 用過一次就會出現",
|
|
440
|
+
"claude_pane_unavailable_error": "讀不到 · 會自動重試",
|
|
441
|
+
"claude_pane_waiting_input": "要你回答",
|
|
442
|
+
"claude_pane_waiting_dialog": "對話框開著",
|
|
443
|
+
"claude_pane_waiting_sandbox": "要允許沙盒",
|
|
444
|
+
"claude_pane_waiting_worker": "子代理請求",
|
|
445
|
+
"claude_pane_waiting_goal": "確認目標",
|
|
446
|
+
"claude_pane_waiting_for": "等你:{reason}",
|
|
436
447
|
"claude_pane_agent_running": "執行中",
|
|
437
448
|
"claude_pane_agent_idle": "閒置",
|
|
438
449
|
"claude_pane_agent_pending": "待執行",
|
|
@@ -471,6 +482,7 @@
|
|
|
471
482
|
"claude_pane_menu": "Claude Code 側邊面板",
|
|
472
483
|
"claude_pane_tooltip": "在 Claude Code 顯示額度、對話與背景工作",
|
|
473
484
|
"claude_pane_enabled_msg": "已開啟側邊面板。開新的 Claude Code 對話(或在對話裡打 /reload-plugins)後生效;終端機寬度不夠時打 /usage-dash 叫出面板。",
|
|
485
|
+
"claude_pane_fullscreen_enabled_note": "已一併開啟 Claude Code 的全螢幕介面,讓面板顯示在右側。取消勾選側邊面板時會改回。",
|
|
474
486
|
"claude_pane_disabled_msg": "已移除 Claude Code 側邊面板。",
|
|
475
487
|
"claude_pane_action_failed": "Claude Code 側邊面板切換失敗。",
|
|
476
488
|
"claude_pane_source_missing": "找不到面板原始碼。已檢查:{tried}",
|
|
@@ -912,8 +924,19 @@
|
|
|
912
924
|
"claude_pane_week": "Week",
|
|
913
925
|
"claude_pane_period": "Period",
|
|
914
926
|
"claude_pane_sessions": "Claude sessions",
|
|
927
|
+
"claude_pane_session_done": "Done: {title}",
|
|
928
|
+
"claude_pane_session_waiting": "Waiting for you: {title}",
|
|
915
929
|
"claude_pane_busy": "Busy",
|
|
916
930
|
"claude_pane_waiting": "waiting for you",
|
|
931
|
+
"claude_pane_unavailable_not_signed_in": "Not signed in · sign in once in its CLI",
|
|
932
|
+
"claude_pane_unavailable_no_data": "No data yet · shows up after first use",
|
|
933
|
+
"claude_pane_unavailable_error": "Couldn't read · retrying automatically",
|
|
934
|
+
"claude_pane_waiting_input": "needs your answer",
|
|
935
|
+
"claude_pane_waiting_dialog": "dialog open",
|
|
936
|
+
"claude_pane_waiting_sandbox": "sandbox permission",
|
|
937
|
+
"claude_pane_waiting_worker": "subagent request",
|
|
938
|
+
"claude_pane_waiting_goal": "confirm the goal",
|
|
939
|
+
"claude_pane_waiting_for": "waiting: {reason}",
|
|
917
940
|
"claude_pane_agent_running": "running",
|
|
918
941
|
"claude_pane_agent_idle": "idle",
|
|
919
942
|
"claude_pane_agent_pending": "pending",
|
|
@@ -952,6 +975,7 @@
|
|
|
952
975
|
"claude_pane_menu": "Claude Code side pane",
|
|
953
976
|
"claude_pane_tooltip": "Show quotas, Claude sessions and background jobs in Claude Code",
|
|
954
977
|
"claude_pane_enabled_msg": "Pane enabled. Open a new Claude Code conversation (or run /reload-plugins in a conversation). If the terminal is too narrow, run /usage-dash to open the pane.",
|
|
978
|
+
"claude_pane_fullscreen_enabled_note": "Claude Code's fullscreen layout is now on so the pane can sit on the right. Turning the side pane off switches it back.",
|
|
955
979
|
"claude_pane_disabled_msg": "Claude Code side pane removed.",
|
|
956
980
|
"claude_pane_action_failed": "Could not change the Claude Code side pane.",
|
|
957
981
|
"claude_pane_source_missing": "Pane source not found. Tried: {tried}",
|
|
@@ -1393,8 +1417,19 @@
|
|
|
1393
1417
|
"claude_pane_week": "周",
|
|
1394
1418
|
"claude_pane_period": "期间",
|
|
1395
1419
|
"claude_pane_sessions": "Claude 对话",
|
|
1420
|
+
"claude_pane_session_done": "完成:{title}",
|
|
1421
|
+
"claude_pane_session_waiting": "在等你:{title}",
|
|
1396
1422
|
"claude_pane_busy": "忙",
|
|
1397
1423
|
"claude_pane_waiting": "等你",
|
|
1424
|
+
"claude_pane_unavailable_not_signed_in": "未登录 · 在它的 CLI 登录一次",
|
|
1425
|
+
"claude_pane_unavailable_no_data": "还没有数据 · 用过一次就会出现",
|
|
1426
|
+
"claude_pane_unavailable_error": "读不到 · 会自动重试",
|
|
1427
|
+
"claude_pane_waiting_input": "要你回答",
|
|
1428
|
+
"claude_pane_waiting_dialog": "对话框开着",
|
|
1429
|
+
"claude_pane_waiting_sandbox": "要允许沙盒",
|
|
1430
|
+
"claude_pane_waiting_worker": "子代理请求",
|
|
1431
|
+
"claude_pane_waiting_goal": "确认目标",
|
|
1432
|
+
"claude_pane_waiting_for": "等你:{reason}",
|
|
1398
1433
|
"claude_pane_agent_running": "运行中",
|
|
1399
1434
|
"claude_pane_agent_idle": "空闲",
|
|
1400
1435
|
"claude_pane_agent_pending": "待运行",
|
|
@@ -1433,6 +1468,7 @@
|
|
|
1433
1468
|
"claude_pane_menu": "Claude Code 侧边面板",
|
|
1434
1469
|
"claude_pane_tooltip": "在 Claude Code 显示额度、对话与后台任务",
|
|
1435
1470
|
"claude_pane_enabled_msg": "已开启侧边面板。打开新的 Claude Code 对话(或在对话里输入 /reload-plugins)后生效;终端宽度不够时输入 /usage-dash 打开面板。",
|
|
1471
|
+
"claude_pane_fullscreen_enabled_note": "已一并开启 Claude Code 的全屏界面,让面板显示在右侧。取消勾选侧边面板时会改回。",
|
|
1436
1472
|
"claude_pane_disabled_msg": "已移除 Claude Code 侧边面板。",
|
|
1437
1473
|
"claude_pane_action_failed": "Claude Code 侧边面板切换失败。",
|
|
1438
1474
|
"claude_pane_source_missing": "找不到面板源代码。已检查:{tried}",
|
|
@@ -1874,8 +1910,19 @@
|
|
|
1874
1910
|
"claude_pane_week": "週",
|
|
1875
1911
|
"claude_pane_period": "期間",
|
|
1876
1912
|
"claude_pane_sessions": "Claude セッション",
|
|
1913
|
+
"claude_pane_session_done": "完了:{title}",
|
|
1914
|
+
"claude_pane_session_waiting": "入力待ち:{title}",
|
|
1877
1915
|
"claude_pane_busy": "実行中",
|
|
1878
1916
|
"claude_pane_waiting": "入力待ち",
|
|
1917
|
+
"claude_pane_unavailable_not_signed_in": "未ログイン · CLI で一度ログイン",
|
|
1918
|
+
"claude_pane_unavailable_no_data": "まだデータなし · 一度使うと表示",
|
|
1919
|
+
"claude_pane_unavailable_error": "読み取れません · 自動で再試行",
|
|
1920
|
+
"claude_pane_waiting_input": "回答待ち",
|
|
1921
|
+
"claude_pane_waiting_dialog": "ダイアログ表示中",
|
|
1922
|
+
"claude_pane_waiting_sandbox": "サンドボックス許可",
|
|
1923
|
+
"claude_pane_waiting_worker": "サブエージェント要求",
|
|
1924
|
+
"claude_pane_waiting_goal": "目標の確認",
|
|
1925
|
+
"claude_pane_waiting_for": "入力待ち:{reason}",
|
|
1879
1926
|
"claude_pane_agent_running": "実行中",
|
|
1880
1927
|
"claude_pane_agent_idle": "待機中",
|
|
1881
1928
|
"claude_pane_agent_pending": "実行待ち",
|
|
@@ -1914,6 +1961,7 @@
|
|
|
1914
1961
|
"claude_pane_menu": "Claude Code サイドパネル",
|
|
1915
1962
|
"claude_pane_tooltip": "Claude Code に使用量、セッション、バックグラウンド作業を表示",
|
|
1916
1963
|
"claude_pane_enabled_msg": "サイドパネルを有効にしました。新しい Claude Code セッションを開くか、会話で /reload-plugins を実行してください。ターミナルの幅が足りない場合は /usage-dash で開けます。",
|
|
1964
|
+
"claude_pane_fullscreen_enabled_note": "Claude Code のフルスクリーンレイアウトを有効にし、パネルを右側に表示できるようにしました。サイドパネルをオフにすると元に戻ります。",
|
|
1917
1965
|
"claude_pane_disabled_msg": "Claude Code サイドパネルを削除しました。",
|
|
1918
1966
|
"claude_pane_action_failed": "Claude Code サイドパネルを切り替えられませんでした。",
|
|
1919
1967
|
"claude_pane_source_missing": "パネルのソースがありません。確認先: {tried}",
|
|
@@ -2355,8 +2403,19 @@
|
|
|
2355
2403
|
"claude_pane_week": "주간",
|
|
2356
2404
|
"claude_pane_period": "기간",
|
|
2357
2405
|
"claude_pane_sessions": "Claude 대화",
|
|
2406
|
+
"claude_pane_session_done": "완료: {title}",
|
|
2407
|
+
"claude_pane_session_waiting": "입력 대기: {title}",
|
|
2358
2408
|
"claude_pane_busy": "작업 중",
|
|
2359
2409
|
"claude_pane_waiting": "입력 대기",
|
|
2410
|
+
"claude_pane_unavailable_not_signed_in": "로그인 안 됨 · CLI에서 한 번 로그인",
|
|
2411
|
+
"claude_pane_unavailable_no_data": "아직 데이터 없음 · 한 번 사용하면 표시",
|
|
2412
|
+
"claude_pane_unavailable_error": "읽을 수 없음 · 자동으로 재시도",
|
|
2413
|
+
"claude_pane_waiting_input": "답변 필요",
|
|
2414
|
+
"claude_pane_waiting_dialog": "대화 상자 열림",
|
|
2415
|
+
"claude_pane_waiting_sandbox": "샌드박스 권한",
|
|
2416
|
+
"claude_pane_waiting_worker": "하위 에이전트 요청",
|
|
2417
|
+
"claude_pane_waiting_goal": "목표 확인",
|
|
2418
|
+
"claude_pane_waiting_for": "입력 대기: {reason}",
|
|
2360
2419
|
"claude_pane_agent_running": "실행 중",
|
|
2361
2420
|
"claude_pane_agent_idle": "유휴",
|
|
2362
2421
|
"claude_pane_agent_pending": "실행 대기",
|
|
@@ -2395,6 +2454,7 @@
|
|
|
2395
2454
|
"claude_pane_menu": "Claude Code 사이드 패널",
|
|
2396
2455
|
"claude_pane_tooltip": "Claude Code에 사용량, 대화 및 백그라운드 작업 표시",
|
|
2397
2456
|
"claude_pane_enabled_msg": "사이드 패널을 켰습니다. 새 Claude Code 대화를 열거나 대화에서 /reload-plugins를 실행하세요. 터미널이 좁으면 /usage-dash로 패널을 여세요.",
|
|
2457
|
+
"claude_pane_fullscreen_enabled_note": "Claude Code의 전체 화면 레이아웃도 켜서 패널이 오른쪽에 표시됩니다. 사이드 패널을 끄면 원래대로 돌아갑니다.",
|
|
2398
2458
|
"claude_pane_disabled_msg": "Claude Code 사이드 패널을 제거했습니다.",
|
|
2399
2459
|
"claude_pane_action_failed": "Claude Code 사이드 패널을 전환하지 못했습니다.",
|
|
2400
2460
|
"claude_pane_source_missing": "패널 소스를 찾을 수 없습니다. 확인한 경로: {tried}",
|
|
@@ -45,6 +45,8 @@ def _resolve_source() -> Path:
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def _status_argv() -> list[str]:
|
|
48
|
+
if getattr(sys, "frozen", False) and sys.platform == "win32":
|
|
49
|
+
return [sys.executable, "status", "--json"]
|
|
48
50
|
if getattr(sys, "frozen", False):
|
|
49
51
|
resources = str(Path(sys.executable).resolve().parent.parent / "Resources")
|
|
50
52
|
major, minor = sys.version_info[:2]
|
|
@@ -142,9 +144,25 @@ def enable_claude_pane() -> int:
|
|
|
142
144
|
return 0
|
|
143
145
|
|
|
144
146
|
|
|
147
|
+
def is_fullscreen_layout() -> bool:
|
|
148
|
+
return setup_hook._load_settings().get("tui") == "fullscreen"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def enable_fullscreen_layout() -> None:
|
|
152
|
+
settings = setup_hook._load_settings()
|
|
153
|
+
settings["tui"] = "fullscreen"
|
|
154
|
+
usage = settings.get("usage")
|
|
155
|
+
if not isinstance(usage, dict):
|
|
156
|
+
usage = {}
|
|
157
|
+
settings["usage"] = usage
|
|
158
|
+
usage["claudePaneSetFullscreen"] = True
|
|
159
|
+
setup_hook._save_settings(settings)
|
|
160
|
+
|
|
161
|
+
|
|
145
162
|
def disable_claude_pane() -> int:
|
|
146
163
|
settings = setup_hook._load_settings()
|
|
147
164
|
env = _env(settings)
|
|
165
|
+
changed = False
|
|
148
166
|
if PLUGIN_DIRS_KEY in env:
|
|
149
167
|
value = _remove_path(env[PLUGIN_DIRS_KEY], str(INSTALL_DIR.absolute()))
|
|
150
168
|
if value:
|
|
@@ -153,6 +171,16 @@ def disable_claude_pane() -> int:
|
|
|
153
171
|
env.pop(PLUGIN_DIRS_KEY)
|
|
154
172
|
if not env:
|
|
155
173
|
settings.pop("env", None)
|
|
174
|
+
changed = True
|
|
175
|
+
usage = settings.get("usage")
|
|
176
|
+
if isinstance(usage, dict) and usage.get("claudePaneSetFullscreen") is True:
|
|
177
|
+
if settings.get("tui") == "fullscreen":
|
|
178
|
+
settings.pop("tui")
|
|
179
|
+
usage.pop("claudePaneSetFullscreen")
|
|
180
|
+
if not usage:
|
|
181
|
+
settings.pop("usage")
|
|
182
|
+
changed = True
|
|
183
|
+
if changed:
|
|
156
184
|
setup_hook._save_settings(settings)
|
|
157
185
|
if INSTALL_DIR.exists():
|
|
158
186
|
shutil.rmtree(INSTALL_DIR)
|
|
@@ -314,6 +314,11 @@ async def run_tui(mock: bool, interval: int, force_group: int | None = None) ->
|
|
|
314
314
|
|
|
315
315
|
|
|
316
316
|
def main() -> None:
|
|
317
|
+
if sys.argv[1:2] == ["status"]:
|
|
318
|
+
import usage_cli
|
|
319
|
+
|
|
320
|
+
usage_cli.main()
|
|
321
|
+
return
|
|
317
322
|
_setup_logging()
|
|
318
323
|
args = parse_args()
|
|
319
324
|
if args.doctor:
|
|
@@ -82,6 +82,50 @@ def test_disable_only_ours(isolated: Path, others: bool, keep_env: bool) -> None
|
|
|
82
82
|
assert not pane.is_claude_pane_enabled()
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
def test_enable_fullscreen_layout_records_usage_flag(isolated: Path) -> None:
|
|
86
|
+
setup_hook._save_settings({"usage": {"other": "keep"}, "other": "keep"})
|
|
87
|
+
|
|
88
|
+
pane.enable_fullscreen_layout()
|
|
89
|
+
|
|
90
|
+
assert setup_hook._load_settings() == {
|
|
91
|
+
"tui": "fullscreen",
|
|
92
|
+
"usage": {"other": "keep", "claudePaneSetFullscreen": True},
|
|
93
|
+
"other": "keep",
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_disable_restores_fullscreen_layout_set_by_pane(isolated: Path) -> None:
|
|
98
|
+
pane.INSTALL_DIR.mkdir(parents=True)
|
|
99
|
+
setup_hook._save_settings(
|
|
100
|
+
{
|
|
101
|
+
"tui": "fullscreen",
|
|
102
|
+
"usage": {"claudePaneSetFullscreen": True, "other": "keep"},
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
assert pane.disable_claude_pane() == 0
|
|
107
|
+
|
|
108
|
+
assert setup_hook._load_settings() == {"usage": {"other": "keep"}}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_disable_keeps_user_fullscreen_layout(isolated: Path) -> None:
|
|
112
|
+
pane.INSTALL_DIR.mkdir(parents=True)
|
|
113
|
+
setup_hook._save_settings({"tui": "fullscreen"})
|
|
114
|
+
|
|
115
|
+
assert pane.disable_claude_pane() == 0
|
|
116
|
+
|
|
117
|
+
assert setup_hook._load_settings() == {"tui": "fullscreen"}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_disable_keeps_changed_tui_when_pane_flagged(isolated: Path) -> None:
|
|
121
|
+
pane.INSTALL_DIR.mkdir(parents=True)
|
|
122
|
+
setup_hook._save_settings({"tui": "compact", "usage": {"claudePaneSetFullscreen": True}})
|
|
123
|
+
|
|
124
|
+
assert pane.disable_claude_pane() == 0
|
|
125
|
+
|
|
126
|
+
assert setup_hook._load_settings() == {"tui": "compact"}
|
|
127
|
+
|
|
128
|
+
|
|
85
129
|
def test_tilde_disable_and_reinstall(isolated: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
86
130
|
monkeypatch.setenv("HOME", str(pane.INSTALL_DIR.parent))
|
|
87
131
|
monkeypatch.setenv("USERPROFILE", str(pane.INSTALL_DIR.parent))
|
|
@@ -222,6 +266,15 @@ def test_uses_platform_separator(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
222
266
|
assert pane._remove_path("/other;/pane/", "/pane") == "/other"
|
|
223
267
|
|
|
224
268
|
|
|
269
|
+
def test_status_argv_uses_windows_frozen_executable(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
270
|
+
executable = r"C:\\Program Files\\usage\\usage.exe"
|
|
271
|
+
monkeypatch.setattr(sys, "frozen", True, raising=False)
|
|
272
|
+
monkeypatch.setattr(sys, "platform", "win32")
|
|
273
|
+
monkeypatch.setattr(sys, "executable", executable)
|
|
274
|
+
|
|
275
|
+
assert pane._status_argv() == [executable, "status", "--json"]
|
|
276
|
+
|
|
277
|
+
|
|
225
278
|
def test_missing_sidecar_language_falls_back(
|
|
226
279
|
isolated: Path, monkeypatch: pytest.MonkeyPatch
|
|
227
280
|
) -> None:
|
|
@@ -70,6 +70,19 @@ def test_parse_args_setup(monkeypatch: Any) -> None:
|
|
|
70
70
|
assert args.setup is True
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
def test_main_delegates_status_before_logging(monkeypatch: Any) -> None:
|
|
74
|
+
import usage_cli
|
|
75
|
+
|
|
76
|
+
calls: list[str] = []
|
|
77
|
+
monkeypatch.setattr(sys, "argv", ["usage", "status", "--json"])
|
|
78
|
+
monkeypatch.setattr(main, "_setup_logging", lambda: calls.append("logging"))
|
|
79
|
+
monkeypatch.setattr(usage_cli, "main", lambda: calls.append("status"))
|
|
80
|
+
|
|
81
|
+
main.main()
|
|
82
|
+
|
|
83
|
+
assert calls == ["status"]
|
|
84
|
+
|
|
85
|
+
|
|
73
86
|
def test_apply_outcome_success_updates_snapshot_and_clears_fatal_message() -> None:
|
|
74
87
|
state = AppViewState(fatal_message="boom")
|
|
75
88
|
snapshot = _snapshot()
|
|
@@ -28,6 +28,8 @@ usage_cli: Any = import_module("usage_cli")
|
|
|
28
28
|
@pytest.fixture(autouse=True)
|
|
29
29
|
def _isolate_status_quotas(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
30
30
|
monkeypatch.setattr(agy_quota_probe, "CACHE_PATH", tmp_path / "agy_quota_cache.json")
|
|
31
|
+
monkeypatch.setattr(agy_quota_probe, "_TOKEN_PATH", tmp_path / "agy-token")
|
|
32
|
+
agy_quota_probe._TOKEN_PATH.touch()
|
|
31
33
|
monkeypatch.setattr(grok_quota_probe, "load_quota", lambda: None)
|
|
32
34
|
|
|
33
35
|
|
|
@@ -363,6 +365,7 @@ def test_main_status_json_marks_none_loader_unavailable(
|
|
|
363
365
|
payload = json.loads(capsys.readouterr().out)
|
|
364
366
|
assert payload["agents"]["claude-code"] == {
|
|
365
367
|
"available": False,
|
|
368
|
+
"reason": "no_data",
|
|
366
369
|
"five_hour": {"used_percent": None, "resets_at": None, "resets_in_seconds": None},
|
|
367
370
|
"seven_day": {"used_percent": None, "resets_at": None, "resets_in_seconds": None},
|
|
368
371
|
"model": None,
|
|
@@ -1041,6 +1044,7 @@ def test_dashboard_sort_cycle_shape_and_order() -> None:
|
|
|
1041
1044
|
def test_status_payload_antigravity_cache(monkeypatch: pytest.MonkeyPatch, age: int) -> None:
|
|
1042
1045
|
now = datetime(2026, 10, 4, 14, tzinfo=UTC)
|
|
1043
1046
|
fetched_at = datetime.fromtimestamp(now.timestamp() - age + 0.319588, UTC).isoformat()
|
|
1047
|
+
agy_quota_probe._TOKEN_PATH.unlink()
|
|
1044
1048
|
cache = {
|
|
1045
1049
|
"fetched_at": fetched_at,
|
|
1046
1050
|
"groups": [
|
|
@@ -1090,7 +1094,9 @@ def test_status_payload_antigravity_cache(monkeypatch: pytest.MonkeyPatch, age:
|
|
|
1090
1094
|
def test_status_antigravity_unreadable_cache(content: str | None) -> None:
|
|
1091
1095
|
if content is not None:
|
|
1092
1096
|
agy_quota_probe.CACHE_PATH.write_bytes(content.encode("latin-1"))
|
|
1093
|
-
assert usage_cli._status_antigravity(1_000_000) == usage_cli._status_agent(
|
|
1097
|
+
assert usage_cli._status_antigravity(1_000_000) == usage_cli._status_agent(
|
|
1098
|
+
None, 1_000_000, "error" if content == "\xff" else "no_data"
|
|
1099
|
+
)
|
|
1094
1100
|
|
|
1095
1101
|
|
|
1096
1102
|
@pytest.mark.parametrize("tier", ["XPremium", None])
|
|
@@ -1127,3 +1133,32 @@ def test_status_payload_grok(
|
|
|
1127
1133
|
|
|
1128
1134
|
def test_status_grok_none() -> None:
|
|
1129
1135
|
assert usage_cli._status_grok(1_000_000) == usage_cli._status_agent(None, 1_000_000)
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
@pytest.mark.parametrize("signed_in", [False, True])
|
|
1139
|
+
def test_status_antigravity_missing_quota_reason(signed_in: bool) -> None:
|
|
1140
|
+
if not signed_in:
|
|
1141
|
+
agy_quota_probe._TOKEN_PATH.unlink()
|
|
1142
|
+
assert usage_cli._status_antigravity(0)["reason"] == (
|
|
1143
|
+
"no_data" if signed_in else "not_signed_in"
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
@pytest.mark.parametrize("agent_id", ["claude-code", "codex", "antigravity", "grok"])
|
|
1148
|
+
def test_status_loader_exception_reason(monkeypatch: pytest.MonkeyPatch, agent_id: str) -> None:
|
|
1149
|
+
def fail() -> None:
|
|
1150
|
+
raise OSError("quota read failed")
|
|
1151
|
+
|
|
1152
|
+
monkeypatch.setattr(
|
|
1153
|
+
usage_cli, "RATE_LIMIT_LOADERS", {"claude-code": lambda: None, "codex": lambda: None}
|
|
1154
|
+
)
|
|
1155
|
+
if agent_id == "antigravity":
|
|
1156
|
+
agy_quota_probe._TOKEN_PATH.unlink()
|
|
1157
|
+
monkeypatch.setattr(agy_quota_probe, "_read_cache", fail)
|
|
1158
|
+
elif agent_id == "grok":
|
|
1159
|
+
monkeypatch.setattr(grok_quota_probe, "load_quota", fail)
|
|
1160
|
+
else:
|
|
1161
|
+
monkeypatch.setitem(usage_cli.RATE_LIMIT_LOADERS, agent_id, fail)
|
|
1162
|
+
payload = usage_cli._status_payload()
|
|
1163
|
+
assert payload["schema_version"] == 1
|
|
1164
|
+
assert payload["agents"][agent_id]["reason"] == "error"
|
|
@@ -22,6 +22,7 @@ import panels
|
|
|
22
22
|
import prefs
|
|
23
23
|
import service_status
|
|
24
24
|
from i18n import _t
|
|
25
|
+
from installer import claude_pane
|
|
25
26
|
from loaders import codex_loader
|
|
26
27
|
from loaders.agy_quota_probe import AgyQuotaGroup, AgyQuotaResult, AgyQuotaWindow
|
|
27
28
|
from menubar import agy as menubar_agy
|
|
@@ -1534,6 +1535,7 @@ def test_panel_menu_data_is_localized_and_reads_current_checks(
|
|
|
1534
1535
|
monkeypatch.setattr(wintray, "_window_keeper_enabled", lambda: True)
|
|
1535
1536
|
monkeypatch.setattr(wintray, "_session_resume_enabled", lambda: True)
|
|
1536
1537
|
monkeypatch.setattr(wintray, "_terse_mode_enabled", lambda: False)
|
|
1538
|
+
monkeypatch.setattr(wintray, "_claude_pane_enabled", lambda: True)
|
|
1537
1539
|
|
|
1538
1540
|
menu = controller._panel_menu_data()
|
|
1539
1541
|
|
|
@@ -1553,6 +1555,7 @@ def test_panel_menu_data_is_localized_and_reads_current_checks(
|
|
|
1553
1555
|
"window_keeper_menu",
|
|
1554
1556
|
"project_butler",
|
|
1555
1557
|
"terse_mode_menu",
|
|
1558
|
+
"claude_pane_menu",
|
|
1556
1559
|
"separator",
|
|
1557
1560
|
"check_update",
|
|
1558
1561
|
]
|
|
@@ -1566,6 +1569,7 @@ def test_panel_menu_data_is_localized_and_reads_current_checks(
|
|
|
1566
1569
|
assert menu[7]["checked"] is True
|
|
1567
1570
|
assert menu[8]["checked"] is True
|
|
1568
1571
|
assert menu[9]["checked"] is False
|
|
1572
|
+
assert menu[10]["checked"] is True
|
|
1569
1573
|
|
|
1570
1574
|
|
|
1571
1575
|
def test_panel_and_tray_menus_render_the_shared_model(
|
|
@@ -1683,6 +1687,7 @@ def test_tray_quit_label_removes_the_power_glyph() -> None:
|
|
|
1683
1687
|
({"action": "toggle_window_keeper"}, "toggle_window_keeper", ()),
|
|
1684
1688
|
({"action": "toggle_session_resume"}, "toggle_session_resume", ()),
|
|
1685
1689
|
({"action": "toggle_terse_mode"}, "toggle_terse_mode", ()),
|
|
1690
|
+
({"action": "toggle_claude_pane"}, "toggle_claude_pane", ()),
|
|
1686
1691
|
({"action": "check_update"}, "check_update", ()),
|
|
1687
1692
|
({"action": "quit"}, "quit", ()),
|
|
1688
1693
|
],
|
|
@@ -1930,6 +1935,7 @@ def test_attach_schedules_startup_maintenance_after_tray_is_visible(
|
|
|
1930
1935
|
"wintray.app.usage_diagnosis_snapshot.maybe_schedule_refresh",
|
|
1931
1936
|
lambda: events.append("diagnosis"),
|
|
1932
1937
|
)
|
|
1938
|
+
monkeypatch.setattr(claude_pane, "refresh_claude_pane", lambda: events.append("claude-pane"))
|
|
1933
1939
|
monkeypatch.setattr(
|
|
1934
1940
|
controller,
|
|
1935
1941
|
"_clear_stale_update_cache",
|
|
@@ -1951,7 +1957,8 @@ def test_attach_schedules_startup_maintenance_after_tray_is_visible(
|
|
|
1951
1957
|
]
|
|
1952
1958
|
startup = next(thread for thread in threads if thread.target == controller._startup_maintenance)
|
|
1953
1959
|
startup.target(**startup.kwargs)
|
|
1954
|
-
assert events[-
|
|
1960
|
+
assert events[-4:] == [
|
|
1961
|
+
"claude-pane",
|
|
1955
1962
|
"diagnosis",
|
|
1956
1963
|
"clear-update-cache",
|
|
1957
1964
|
(
|
|
@@ -1961,6 +1968,86 @@ def test_attach_schedules_startup_maintenance_after_tray_is_visible(
|
|
|
1961
1968
|
]
|
|
1962
1969
|
|
|
1963
1970
|
|
|
1971
|
+
def test_claude_pane_menu_check_and_toggle_results(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1972
|
+
controller = wintray._WindowsTrayController(mock=True, interval=60)
|
|
1973
|
+
controller.language = "en"
|
|
1974
|
+
entry = wintray_menu.MenuCommand(
|
|
1975
|
+
"claude_pane_menu", "toggle_claude_pane", checked_by="claude_pane"
|
|
1976
|
+
)
|
|
1977
|
+
messages: list[str] = []
|
|
1978
|
+
monkeypatch.setattr(claude_pane, "is_claude_pane_enabled", lambda: False)
|
|
1979
|
+
monkeypatch.setattr(claude_pane, "enable_claude_pane", lambda: 0)
|
|
1980
|
+
monkeypatch.setattr(claude_pane, "is_fullscreen_layout", lambda: True)
|
|
1981
|
+
monkeypatch.setattr(controller, "_message_box", messages.append)
|
|
1982
|
+
|
|
1983
|
+
assert wintray._menu_checked(controller, entry) is False
|
|
1984
|
+
controller._toggle_claude_pane_in_background()
|
|
1985
|
+
|
|
1986
|
+
assert messages == [_t("en", "claude_pane_enabled_msg")]
|
|
1987
|
+
|
|
1988
|
+
|
|
1989
|
+
def test_claude_pane_toggle_enables_fullscreen_and_reports_note(
|
|
1990
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
1991
|
+
) -> None:
|
|
1992
|
+
controller = wintray._WindowsTrayController(mock=True, interval=60)
|
|
1993
|
+
controller.language = "en"
|
|
1994
|
+
messages: list[str] = []
|
|
1995
|
+
calls: list[str] = []
|
|
1996
|
+
monkeypatch.setattr(claude_pane, "is_claude_pane_enabled", lambda: False)
|
|
1997
|
+
monkeypatch.setattr(claude_pane, "enable_claude_pane", lambda: 0)
|
|
1998
|
+
monkeypatch.setattr(claude_pane, "is_fullscreen_layout", lambda: False)
|
|
1999
|
+
monkeypatch.setattr(claude_pane, "enable_fullscreen_layout", lambda: calls.append("fullscreen"))
|
|
2000
|
+
|
|
2001
|
+
monkeypatch.setattr(controller, "_message_box", messages.append)
|
|
2002
|
+
|
|
2003
|
+
controller._toggle_claude_pane_in_background()
|
|
2004
|
+
|
|
2005
|
+
assert calls == ["fullscreen"]
|
|
2006
|
+
assert messages == [
|
|
2007
|
+
f"{_t('en', 'claude_pane_enabled_msg')}\n\n"
|
|
2008
|
+
f"{_t('en', 'claude_pane_fullscreen_enabled_note')}"
|
|
2009
|
+
]
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
def test_claude_pane_toggle_fullscreen_failure_reports_error(
|
|
2013
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
2014
|
+
) -> None:
|
|
2015
|
+
controller = wintray._WindowsTrayController(mock=True, interval=60)
|
|
2016
|
+
controller.language = "en"
|
|
2017
|
+
messages: list[str] = []
|
|
2018
|
+
monkeypatch.setattr(claude_pane, "is_claude_pane_enabled", lambda: False)
|
|
2019
|
+
monkeypatch.setattr(claude_pane, "enable_claude_pane", lambda: 0)
|
|
2020
|
+
monkeypatch.setattr(claude_pane, "is_fullscreen_layout", lambda: False)
|
|
2021
|
+
|
|
2022
|
+
def fail() -> None:
|
|
2023
|
+
raise OSError("fullscreen failure")
|
|
2024
|
+
|
|
2025
|
+
monkeypatch.setattr(claude_pane, "enable_fullscreen_layout", fail)
|
|
2026
|
+
monkeypatch.setattr(controller, "_message_box", messages.append)
|
|
2027
|
+
|
|
2028
|
+
controller._toggle_claude_pane_in_background()
|
|
2029
|
+
|
|
2030
|
+
assert messages == [f"{_t('en', 'claude_pane_action_failed')}\n\nfullscreen failure"]
|
|
2031
|
+
|
|
2032
|
+
|
|
2033
|
+
def test_claude_pane_toggle_failure_reports_output(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
2034
|
+
controller = wintray._WindowsTrayController(mock=True, interval=60)
|
|
2035
|
+
controller.language = "en"
|
|
2036
|
+
messages: list[str] = []
|
|
2037
|
+
monkeypatch.setattr(claude_pane, "is_claude_pane_enabled", lambda: False)
|
|
2038
|
+
|
|
2039
|
+
def fail() -> int:
|
|
2040
|
+
print("pane failure")
|
|
2041
|
+
return 1
|
|
2042
|
+
|
|
2043
|
+
monkeypatch.setattr(claude_pane, "enable_claude_pane", fail)
|
|
2044
|
+
monkeypatch.setattr(controller, "_message_box", messages.append)
|
|
2045
|
+
|
|
2046
|
+
controller._toggle_claude_pane_in_background()
|
|
2047
|
+
|
|
2048
|
+
assert messages == [f"{_t('en', 'claude_pane_action_failed')}\n\npane failure"]
|
|
2049
|
+
|
|
2050
|
+
|
|
1964
2051
|
def test_refresh_requested_while_busy_runs_once_after_current_refresh(
|
|
1965
2052
|
monkeypatch: pytest.MonkeyPatch,
|
|
1966
2053
|
) -> None:
|