usage-cli 0.31.5__tar.gz → 0.32.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {usage_cli-0.31.5/usage_cli.egg-info → usage_cli-0.32.0}/PKG-INFO +3 -2
- {usage_cli-0.31.5 → usage_cli-0.32.0}/README.md +2 -1
- {usage_cli-0.31.5 → usage_cli-0.32.0}/i18n.json +270 -5
- usage_cli-0.32.0/installer/claude_pane.py +175 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/actions.py +48 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/app.py +6 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/menu.py +12 -1
- usage_cli-0.32.0/menubar/new_badge.py +97 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/update.py +5 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/pyproject.toml +1 -1
- {usage_cli-0.31.5 → usage_cli-0.32.0}/setup_app.py +1 -0
- usage_cli-0.32.0/tests/test_claude_pane.py +363 -0
- usage_cli-0.32.0/tests/test_new_badge.py +137 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_cli.py +108 -1
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_statusline_grok.py +13 -4
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_wintray.py +31 -1
- {usage_cli-0.31.5 → usage_cli-0.32.0/usage_cli.egg-info}/PKG-INFO +3 -2
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_cli.egg-info/SOURCES.txt +4 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_cli.py +62 -3
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_statusline_grok.py +8 -2
- {usage_cli-0.31.5 → usage_cli-0.32.0}/wintray/app.py +13 -3
- {usage_cli-0.31.5 → usage_cli-0.32.0}/LICENSE +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/agy.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/claude.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/codex.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/grok.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/muse.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/rate_limits.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/registry.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/adapters/types.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/aggregator.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/blocks.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/diagnoser.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/dispatch_ledger.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/insights.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/persona_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/reporter.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/subscription.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/analyzer/usage_snapshot.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/doctor.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/fsevents_watch.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/i18n.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/installer/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/installer/login_item.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/installer/session_hooks.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/installer/setup_hook.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/installer/statusline_settings.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/agy_disk_cache.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/agy_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/agy_quota_probe.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/cache_quarantine.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/claude_paths.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/codex_disk_cache.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/codex_events.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/codex_fork_replay.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/codex_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/codex_paths.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/disk_cache_common.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/disk_cache_lifecycle.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/grok_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/grok_quota_probe.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/history_disk_cache.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/history_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/jsonl_limits.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/jsonl_utils.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/loaders/muse_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/main.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/agy.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/chrome.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/grok.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/notify.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/popover.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/prefs.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/refresh.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/state.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/menubar/title.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/base.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/dynamic_height.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/panel_scale.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/panel_window.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/panel_window_state.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/payload.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/web_panel.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/panels/window_drag.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/prefs.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/pricing.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/project_resolver.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/quota/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/quota/agy_window_keeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/quota/burn_rate.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/quota/codex_window_keeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/quota/usage_rate.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/quota/window_keeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/service_status.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/setup.cfg +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_adapters_claude.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_adapters_codex.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_adapters_rate_limits.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_aggregator.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_agy_adapter.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_agy_disk_cache.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_agy_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_agy_quota_probe.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_agy_window_keeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_analyzer_blocks.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_analyzer_pipeline.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_burn_rate.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_cache_quarantine.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_claude_paths.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_codex_consistency.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_codex_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_codex_window_keeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_diagnoser.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_disk_cache_shards.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_dispatch_ledger.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_doctor.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_dynamic_height.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_fsevents_watch.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_grok_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_grok_quota_probe.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_history_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_hook_scripts_are_standalone.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_html_report_snapshot.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_i18n_format_fallback.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_i18n_key_parity.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_i18n_packaged_path.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_insights.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_jsonl_utils.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_login_item.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_main.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_menubar.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_menubar_agy.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_menubar_grok.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_menubar_notify.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_menubar_prefs.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_menubar_state.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_muse_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_packaged_resources.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_packaging.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_panel_footer_layout.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_panel_scale.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_panel_window_state.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_panels.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_panels_lazy_import.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_persona_loader.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_pricing.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_project_resolver.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_report_daily_chart.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_report_filter_js.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_reporter_build_data.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_service_status.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_session_resume.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_setup_app.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_setup_hook.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_setup_hook_coexistence.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_setup_hook_resume.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_setup_hook_terse.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_subprocess_utils.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_subscription.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_time_utils.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_tui.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_tui_sprite.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_ui_tables.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_update_checker.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_update_gate.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_update_release_notes.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_client.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_diagnosis_snapshot.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_dir_sweeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_lang.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_logging.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_notifications.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_rate.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_snapshot.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_snapshot_replay.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_statusline.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_statusline_agy.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_statusline_forwarder.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_terse_mode.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_usage_terse_reminder.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_web_panel_payload.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_wheel_packaging.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_win_login_item.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_window_keeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tests/test_windows_manifest.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tui/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tui/app.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/tui/sprite.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/html_report.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/report_charts.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/report_daily_chart.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/report_filter.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/report_ledger.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/report_scripts.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/report_styles.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/ui/tables.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/updates/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/updates/checker.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/updates/gate.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/updates/release_notes.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_cli.egg-info/dependency_links.txt +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_cli.egg-info/entry_points.txt +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_cli.egg-info/requires.txt +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_cli.egg-info/top_level.txt +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_client.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_common/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_common/subprocess_utils.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_common/time_utils.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_common/usage_dir_sweeper.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_common/usage_lang.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_common/usage_logging.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_diagnosis_snapshot.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_notifications.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_session_resume.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_statusline.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_statusline_agy.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_statusline_forwarder.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_terse_mode.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/usage_terse_reminder.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/wintray/__init__.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/wintray/login_item.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/wintray/menu.py +0 -0
- {usage_cli-0.31.5 → usage_cli-0.32.0}/wintray/watch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usage-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.32.0
|
|
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
|
|
@@ -76,8 +76,9 @@ 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):** Toggle quotas, Claude sessions and background jobs inside Claude Code. Requires a recent Claude Code with mod support (tested with 2.1.289). Open a new conversation or run `/reload-plugins` after enabling. The pane opens on the right automatically at terminal widths ≥144 columns; use `/usage-dash` in narrower terminals. macOS Terminal supports only 256 colors and may show a gray background; select an ANSI dark theme in `/config`. Conversations waiting for your permission or MCP input are marked in yellow across panes, and background jobs also list subagents started by Claude with the Agent tool and their status. When the usage app starts, it automatically updates an enabled pane to the bundled version.
|
|
79
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.
|
|
80
|
-
- **Terminal Integration:** `usage status --json` hands your Claude Code and
|
|
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).
|
|
81
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.
|
|
82
83
|
|
|
83
84
|
### Stay Current
|
|
@@ -56,8 +56,9 @@ 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):** Toggle quotas, Claude sessions and background jobs inside Claude Code. Requires a recent Claude Code with mod support (tested with 2.1.289). Open a new conversation or run `/reload-plugins` after enabling. The pane opens on the right automatically at terminal widths ≥144 columns; use `/usage-dash` in narrower terminals. macOS Terminal supports only 256 colors and may show a gray background; select an ANSI dark theme in `/config`. Conversations waiting for your permission or MCP input are marked in yellow across panes, and background jobs also list subagents started by Claude with the Agent tool and their status. When the usage app starts, it automatically updates an enabled pane to the bundled version.
|
|
59
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.
|
|
60
|
-
- **Terminal Integration:** `usage status --json` hands your Claude Code and
|
|
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).
|
|
61
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.
|
|
62
63
|
|
|
63
64
|
### Stay Current
|
|
@@ -425,7 +425,60 @@
|
|
|
425
425
|
"setup_hook_deleted": "✓ 已刪除 hook:{path}",
|
|
426
426
|
"setup_status_file_deleted": "✓ 已刪除狀態檔:{path}",
|
|
427
427
|
"wintray_unavailable": "Windows 系統匣介面尚未安裝,改用終端機 TUI。",
|
|
428
|
-
"wintray_already_running": "usage 已在執行中;請點系統匣的 usage 圖示開啟面板。"
|
|
428
|
+
"wintray_already_running": "usage 已在執行中;請點系統匣的 usage 圖示開啟面板。",
|
|
429
|
+
"claude_pane_quota": "額度",
|
|
430
|
+
"claude_pane_five_hour": "5小時",
|
|
431
|
+
"claude_pane_week": "週",
|
|
432
|
+
"claude_pane_period": "期間",
|
|
433
|
+
"claude_pane_sessions": "Claude 對話",
|
|
434
|
+
"claude_pane_busy": "忙",
|
|
435
|
+
"claude_pane_waiting": "等你",
|
|
436
|
+
"claude_pane_agent_running": "執行中",
|
|
437
|
+
"claude_pane_agent_idle": "閒置",
|
|
438
|
+
"claude_pane_agent_pending": "待執行",
|
|
439
|
+
"claude_pane_busy_count": "{count} 忙 / {total}",
|
|
440
|
+
"claude_pane_idle": "閒置 {ago}",
|
|
441
|
+
"claude_pane_runs": "背景工作",
|
|
442
|
+
"claude_pane_none": "(沒有)",
|
|
443
|
+
"claude_pane_untitled": "(無標題)",
|
|
444
|
+
"claude_pane_no_project": "(無專案)",
|
|
445
|
+
"claude_pane_here": "(這裡)",
|
|
446
|
+
"claude_pane_more": "還有 {count} 個",
|
|
447
|
+
"claude_pane_less": "收回",
|
|
448
|
+
"claude_pane_terminal": "終端",
|
|
449
|
+
"claude_pane_desktop": "桌面",
|
|
450
|
+
"claude_pane_minutes": "{count}分",
|
|
451
|
+
"claude_pane_hours": "{count}時",
|
|
452
|
+
"claude_pane_days": "{count}天",
|
|
453
|
+
"claude_pane_seconds": "{count}秒",
|
|
454
|
+
"claude_pane_minutes_ago": "{count}分前",
|
|
455
|
+
"claude_pane_seconds_ago": "{count}秒前",
|
|
456
|
+
"claude_pane_just_now": "剛剛",
|
|
457
|
+
"claude_pane_failed": "失敗",
|
|
458
|
+
"claude_pane_completed": "完成",
|
|
459
|
+
"claude_pane_completed_ago": "{ago}{status}",
|
|
460
|
+
"claude_pane_quota_invalid": "額度 JSON 缺少 agents",
|
|
461
|
+
"claude_pane_exit_code": "結束碼 {code}",
|
|
462
|
+
"claude_pane_quota_error": "額度讀取失敗:{error}",
|
|
463
|
+
"claude_pane_session_error": "對話讀取失敗:{error}",
|
|
464
|
+
"claude_pane_home_missing": "HOME 未設定",
|
|
465
|
+
"claude_pane_description": "開啟額度、Claude 對話與背景工作面板",
|
|
466
|
+
"claude_pane_title": "使用狀態",
|
|
467
|
+
"claude_pane_opened": "已開啟使用狀態面板。",
|
|
468
|
+
"claude_pane_compact_summary": "對話 {busy} 忙 / {total} · 背景工作 ",
|
|
469
|
+
"claude_pane_sidecar_error": "面板設定讀取失敗:{error}",
|
|
470
|
+
"menu_badge_new": "新",
|
|
471
|
+
"claude_pane_menu": "Claude Code 側邊面板",
|
|
472
|
+
"claude_pane_tooltip": "在 Claude Code 顯示額度、對話與背景工作",
|
|
473
|
+
"claude_pane_enabled_msg": "已開啟側邊面板。開新的 Claude Code 對話(或在對話裡打 /reload-plugins)後生效;終端機寬度不夠時打 /usage-dash 叫出面板。",
|
|
474
|
+
"claude_pane_disabled_msg": "已移除 Claude Code 側邊面板。",
|
|
475
|
+
"claude_pane_action_failed": "Claude Code 側邊面板切換失敗。",
|
|
476
|
+
"claude_pane_source_missing": "找不到面板原始碼。已檢查:{tried}",
|
|
477
|
+
"claude_pane_invalid_env": "Claude 設定的 env 必須是物件,CLAUDE_CODE_PLUGIN_DIRS 必須是字串。",
|
|
478
|
+
"claude_pane_invalid_sidecar": "usage-pane.json 格式錯誤",
|
|
479
|
+
"claude_pane_elapsed": "{minutes}分{seconds}秒",
|
|
480
|
+
"claude_pane_remaining": "剩",
|
|
481
|
+
"claude_pane_hours_ago": "{count}時前"
|
|
429
482
|
},
|
|
430
483
|
"en": {
|
|
431
484
|
"panel_default_name": "Default",
|
|
@@ -853,7 +906,60 @@
|
|
|
853
906
|
"setup_hook_deleted": "✓ Deleted hook: {path}",
|
|
854
907
|
"setup_status_file_deleted": "✓ Deleted status file: {path}",
|
|
855
908
|
"wintray_unavailable": "The Windows tray interface is not installed; falling back to the terminal TUI.",
|
|
856
|
-
"wintray_already_running": "usage is already running; click the usage tray icon to open the panel."
|
|
909
|
+
"wintray_already_running": "usage is already running; click the usage tray icon to open the panel.",
|
|
910
|
+
"claude_pane_quota": "Quota",
|
|
911
|
+
"claude_pane_five_hour": "5h",
|
|
912
|
+
"claude_pane_week": "Week",
|
|
913
|
+
"claude_pane_period": "Period",
|
|
914
|
+
"claude_pane_sessions": "Claude sessions",
|
|
915
|
+
"claude_pane_busy": "Busy",
|
|
916
|
+
"claude_pane_waiting": "waiting for you",
|
|
917
|
+
"claude_pane_agent_running": "running",
|
|
918
|
+
"claude_pane_agent_idle": "idle",
|
|
919
|
+
"claude_pane_agent_pending": "pending",
|
|
920
|
+
"claude_pane_busy_count": "{count} busy / {total}",
|
|
921
|
+
"claude_pane_idle": "Idle {ago}",
|
|
922
|
+
"claude_pane_runs": "Background jobs",
|
|
923
|
+
"claude_pane_none": "(none)",
|
|
924
|
+
"claude_pane_untitled": "(untitled)",
|
|
925
|
+
"claude_pane_no_project": "(no project)",
|
|
926
|
+
"claude_pane_here": "(here)",
|
|
927
|
+
"claude_pane_more": "{count} more",
|
|
928
|
+
"claude_pane_less": "Show less",
|
|
929
|
+
"claude_pane_terminal": "Terminal",
|
|
930
|
+
"claude_pane_desktop": "Desktop",
|
|
931
|
+
"claude_pane_minutes": "{count}m",
|
|
932
|
+
"claude_pane_hours": "{count}h",
|
|
933
|
+
"claude_pane_days": "{count}d",
|
|
934
|
+
"claude_pane_seconds": "{count}s",
|
|
935
|
+
"claude_pane_minutes_ago": "{count}m ago",
|
|
936
|
+
"claude_pane_seconds_ago": "{count}s ago",
|
|
937
|
+
"claude_pane_just_now": "just now",
|
|
938
|
+
"claude_pane_failed": "failed",
|
|
939
|
+
"claude_pane_completed": "completed",
|
|
940
|
+
"claude_pane_completed_ago": "{ago} · {status}",
|
|
941
|
+
"claude_pane_quota_invalid": "Quota JSON has no agents",
|
|
942
|
+
"claude_pane_exit_code": "exit code {code}",
|
|
943
|
+
"claude_pane_quota_error": "Cannot read quota: {error}",
|
|
944
|
+
"claude_pane_session_error": "Cannot read sessions: {error}",
|
|
945
|
+
"claude_pane_home_missing": "HOME is not set",
|
|
946
|
+
"claude_pane_description": "Open the quota, Claude sessions and background jobs pane",
|
|
947
|
+
"claude_pane_title": "Usage",
|
|
948
|
+
"claude_pane_opened": "Usage pane opened.",
|
|
949
|
+
"claude_pane_compact_summary": "Sessions {busy} busy / {total} · Background jobs ",
|
|
950
|
+
"claude_pane_sidecar_error": "Cannot read pane settings: {error}",
|
|
951
|
+
"menu_badge_new": "New",
|
|
952
|
+
"claude_pane_menu": "Claude Code side pane",
|
|
953
|
+
"claude_pane_tooltip": "Show quotas, Claude sessions and background jobs in Claude Code",
|
|
954
|
+
"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.",
|
|
955
|
+
"claude_pane_disabled_msg": "Claude Code side pane removed.",
|
|
956
|
+
"claude_pane_action_failed": "Could not change the Claude Code side pane.",
|
|
957
|
+
"claude_pane_source_missing": "Pane source not found. Tried: {tried}",
|
|
958
|
+
"claude_pane_invalid_env": "Claude settings env must be an object and CLAUDE_CODE_PLUGIN_DIRS must be a string.",
|
|
959
|
+
"claude_pane_invalid_sidecar": "Invalid usage-pane.json",
|
|
960
|
+
"claude_pane_elapsed": "{minutes}m{seconds}s",
|
|
961
|
+
"claude_pane_remaining": "left",
|
|
962
|
+
"claude_pane_hours_ago": "{count}h ago"
|
|
857
963
|
},
|
|
858
964
|
"zh-CN": {
|
|
859
965
|
"panel_default_name": "默认",
|
|
@@ -1281,7 +1387,60 @@
|
|
|
1281
1387
|
"setup_hook_deleted": "✓ 已删除 hook:{path}",
|
|
1282
1388
|
"setup_status_file_deleted": "✓ 已删除状态文件:{path}",
|
|
1283
1389
|
"wintray_unavailable": "Windows 系统托盘界面尚未安装,改用终端 TUI。",
|
|
1284
|
-
"wintray_already_running": "usage 已在运行;请点击系统托盘的 usage 图标打开面板。"
|
|
1390
|
+
"wintray_already_running": "usage 已在运行;请点击系统托盘的 usage 图标打开面板。",
|
|
1391
|
+
"claude_pane_quota": "额度",
|
|
1392
|
+
"claude_pane_five_hour": "5小时",
|
|
1393
|
+
"claude_pane_week": "周",
|
|
1394
|
+
"claude_pane_period": "期间",
|
|
1395
|
+
"claude_pane_sessions": "Claude 对话",
|
|
1396
|
+
"claude_pane_busy": "忙",
|
|
1397
|
+
"claude_pane_waiting": "等你",
|
|
1398
|
+
"claude_pane_agent_running": "运行中",
|
|
1399
|
+
"claude_pane_agent_idle": "空闲",
|
|
1400
|
+
"claude_pane_agent_pending": "待运行",
|
|
1401
|
+
"claude_pane_busy_count": "{count} 忙 / {total}",
|
|
1402
|
+
"claude_pane_idle": "闲置 {ago}",
|
|
1403
|
+
"claude_pane_runs": "后台任务",
|
|
1404
|
+
"claude_pane_none": "(没有)",
|
|
1405
|
+
"claude_pane_untitled": "(无标题)",
|
|
1406
|
+
"claude_pane_no_project": "(无项目)",
|
|
1407
|
+
"claude_pane_here": "(这里)",
|
|
1408
|
+
"claude_pane_more": "还有 {count} 个",
|
|
1409
|
+
"claude_pane_less": "收起",
|
|
1410
|
+
"claude_pane_terminal": "终端",
|
|
1411
|
+
"claude_pane_desktop": "桌面",
|
|
1412
|
+
"claude_pane_minutes": "{count}分",
|
|
1413
|
+
"claude_pane_hours": "{count}时",
|
|
1414
|
+
"claude_pane_days": "{count}天",
|
|
1415
|
+
"claude_pane_seconds": "{count}秒",
|
|
1416
|
+
"claude_pane_minutes_ago": "{count}分前",
|
|
1417
|
+
"claude_pane_seconds_ago": "{count}秒前",
|
|
1418
|
+
"claude_pane_just_now": "刚刚",
|
|
1419
|
+
"claude_pane_failed": "失败",
|
|
1420
|
+
"claude_pane_completed": "完成",
|
|
1421
|
+
"claude_pane_completed_ago": "{ago}{status}",
|
|
1422
|
+
"claude_pane_quota_invalid": "额度 JSON 缺少 agents",
|
|
1423
|
+
"claude_pane_exit_code": "退出码 {code}",
|
|
1424
|
+
"claude_pane_quota_error": "额度读取失败:{error}",
|
|
1425
|
+
"claude_pane_session_error": "对话读取失败:{error}",
|
|
1426
|
+
"claude_pane_home_missing": "HOME 未设置",
|
|
1427
|
+
"claude_pane_description": "打开额度、Claude 对话与后台任务面板",
|
|
1428
|
+
"claude_pane_title": "使用状态",
|
|
1429
|
+
"claude_pane_opened": "已打开使用状态面板。",
|
|
1430
|
+
"claude_pane_compact_summary": "对话 {busy} 忙 / {total} · 后台任务 ",
|
|
1431
|
+
"claude_pane_sidecar_error": "面板设置读取失败:{error}",
|
|
1432
|
+
"menu_badge_new": "新",
|
|
1433
|
+
"claude_pane_menu": "Claude Code 侧边面板",
|
|
1434
|
+
"claude_pane_tooltip": "在 Claude Code 显示额度、对话与后台任务",
|
|
1435
|
+
"claude_pane_enabled_msg": "已开启侧边面板。打开新的 Claude Code 对话(或在对话里输入 /reload-plugins)后生效;终端宽度不够时输入 /usage-dash 打开面板。",
|
|
1436
|
+
"claude_pane_disabled_msg": "已移除 Claude Code 侧边面板。",
|
|
1437
|
+
"claude_pane_action_failed": "Claude Code 侧边面板切换失败。",
|
|
1438
|
+
"claude_pane_source_missing": "找不到面板源代码。已检查:{tried}",
|
|
1439
|
+
"claude_pane_invalid_env": "Claude 设置的 env 必须是对象,CLAUDE_CODE_PLUGIN_DIRS 必须是字符串。",
|
|
1440
|
+
"claude_pane_invalid_sidecar": "usage-pane.json 格式错误",
|
|
1441
|
+
"claude_pane_elapsed": "{minutes}分{seconds}秒",
|
|
1442
|
+
"claude_pane_remaining": "剩",
|
|
1443
|
+
"claude_pane_hours_ago": "{count}时前"
|
|
1285
1444
|
},
|
|
1286
1445
|
"ja": {
|
|
1287
1446
|
"panel_default_name": "デフォルト",
|
|
@@ -1709,7 +1868,60 @@
|
|
|
1709
1868
|
"setup_hook_deleted": "✓ hook を削除しました: {path}",
|
|
1710
1869
|
"setup_status_file_deleted": "✓ 状態ファイルを削除しました: {path}",
|
|
1711
1870
|
"wintray_unavailable": "Windows のシステムトレイ画面は未インストールです。ターミナル TUI に切り替えます。",
|
|
1712
|
-
"wintray_already_running": "usage は既に実行中です。トレイの usage アイコンをクリックしてパネルを開いてください。"
|
|
1871
|
+
"wintray_already_running": "usage は既に実行中です。トレイの usage アイコンをクリックしてパネルを開いてください。",
|
|
1872
|
+
"claude_pane_quota": "使用量",
|
|
1873
|
+
"claude_pane_five_hour": "5時間",
|
|
1874
|
+
"claude_pane_week": "週",
|
|
1875
|
+
"claude_pane_period": "期間",
|
|
1876
|
+
"claude_pane_sessions": "Claude セッション",
|
|
1877
|
+
"claude_pane_busy": "実行中",
|
|
1878
|
+
"claude_pane_waiting": "入力待ち",
|
|
1879
|
+
"claude_pane_agent_running": "実行中",
|
|
1880
|
+
"claude_pane_agent_idle": "待機中",
|
|
1881
|
+
"claude_pane_agent_pending": "実行待ち",
|
|
1882
|
+
"claude_pane_busy_count": "{count} 実行中 / {total}",
|
|
1883
|
+
"claude_pane_idle": "待機 {ago}",
|
|
1884
|
+
"claude_pane_runs": "バックグラウンド作業",
|
|
1885
|
+
"claude_pane_none": "(なし)",
|
|
1886
|
+
"claude_pane_untitled": "(無題)",
|
|
1887
|
+
"claude_pane_no_project": "(プロジェクトなし)",
|
|
1888
|
+
"claude_pane_here": "(ここ)",
|
|
1889
|
+
"claude_pane_more": "あと {count} 件",
|
|
1890
|
+
"claude_pane_less": "折りたたむ",
|
|
1891
|
+
"claude_pane_terminal": "ターミナル",
|
|
1892
|
+
"claude_pane_desktop": "デスクトップ",
|
|
1893
|
+
"claude_pane_minutes": "{count}分",
|
|
1894
|
+
"claude_pane_hours": "{count}時間",
|
|
1895
|
+
"claude_pane_days": "{count}日",
|
|
1896
|
+
"claude_pane_seconds": "{count}秒",
|
|
1897
|
+
"claude_pane_minutes_ago": "{count}分前",
|
|
1898
|
+
"claude_pane_seconds_ago": "{count}秒前",
|
|
1899
|
+
"claude_pane_just_now": "たった今",
|
|
1900
|
+
"claude_pane_failed": "失敗",
|
|
1901
|
+
"claude_pane_completed": "完了",
|
|
1902
|
+
"claude_pane_completed_ago": "{ago} · {status}",
|
|
1903
|
+
"claude_pane_quota_invalid": "使用量 JSON に agents がありません",
|
|
1904
|
+
"claude_pane_exit_code": "終了コード {code}",
|
|
1905
|
+
"claude_pane_quota_error": "使用量を読み込めません: {error}",
|
|
1906
|
+
"claude_pane_session_error": "セッションを読み込めません: {error}",
|
|
1907
|
+
"claude_pane_home_missing": "HOME が設定されていません",
|
|
1908
|
+
"claude_pane_description": "使用量、Claude セッション、バックグラウンド作業のパネルを開く",
|
|
1909
|
+
"claude_pane_title": "使用状況",
|
|
1910
|
+
"claude_pane_opened": "使用状況パネルを開きました。",
|
|
1911
|
+
"claude_pane_compact_summary": "セッション {busy} 実行中 / {total} · バックグラウンド作業 ",
|
|
1912
|
+
"claude_pane_sidecar_error": "パネル設定を読み込めません: {error}",
|
|
1913
|
+
"menu_badge_new": "新規",
|
|
1914
|
+
"claude_pane_menu": "Claude Code サイドパネル",
|
|
1915
|
+
"claude_pane_tooltip": "Claude Code に使用量、セッション、バックグラウンド作業を表示",
|
|
1916
|
+
"claude_pane_enabled_msg": "サイドパネルを有効にしました。新しい Claude Code セッションを開くか、会話で /reload-plugins を実行してください。ターミナルの幅が足りない場合は /usage-dash で開けます。",
|
|
1917
|
+
"claude_pane_disabled_msg": "Claude Code サイドパネルを削除しました。",
|
|
1918
|
+
"claude_pane_action_failed": "Claude Code サイドパネルを切り替えられませんでした。",
|
|
1919
|
+
"claude_pane_source_missing": "パネルのソースがありません。確認先: {tried}",
|
|
1920
|
+
"claude_pane_invalid_env": "Claude 設定の env はオブジェクト、CLAUDE_CODE_PLUGIN_DIRS は文字列が必要です。",
|
|
1921
|
+
"claude_pane_invalid_sidecar": "usage-pane.json の形式が不正です",
|
|
1922
|
+
"claude_pane_elapsed": "{minutes}分{seconds}秒",
|
|
1923
|
+
"claude_pane_remaining": "残り",
|
|
1924
|
+
"claude_pane_hours_ago": "{count}時間前"
|
|
1713
1925
|
},
|
|
1714
1926
|
"ko": {
|
|
1715
1927
|
"panel_default_name": "기본",
|
|
@@ -2137,6 +2349,59 @@
|
|
|
2137
2349
|
"setup_hook_deleted": "✓ hook 삭제됨: {path}",
|
|
2138
2350
|
"setup_status_file_deleted": "✓ 상태 파일 삭제됨: {path}",
|
|
2139
2351
|
"wintray_unavailable": "Windows 시스템 트레이 화면이 설치되지 않아 터미널 TUI로 전환합니다.",
|
|
2140
|
-
"wintray_already_running": "usage가 이미 실행 중입니다. 트레이의 usage 아이콘을 클릭해 패널을 여세요."
|
|
2352
|
+
"wintray_already_running": "usage가 이미 실행 중입니다. 트레이의 usage 아이콘을 클릭해 패널을 여세요.",
|
|
2353
|
+
"claude_pane_quota": "사용량",
|
|
2354
|
+
"claude_pane_five_hour": "5시간",
|
|
2355
|
+
"claude_pane_week": "주간",
|
|
2356
|
+
"claude_pane_period": "기간",
|
|
2357
|
+
"claude_pane_sessions": "Claude 대화",
|
|
2358
|
+
"claude_pane_busy": "작업 중",
|
|
2359
|
+
"claude_pane_waiting": "입력 대기",
|
|
2360
|
+
"claude_pane_agent_running": "실행 중",
|
|
2361
|
+
"claude_pane_agent_idle": "유휴",
|
|
2362
|
+
"claude_pane_agent_pending": "실행 대기",
|
|
2363
|
+
"claude_pane_busy_count": "{count} 작업 중 / {total}",
|
|
2364
|
+
"claude_pane_idle": "대기 {ago}",
|
|
2365
|
+
"claude_pane_runs": "백그라운드 작업",
|
|
2366
|
+
"claude_pane_none": "(없음)",
|
|
2367
|
+
"claude_pane_untitled": "(제목 없음)",
|
|
2368
|
+
"claude_pane_no_project": "(프로젝트 없음)",
|
|
2369
|
+
"claude_pane_here": "(여기)",
|
|
2370
|
+
"claude_pane_more": "{count}개 더",
|
|
2371
|
+
"claude_pane_less": "접기",
|
|
2372
|
+
"claude_pane_terminal": "터미널",
|
|
2373
|
+
"claude_pane_desktop": "데스크톱",
|
|
2374
|
+
"claude_pane_minutes": "{count}분",
|
|
2375
|
+
"claude_pane_hours": "{count}시간",
|
|
2376
|
+
"claude_pane_days": "{count}일",
|
|
2377
|
+
"claude_pane_seconds": "{count}초",
|
|
2378
|
+
"claude_pane_minutes_ago": "{count}분 전",
|
|
2379
|
+
"claude_pane_seconds_ago": "{count}초 전",
|
|
2380
|
+
"claude_pane_just_now": "방금",
|
|
2381
|
+
"claude_pane_failed": "실패",
|
|
2382
|
+
"claude_pane_completed": "완료",
|
|
2383
|
+
"claude_pane_completed_ago": "{ago} · {status}",
|
|
2384
|
+
"claude_pane_quota_invalid": "사용량 JSON에 agents가 없습니다",
|
|
2385
|
+
"claude_pane_exit_code": "종료 코드 {code}",
|
|
2386
|
+
"claude_pane_quota_error": "사용량을 읽을 수 없습니다: {error}",
|
|
2387
|
+
"claude_pane_session_error": "대화를 읽을 수 없습니다: {error}",
|
|
2388
|
+
"claude_pane_home_missing": "HOME이 설정되지 않았습니다",
|
|
2389
|
+
"claude_pane_description": "사용량, Claude 대화 및 백그라운드 작업 패널 열기",
|
|
2390
|
+
"claude_pane_title": "사용 현황",
|
|
2391
|
+
"claude_pane_opened": "사용 현황 패널을 열었습니다.",
|
|
2392
|
+
"claude_pane_compact_summary": "대화 {busy} 작업 중 / {total} · 백그라운드 작업 ",
|
|
2393
|
+
"claude_pane_sidecar_error": "패널 설정을 읽을 수 없습니다: {error}",
|
|
2394
|
+
"menu_badge_new": "신규",
|
|
2395
|
+
"claude_pane_menu": "Claude Code 사이드 패널",
|
|
2396
|
+
"claude_pane_tooltip": "Claude Code에 사용량, 대화 및 백그라운드 작업 표시",
|
|
2397
|
+
"claude_pane_enabled_msg": "사이드 패널을 켰습니다. 새 Claude Code 대화를 열거나 대화에서 /reload-plugins를 실행하세요. 터미널이 좁으면 /usage-dash로 패널을 여세요.",
|
|
2398
|
+
"claude_pane_disabled_msg": "Claude Code 사이드 패널을 제거했습니다.",
|
|
2399
|
+
"claude_pane_action_failed": "Claude Code 사이드 패널을 전환하지 못했습니다.",
|
|
2400
|
+
"claude_pane_source_missing": "패널 소스를 찾을 수 없습니다. 확인한 경로: {tried}",
|
|
2401
|
+
"claude_pane_invalid_env": "Claude 설정의 env는 객체, CLAUDE_CODE_PLUGIN_DIRS는 문자열이어야 합니다.",
|
|
2402
|
+
"claude_pane_invalid_sidecar": "usage-pane.json 형식이 잘못되었습니다",
|
|
2403
|
+
"claude_pane_elapsed": "{minutes}분{seconds}초",
|
|
2404
|
+
"claude_pane_remaining": "남음",
|
|
2405
|
+
"claude_pane_hours_ago": "{count}시간 전"
|
|
2141
2406
|
}
|
|
2142
2407
|
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Install the opt-in Claude Code pane without changing other plugins."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import filecmp
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from i18n import t as _t
|
|
13
|
+
from installer import setup_hook
|
|
14
|
+
from usage_common.usage_lang import detect_lang
|
|
15
|
+
|
|
16
|
+
INSTALL_DIR = Path.home() / ".usage" / "claude-pane" / "usage-dash"
|
|
17
|
+
PLUGIN_DIRS_KEY = "CLAUDE_CODE_PLUGIN_DIRS"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _same_path(left: str, right: str) -> bool:
|
|
21
|
+
return os.path.normpath(os.path.expanduser(left)) == os.path.normpath(os.path.expanduser(right))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _add_path(value: str, path: str) -> str:
|
|
25
|
+
parts = value.split(os.pathsep) if value else []
|
|
26
|
+
if not any(part and _same_path(part, path) for part in parts):
|
|
27
|
+
parts.append(path)
|
|
28
|
+
return os.pathsep.join(parts)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _remove_path(value: str, path: str) -> str:
|
|
32
|
+
parts = [part for part in value.split(os.pathsep) if not (part and _same_path(part, path))]
|
|
33
|
+
return os.pathsep.join(parts) if any(parts) else ""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _resolve_source() -> Path:
|
|
37
|
+
paths = [
|
|
38
|
+
Path(__file__).resolve().parent.parent / "claude_pane" / "usage-dash",
|
|
39
|
+
Path(sys.executable).resolve().parent.parent / "Resources" / "claude_pane" / "usage-dash",
|
|
40
|
+
]
|
|
41
|
+
for path in paths:
|
|
42
|
+
if path.is_dir():
|
|
43
|
+
return path
|
|
44
|
+
raise SystemExit(_t("claude_pane_source_missing", tried=", ".join(map(str, paths))))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _status_argv() -> list[str]:
|
|
48
|
+
if getattr(sys, "frozen", False):
|
|
49
|
+
resources = str(Path(sys.executable).resolve().parent.parent / "Resources")
|
|
50
|
+
major, minor = sys.version_info[:2]
|
|
51
|
+
bootstrap = (
|
|
52
|
+
f"import sys;sys.path[:0]=[{resources!r}+'/lib/python{major}{minor}.zip',"
|
|
53
|
+
f"{resources!r}+'/lib/python{major}.{minor}',"
|
|
54
|
+
f"{resources!r}+'/lib/python{major}.{minor}/lib-dynload',{resources!r}];"
|
|
55
|
+
"import usage_cli;sys.argv=['usage','status','--json'];usage_cli.main()"
|
|
56
|
+
)
|
|
57
|
+
return [
|
|
58
|
+
"/usr/bin/env",
|
|
59
|
+
f"PYTHONHOME={resources}",
|
|
60
|
+
f"RESOURCEPATH={resources}",
|
|
61
|
+
sys.executable,
|
|
62
|
+
"-c",
|
|
63
|
+
bootstrap,
|
|
64
|
+
]
|
|
65
|
+
return [
|
|
66
|
+
sys.executable,
|
|
67
|
+
str(Path(__file__).resolve().parent.parent / "usage_cli.py"),
|
|
68
|
+
"status",
|
|
69
|
+
"--json",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _sidecar_text() -> str:
|
|
74
|
+
from i18n import I18N_PATH
|
|
75
|
+
|
|
76
|
+
bundle = json.loads(I18N_PATH.read_text(encoding="utf-8"))
|
|
77
|
+
english = bundle["en"]
|
|
78
|
+
table = bundle.get(detect_lang(), english)
|
|
79
|
+
strings = {
|
|
80
|
+
key: table.get(key) or value
|
|
81
|
+
for key, value in english.items()
|
|
82
|
+
if key.startswith("claude_pane_")
|
|
83
|
+
}
|
|
84
|
+
return (
|
|
85
|
+
json.dumps(
|
|
86
|
+
{"strings": strings, "status_argv": _status_argv()}, ensure_ascii=False, indent=2
|
|
87
|
+
)
|
|
88
|
+
+ "\n"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _write_sidecar() -> None:
|
|
93
|
+
setup_hook._atomic_write_text(INSTALL_DIR / "usage-pane.json", _sidecar_text())
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _copy_pane() -> None:
|
|
97
|
+
source = _resolve_source()
|
|
98
|
+
if INSTALL_DIR.exists():
|
|
99
|
+
shutil.rmtree(INSTALL_DIR)
|
|
100
|
+
shutil.copytree(source, INSTALL_DIR, ignore=shutil.ignore_patterns("*.test.ts"))
|
|
101
|
+
_write_sidecar()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _installed_is_current() -> bool:
|
|
105
|
+
source = _resolve_source()
|
|
106
|
+
shipped = {
|
|
107
|
+
path.relative_to(source)
|
|
108
|
+
for path in source.rglob("*")
|
|
109
|
+
if path.is_file() and not path.name.endswith(".test.ts")
|
|
110
|
+
}
|
|
111
|
+
# Claude Code writes its own type stubs under .claude-plugin/types; they are not ours.
|
|
112
|
+
generated = Path(".claude-plugin", "types")
|
|
113
|
+
installed = {
|
|
114
|
+
path.relative_to(INSTALL_DIR)
|
|
115
|
+
for path in INSTALL_DIR.rglob("*")
|
|
116
|
+
if path.is_file() and generated not in path.relative_to(INSTALL_DIR).parents
|
|
117
|
+
} - {Path("usage-pane.json")}
|
|
118
|
+
if shipped != installed:
|
|
119
|
+
return False
|
|
120
|
+
if not all(filecmp.cmp(source / path, INSTALL_DIR / path, shallow=False) for path in shipped):
|
|
121
|
+
return False
|
|
122
|
+
try:
|
|
123
|
+
return (INSTALL_DIR / "usage-pane.json").read_text(encoding="utf-8") == _sidecar_text()
|
|
124
|
+
except OSError:
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _env(settings: dict[str, object]) -> dict[str, str]:
|
|
129
|
+
env = settings.get("env", {})
|
|
130
|
+
if not isinstance(env, dict) or not isinstance(env.get(PLUGIN_DIRS_KEY, ""), str):
|
|
131
|
+
raise SystemExit(_t("claude_pane_invalid_env"))
|
|
132
|
+
return env
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def enable_claude_pane() -> int:
|
|
136
|
+
_copy_pane()
|
|
137
|
+
settings = setup_hook._load_settings()
|
|
138
|
+
env = _env(settings)
|
|
139
|
+
env[PLUGIN_DIRS_KEY] = _add_path(env.get(PLUGIN_DIRS_KEY, ""), str(INSTALL_DIR.absolute()))
|
|
140
|
+
settings["env"] = env
|
|
141
|
+
setup_hook._save_settings(settings)
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def disable_claude_pane() -> int:
|
|
146
|
+
settings = setup_hook._load_settings()
|
|
147
|
+
env = _env(settings)
|
|
148
|
+
if PLUGIN_DIRS_KEY in env:
|
|
149
|
+
value = _remove_path(env[PLUGIN_DIRS_KEY], str(INSTALL_DIR.absolute()))
|
|
150
|
+
if value:
|
|
151
|
+
env[PLUGIN_DIRS_KEY] = value
|
|
152
|
+
else:
|
|
153
|
+
env.pop(PLUGIN_DIRS_KEY)
|
|
154
|
+
if not env:
|
|
155
|
+
settings.pop("env", None)
|
|
156
|
+
setup_hook._save_settings(settings)
|
|
157
|
+
if INSTALL_DIR.exists():
|
|
158
|
+
shutil.rmtree(INSTALL_DIR)
|
|
159
|
+
return 0
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def is_claude_pane_enabled() -> bool:
|
|
163
|
+
try:
|
|
164
|
+
value = _env(setup_hook._load_settings()).get(PLUGIN_DIRS_KEY, "")
|
|
165
|
+
except SystemExit:
|
|
166
|
+
return False
|
|
167
|
+
return INSTALL_DIR.is_dir() and any(
|
|
168
|
+
part and _same_path(part, str(INSTALL_DIR.absolute())) for part in value.split(os.pathsep)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def refresh_claude_pane() -> None:
|
|
173
|
+
"""Re-copy an enabled pane whose files differ from this build's, e.g. after an app upgrade."""
|
|
174
|
+
if is_claude_pane_enabled() and not _installed_is_current():
|
|
175
|
+
_copy_pane()
|
|
@@ -23,6 +23,7 @@ from installer.statusline_settings import (
|
|
|
23
23
|
_toggle_statusline_settings,
|
|
24
24
|
)
|
|
25
25
|
from menubar.chrome import _make_alert
|
|
26
|
+
from menubar.new_badge import dismiss_badge
|
|
26
27
|
from usage_common.usage_lang import detect_lang
|
|
27
28
|
|
|
28
29
|
logger = logging.getLogger(__name__)
|
|
@@ -200,3 +201,50 @@ def show_forwarder_mode_prompt_if_needed(language: str | None = None) -> None:
|
|
|
200
201
|
except Exception:
|
|
201
202
|
if os.environ.get("USAGE_DEBUG") == "1":
|
|
202
203
|
logger.warning("forwarder prompt dismissal failed", exc_info=True)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class _PaneApp(_ActionApp, Protocol):
|
|
207
|
+
def _mark_switch_menu_action(self) -> None: ...
|
|
208
|
+
def _refresh(self) -> None: ...
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def toggle_claude_pane(app: _PaneApp) -> None:
|
|
212
|
+
import threading
|
|
213
|
+
|
|
214
|
+
dismiss_badge("claude_pane")
|
|
215
|
+
app._mark_switch_menu_action()
|
|
216
|
+
threading.Thread(target=toggle_claude_pane_in_background, args=(app,), daemon=True).start()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def toggle_claude_pane_in_background(app: _ActionApp) -> None:
|
|
220
|
+
from installer import claude_pane
|
|
221
|
+
|
|
222
|
+
output = io.StringIO()
|
|
223
|
+
ok = False
|
|
224
|
+
enabled = False
|
|
225
|
+
try:
|
|
226
|
+
with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output):
|
|
227
|
+
enabled = not claude_pane.is_claude_pane_enabled()
|
|
228
|
+
action = claude_pane.enable_claude_pane if enabled else claude_pane.disable_claude_pane
|
|
229
|
+
ok = action() == 0
|
|
230
|
+
except SystemExit as exc:
|
|
231
|
+
print(exc.code, file=output)
|
|
232
|
+
except Exception as exc:
|
|
233
|
+
print(f"{type(exc).__name__}: {exc}", file=output)
|
|
234
|
+
app.performSelectorOnMainThread_withObject_waitUntilDone_(
|
|
235
|
+
"_finishClaudePane:",
|
|
236
|
+
{"ok": ok, "enabled": enabled, "output": output.getvalue().strip()},
|
|
237
|
+
False,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def finish_claude_pane(app: _PaneApp, result: dict[str, Any]) -> None:
|
|
242
|
+
alert = _make_alert()
|
|
243
|
+
if result.get("ok"):
|
|
244
|
+
key = "claude_pane_enabled_msg" if result.get("enabled") else "claude_pane_disabled_msg"
|
|
245
|
+
alert.setMessageText_(_t(app.language, key))
|
|
246
|
+
else:
|
|
247
|
+
alert.setMessageText_(_t(app.language, "claude_pane_action_failed"))
|
|
248
|
+
alert.setInformativeText_(str(result.get("output") or ""))
|
|
249
|
+
alert.runModal()
|
|
250
|
+
app._refresh()
|
|
@@ -579,6 +579,12 @@ class AppDelegate(NSObject):
|
|
|
579
579
|
alert.runModal()
|
|
580
580
|
self._refresh()
|
|
581
581
|
|
|
582
|
+
def toggleClaudePane_(self, sender: Any) -> None:
|
|
583
|
+
menubar_actions.toggle_claude_pane(self)
|
|
584
|
+
|
|
585
|
+
def _finishClaudePane_(self, result: dict[str, Any]) -> None:
|
|
586
|
+
menubar_actions.finish_claude_pane(self, result)
|
|
587
|
+
|
|
582
588
|
def toggleTerseMode_(self, sender: Any) -> None:
|
|
583
589
|
self._mark_switch_menu_action()
|
|
584
590
|
thread = threading.Thread(target=self._toggle_terse_mode_in_background, daemon=True)
|
|
@@ -11,7 +11,8 @@ from typing import Any, Protocol
|
|
|
11
11
|
from AppKit import NSMakePoint, NSMenu, NSMenuItem
|
|
12
12
|
|
|
13
13
|
from i18n import _t
|
|
14
|
-
from installer import login_item
|
|
14
|
+
from installer import claude_pane, login_item
|
|
15
|
+
from menubar.new_badge import apply_badge
|
|
15
16
|
from menubar.prefs import (
|
|
16
17
|
_hide_agy_enabled,
|
|
17
18
|
_hide_claude_enabled,
|
|
@@ -178,6 +179,16 @@ def build_switch_menu(app: _SwitchMenuApp, sender: Any) -> None:
|
|
|
178
179
|
tooltip_key="terse_mode_tooltip",
|
|
179
180
|
)
|
|
180
181
|
)
|
|
182
|
+
claude_pane_item = build_menu_item(
|
|
183
|
+
app.language,
|
|
184
|
+
"claude_pane_menu",
|
|
185
|
+
"toggleClaudePane:",
|
|
186
|
+
target=app,
|
|
187
|
+
state=claude_pane.is_claude_pane_enabled(),
|
|
188
|
+
tooltip_key="claude_pane_tooltip",
|
|
189
|
+
)
|
|
190
|
+
apply_badge(claude_pane_item, app.language, "claude_pane")
|
|
191
|
+
menu.addItem_(claude_pane_item)
|
|
181
192
|
app._switch_menu_action_taken = False
|
|
182
193
|
menu.popUpMenuPositioningItem_atLocation_inView_(None, NSMakePoint(0, 0), sender)
|
|
183
194
|
# Dismissing the menu without picking anything used to close the panel:
|