usage-cli 0.32.6__tar.gz → 0.33.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.32.6 → usage_cli-0.33.0}/PKG-INFO +6 -2
- {usage_cli-0.32.6 → usage_cli-0.33.0}/README.md +5 -1
- {usage_cli-0.32.6 → usage_cli-0.33.0}/i18n.json +140 -5
- {usage_cli-0.32.6 → usage_cli-0.33.0}/installer/claude_pane.py +72 -33
- {usage_cli-0.32.6 → usage_cli-0.33.0}/installer/session_hooks.py +116 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/actions.py +89 -6
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/menu.py +44 -8
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/refresh.py +3 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/pyproject.toml +3 -1
- usage_cli-0.33.0/quota/quota_snapshot.py +32 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/quota/usage_rate.py +39 -3
- {usage_cli-0.32.6 → usage_cli-0.33.0}/setup_app.py +1 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_claude_pane.py +76 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_hook_scripts_are_standalone.py +1 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_menubar.py +90 -10
- usage_cli-0.33.0/tests/test_quota_aware.py +252 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_rate.py +72 -1
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_cli.egg-info/PKG-INFO +6 -2
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_cli.egg-info/SOURCES.txt +3 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_cli.egg-info/top_level.txt +1 -0
- usage_cli-0.33.0/usage_quota_aware.py +150 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/LICENSE +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/THIRD_PARTY_NOTICES.md +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/agy.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/claude.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/codex.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/grok.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/muse.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/rate_limits.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/registry.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/adapters/types.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/aggregator.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/blocks.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/diagnoser.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/dispatch_ledger.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/insights.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/persona_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/reporter.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/subscription.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/analyzer/usage_snapshot.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/doctor.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/fsevents_watch.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/i18n.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/installer/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/installer/codex_hook_trust.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/installer/login_item.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/installer/setup_hook.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/installer/statusline_settings.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/agy_disk_cache.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/agy_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/agy_quota_probe.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/cache_quarantine.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/claude_paths.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/codex_disk_cache.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/codex_events.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/codex_fork_replay.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/codex_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/codex_paths.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/disk_cache_common.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/disk_cache_lifecycle.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/grok_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/grok_quota_probe.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/history_disk_cache.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/history_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/jsonl_limits.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/jsonl_utils.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/loaders/muse_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/main.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/agy.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/app.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/chrome.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/grok.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/manual_refresh.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/new_badge.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/notify.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/popover.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/prefs.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/state.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/title.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/update.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/menubar/whats_new.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/base.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/dynamic_height.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/panel_scale.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/panel_window.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/panel_window_state.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/payload.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/web_panel.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/panels/window_drag.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/prefs.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/pricing.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/project_resolver.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/quota/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/quota/agy_window_keeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/quota/burn_rate.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/quota/codex_window_keeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/quota/keeper_outcome.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/quota/window_keeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/service_status.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/setup.cfg +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_adapters_claude.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_adapters_codex.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_adapters_rate_limits.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_aggregator.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_agy_adapter.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_agy_disk_cache.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_agy_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_agy_quota_probe.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_agy_window_keeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_analyzer_blocks.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_analyzer_pipeline.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_burn_rate.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_cache_quarantine.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_check_upstream.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_claude_paths.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_codex_consistency.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_codex_hook_trust.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_codex_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_codex_window_keeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_diagnoser.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_disk_cache_shards.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_dispatch_ledger.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_doctor.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_dynamic_height.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_fsevents_watch.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_grok_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_grok_quota_probe.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_history_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_html_report_snapshot.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_i18n_format_fallback.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_i18n_key_parity.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_i18n_packaged_path.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_insights.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_jsonl_utils.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_keeper_menu.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_keeper_outcome.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_login_item.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_main.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_manual_refresh.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_menubar_agy.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_menubar_grok.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_menubar_notify.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_menubar_prefs.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_menubar_state.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_menubar_update.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_muse_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_new_badge.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_packaged_resources.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_packaging.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_panel_footer_layout.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_panel_scale.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_panel_window_state.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_panels.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_panels_lazy_import.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_persona_loader.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_pricing.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_project_resolver.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_quota_label.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_report_daily_chart.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_report_filter_js.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_reporter_build_data.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_service_status.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_session_resume.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_setup_app.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_setup_hook.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_setup_hook_coexistence.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_setup_hook_resume.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_setup_hook_terse.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_subprocess_utils.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_subscription.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_time_utils.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_tui.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_tui_sprite.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_ui_tables.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_update_checker.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_update_gate.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_update_release_notes.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_cli.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_client.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_diagnosis_snapshot.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_dir_sweeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_lang.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_logging.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_notifications.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_snapshot.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_snapshot_replay.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_statusline.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_statusline_agy.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_statusline_forwarder.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_statusline_grok.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_terse_mode.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_usage_terse_reminder.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_web_panel_payload.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_whats_new.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_wheel_packaging.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_win_login_item.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_window_keeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_windows_manifest.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tests/test_wintray.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tui/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tui/app.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/tui/sprite.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/html_report.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/report_charts.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/report_daily_chart.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/report_filter.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/report_ledger.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/report_scripts.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/report_styles.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/ui/tables.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/updates/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/updates/checker.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/updates/gate.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/updates/release_notes.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/updates/whats_new.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_cli.egg-info/dependency_links.txt +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_cli.egg-info/entry_points.txt +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_cli.egg-info/requires.txt +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_cli.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_client.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_common/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_common/subprocess_utils.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_common/time_utils.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_common/usage_dir_sweeper.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_common/usage_lang.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_common/usage_logging.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_diagnosis_snapshot.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_notifications.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_session_resume.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_statusline.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_statusline_agy.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_statusline_forwarder.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_statusline_grok.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_terse_mode.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/usage_terse_reminder.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/__init__.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/app.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/login_item.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/menu.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/prefs.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/quota_label.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/taskbar_obstacles.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/taskbar_overlay.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/watch.py +0 -0
- {usage_cli-0.32.6 → usage_cli-0.33.0}/wintray/whats_new.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usage-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.33.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
|
|
@@ -78,6 +78,8 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
78
78
|
- **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.
|
|
79
79
|
- **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.
|
|
80
80
|
- **Claude Code side pane (macOS and Windows):** Quotas, conversations, and background jobs inside Claude Code. [See the side pane](#claude-code-side-pane).
|
|
81
|
+
- **Beginner Mode (macOS):** Off by default. After Claude Code answers, up to three technical terms from that answer appear above the prompt, each with a one-line plain explanation in your UI language. Press 9 to mark them understood; terms you skip come back after 1, 3, then 7 days. Understood terms return as a one-question multiple-choice quiz 7, 21, then 60 days later, at most one a day; a wrong answer puts the term back in the hints. `/terms` opens your term history. Picking terms asks Claude Haiku through your Claude Code, so it uses a little Claude quota, and it pauses while your 5-hour quota is at 90% or more.
|
|
82
|
+
- **Quota-Aware Mode (macOS):** Off by default. When a 5-hour quota passes 80%, 90%, or 95%, or a weekly quota passes 95%, Claude Code gets one line with what is left and when it resets. Before a big task, Claude then tells you and lets you pick a smaller part or wait for the reset. It covers Claude Code, Codex, and Antigravity quota, says each level once per conversation, and makes no extra model calls.
|
|
81
83
|
- **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.
|
|
82
84
|
- **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).
|
|
83
85
|
- **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.
|
|
@@ -102,6 +104,7 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
102
104
|
- Claude Code and Codex numbers are read from local log files on your machine.
|
|
103
105
|
- Antigravity quota requires network access, and only if you use it: quota is fetched from Google's official quota endpoint using the OAuth credential the Antigravity CLI already stored after sign-in — read from macOS Keychain, Windows Credential Manager, or a local token file depending on CLI version. `usage` reads that credential without writing it back and keeps any refreshed access token in memory only; the call itself reads quota metadata.
|
|
104
106
|
- Background network activity: the Antigravity quota/token endpoints above, public Claude and Codex status pages to flag outages, a public model-pricing table to estimate cost (falls back to built-in prices offline), and occasionally checking GitHub for a new version. Claude Code and Codex log contents are never uploaded.
|
|
107
|
+
- Beginner Mode, only if you turn it on, sends Claude Code's latest answer to Claude Haiku through your own Claude Code sign-in to pick terms. Your term list stays in `~/.usage/glossary.json`.
|
|
105
108
|
|
|
106
109
|
## Requirements
|
|
107
110
|
|
|
@@ -162,7 +165,7 @@ See your quota, other conversations, and background jobs without leaving Claude
|
|
|
162
165
|
|
|
163
166
|
**How to enable**
|
|
164
167
|
|
|
165
|
-
1. In the usage menu bar menu (macOS) or the system-tray panel menu (Windows), check **Claude Code side pane**.
|
|
168
|
+
1. In the usage menu bar menu (macOS) or the system-tray panel menu (Windows), check **Claude Code side pane**. On macOS it sits under the **Claude Code** submenu as **Side pane**.
|
|
166
169
|
2. Open a new conversation or run `/reload-plugins`.
|
|
167
170
|
3. At terminal widths ≥144 columns, the pane opens on the right automatically. In narrower terminals, enter `/usage-dash`.
|
|
168
171
|
|
|
@@ -251,6 +254,7 @@ If the menu bar shows `--`, it's usually not broken — there's just no local da
|
|
|
251
254
|
| AI Update Daily | ✅ | — | — |
|
|
252
255
|
| Progress Concierge & Token Saver | ✅ | — | — |
|
|
253
256
|
| Token-waste Health Check | ✅ | — | — |
|
|
257
|
+
| Claude Code Beginner Mode & Quota-Aware Mode | ✅ | — | — |
|
|
254
258
|
| Open-source license | AGPL-3.0 | MIT | — |
|
|
255
259
|
|
|
256
260
|
## When usage Isn't the Right Fit
|
|
@@ -57,6 +57,8 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
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
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
|
+
- **Beginner Mode (macOS):** Off by default. After Claude Code answers, up to three technical terms from that answer appear above the prompt, each with a one-line plain explanation in your UI language. Press 9 to mark them understood; terms you skip come back after 1, 3, then 7 days. Understood terms return as a one-question multiple-choice quiz 7, 21, then 60 days later, at most one a day; a wrong answer puts the term back in the hints. `/terms` opens your term history. Picking terms asks Claude Haiku through your Claude Code, so it uses a little Claude quota, and it pauses while your 5-hour quota is at 90% or more.
|
|
61
|
+
- **Quota-Aware Mode (macOS):** Off by default. When a 5-hour quota passes 80%, 90%, or 95%, or a weekly quota passes 95%, Claude Code gets one line with what is left and when it resets. Before a big task, Claude then tells you and lets you pick a smaller part or wait for the reset. It covers Claude Code, Codex, and Antigravity quota, says each level once per conversation, and makes no extra model calls.
|
|
60
62
|
- **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
63
|
- **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
64
|
- **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.
|
|
@@ -81,6 +83,7 @@ It lands in your Applications folder automatically. Open it once; if macOS 15 or
|
|
|
81
83
|
- Claude Code and Codex numbers are read from local log files on your machine.
|
|
82
84
|
- Antigravity quota requires network access, and only if you use it: quota is fetched from Google's official quota endpoint using the OAuth credential the Antigravity CLI already stored after sign-in — read from macOS Keychain, Windows Credential Manager, or a local token file depending on CLI version. `usage` reads that credential without writing it back and keeps any refreshed access token in memory only; the call itself reads quota metadata.
|
|
83
85
|
- Background network activity: the Antigravity quota/token endpoints above, public Claude and Codex status pages to flag outages, a public model-pricing table to estimate cost (falls back to built-in prices offline), and occasionally checking GitHub for a new version. Claude Code and Codex log contents are never uploaded.
|
|
86
|
+
- Beginner Mode, only if you turn it on, sends Claude Code's latest answer to Claude Haiku through your own Claude Code sign-in to pick terms. Your term list stays in `~/.usage/glossary.json`.
|
|
84
87
|
|
|
85
88
|
## Requirements
|
|
86
89
|
|
|
@@ -141,7 +144,7 @@ See your quota, other conversations, and background jobs without leaving Claude
|
|
|
141
144
|
|
|
142
145
|
**How to enable**
|
|
143
146
|
|
|
144
|
-
1. In the usage menu bar menu (macOS) or the system-tray panel menu (Windows), check **Claude Code side pane**.
|
|
147
|
+
1. In the usage menu bar menu (macOS) or the system-tray panel menu (Windows), check **Claude Code side pane**. On macOS it sits under the **Claude Code** submenu as **Side pane**.
|
|
145
148
|
2. Open a new conversation or run `/reload-plugins`.
|
|
146
149
|
3. At terminal widths ≥144 columns, the pane opens on the right automatically. In narrower terminals, enter `/usage-dash`.
|
|
147
150
|
|
|
@@ -230,6 +233,7 @@ If the menu bar shows `--`, it's usually not broken — there's just no local da
|
|
|
230
233
|
| AI Update Daily | ✅ | — | — |
|
|
231
234
|
| Progress Concierge & Token Saver | ✅ | — | — |
|
|
232
235
|
| Token-waste Health Check | ✅ | — | — |
|
|
236
|
+
| Claude Code Beginner Mode & Quota-Aware Mode | ✅ | — | — |
|
|
233
237
|
| Open-source license | AGPL-3.0 | MIT | — |
|
|
234
238
|
|
|
235
239
|
## When usage Isn't the Right Fit
|
|
@@ -490,6 +490,8 @@
|
|
|
490
490
|
"claude_pane_sidecar_error": "面板設定讀取失敗:{error}",
|
|
491
491
|
"menu_badge_new": "新",
|
|
492
492
|
"claude_pane_menu": "Claude Code 側邊面板",
|
|
493
|
+
"claude_code_section": "Claude Code",
|
|
494
|
+
"claude_pane_section_menu": "側邊面板",
|
|
493
495
|
"claude_pane_tooltip": "在 Claude Code 顯示額度、對話與背景工作",
|
|
494
496
|
"claude_pane_enabled_msg": "已開啟側邊面板。開新的 Claude Code 對話(或在對話裡打 /reload-plugins)後生效;終端機寬度不夠時打 /usage-dash 叫出面板。",
|
|
495
497
|
"claude_pane_fullscreen_enabled_note": "已一併開啟 Claude Code 的全螢幕介面,讓面板顯示在右側。取消勾選側邊面板時會改回。",
|
|
@@ -515,7 +517,32 @@
|
|
|
515
517
|
"keeper_failed_line": "上次失敗 {time}:{tool}({reason})",
|
|
516
518
|
"terse_codex_hook_untrusted": "Codex 還沒批准 usage 的精簡模式 hook,Codex 那邊不會生效。在 Codex 裡打 /hooks 批准它。",
|
|
517
519
|
"terse_codex_hook_disabled": "你在 Codex 停用了 usage 的精簡模式 hook。在 Codex 裡打 /hooks 重新啟用。",
|
|
518
|
-
"claude_pane_stopped": "已停止"
|
|
520
|
+
"claude_pane_stopped": "已停止",
|
|
521
|
+
"claude_beginner_menu": "新手模式",
|
|
522
|
+
"claude_beginner_tooltip": "幫你看懂 Claude 的回答:回答完,在輸入框上方用白話提示最多 3 個專有名詞。按 9 標成「懂了」就不再提示,之後開新對話時會出選擇題小考。會用到少量 Claude 額度。",
|
|
523
|
+
"claude_beginner_title": "名詞提示",
|
|
524
|
+
"claude_beginner_history_title": "名詞提示紀錄",
|
|
525
|
+
"claude_beginner_description": "開啟名詞提示紀錄",
|
|
526
|
+
"claude_beginner_all_known": "都懂了",
|
|
527
|
+
"claude_beginner_dismiss": "收起",
|
|
528
|
+
"claude_beginner_known": "懂了",
|
|
529
|
+
"claude_beginner_unknown": "還不懂",
|
|
530
|
+
"claude_beginner_empty": "還沒有名詞紀錄。",
|
|
531
|
+
"claude_beginner_opened": "已開啟名詞提示紀錄。",
|
|
532
|
+
"claude_beginner_quiz_title": "小考",
|
|
533
|
+
"claude_beginner_quiz_question": "{term} 是什麼意思?",
|
|
534
|
+
"claude_beginner_quiz_right": "答對了!{days} 天後再考一次。",
|
|
535
|
+
"claude_beginner_quiz_done": "答對了!這個詞你已經學會了。",
|
|
536
|
+
"claude_beginner_quiz_wrong": "答錯了。正確答案:{answer}。這個詞之後會再出現在名詞提示裡。",
|
|
537
|
+
"claude_beginner_enabled_msg": "已開啟新手模式。重開 Claude Code 後生效。",
|
|
538
|
+
"claude_beginner_disabled_msg": "已關閉新手模式。重開 Claude Code 後生效。",
|
|
539
|
+
"claude_beginner_action_failed": "無法切換新手模式",
|
|
540
|
+
"quota_aware_menu": "額度感知模式",
|
|
541
|
+
"quota_aware_tooltip": "額度快用完時,Claude 開大任務前會先告訴你剩多少、幾點重置,讓你選先做小的還是等重置。",
|
|
542
|
+
"quota_aware_enabled_msg": "已開啟額度感知模式。請重啟 Claude Code 套用。",
|
|
543
|
+
"quota_aware_disabled_msg": "已關閉額度感知模式。請重啟 Claude Code 套用。",
|
|
544
|
+
"quota_aware_action_failed": "無法切換額度感知模式。",
|
|
545
|
+
"claude_beginner_error": "新手模式:{error}"
|
|
519
546
|
},
|
|
520
547
|
"en": {
|
|
521
548
|
"panel_default_name": "Default",
|
|
@@ -1008,6 +1035,8 @@
|
|
|
1008
1035
|
"claude_pane_sidecar_error": "Cannot read pane settings: {error}",
|
|
1009
1036
|
"menu_badge_new": "New",
|
|
1010
1037
|
"claude_pane_menu": "Claude Code side pane",
|
|
1038
|
+
"claude_code_section": "Claude Code",
|
|
1039
|
+
"claude_pane_section_menu": "Side pane",
|
|
1011
1040
|
"claude_pane_tooltip": "Show quotas, Claude sessions and background jobs in Claude Code",
|
|
1012
1041
|
"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.",
|
|
1013
1042
|
"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.",
|
|
@@ -1033,7 +1062,32 @@
|
|
|
1033
1062
|
"keeper_failed_line": "Last failure {time}: {tool} ({reason})",
|
|
1034
1063
|
"terse_codex_hook_untrusted": "Codex has not approved usage’s terse mode hook, so terse mode will not work in Codex. Run /hooks in Codex to approve it.",
|
|
1035
1064
|
"terse_codex_hook_disabled": "You disabled usage’s terse mode hook in Codex. Run /hooks in Codex to enable it again.",
|
|
1036
|
-
"claude_pane_stopped": "stopped"
|
|
1065
|
+
"claude_pane_stopped": "stopped",
|
|
1066
|
+
"claude_beginner_menu": "Beginner Mode",
|
|
1067
|
+
"claude_beginner_tooltip": "Understand Claude’s answers: after each answer, see up to 3 technical terms explained above the prompt. Press 9 to mark them as understood so they stop appearing; a new conversation will later quiz you on them with a multiple-choice question. Uses a small amount of Claude quota.",
|
|
1068
|
+
"claude_beginner_title": "Terms",
|
|
1069
|
+
"claude_beginner_history_title": "Term history",
|
|
1070
|
+
"claude_beginner_description": "Open your term history",
|
|
1071
|
+
"claude_beginner_all_known": "All understood",
|
|
1072
|
+
"claude_beginner_dismiss": "Dismiss",
|
|
1073
|
+
"claude_beginner_known": "Understood",
|
|
1074
|
+
"claude_beginner_unknown": "Not yet understood",
|
|
1075
|
+
"claude_beginner_empty": "No terms yet.",
|
|
1076
|
+
"claude_beginner_opened": "Term history opened.",
|
|
1077
|
+
"claude_beginner_quiz_title": "Quick quiz",
|
|
1078
|
+
"claude_beginner_quiz_question": "What does {term} mean?",
|
|
1079
|
+
"claude_beginner_quiz_right": "Correct! Next quiz in {days} days.",
|
|
1080
|
+
"claude_beginner_quiz_done": "Correct! You have learned this term.",
|
|
1081
|
+
"claude_beginner_quiz_wrong": "Not quite. The answer is: {answer}. This term will show up in the hints again.",
|
|
1082
|
+
"claude_beginner_enabled_msg": "Beginner Mode enabled. Restart Claude Code to apply.",
|
|
1083
|
+
"claude_beginner_disabled_msg": "Beginner Mode disabled. Restart Claude Code to apply.",
|
|
1084
|
+
"claude_beginner_action_failed": "Could not change Beginner Mode",
|
|
1085
|
+
"quota_aware_menu": "Quota-Aware Mode",
|
|
1086
|
+
"quota_aware_tooltip": "When quota is running low, Claude tells you how much remains and when it resets before starting a large task, so you can choose a smaller part now or wait for the reset.",
|
|
1087
|
+
"quota_aware_enabled_msg": "Quota-Aware Mode enabled. Restart Claude Code to apply.",
|
|
1088
|
+
"quota_aware_disabled_msg": "Quota-Aware Mode disabled. Restart Claude Code to apply.",
|
|
1089
|
+
"quota_aware_action_failed": "Could not change Quota-Aware Mode.",
|
|
1090
|
+
"claude_beginner_error": "Beginner Mode: {error}"
|
|
1037
1091
|
},
|
|
1038
1092
|
"zh-CN": {
|
|
1039
1093
|
"panel_default_name": "默认",
|
|
@@ -1526,6 +1580,8 @@
|
|
|
1526
1580
|
"claude_pane_sidecar_error": "面板设置读取失败:{error}",
|
|
1527
1581
|
"menu_badge_new": "新",
|
|
1528
1582
|
"claude_pane_menu": "Claude Code 侧边面板",
|
|
1583
|
+
"claude_code_section": "Claude Code",
|
|
1584
|
+
"claude_pane_section_menu": "侧边面板",
|
|
1529
1585
|
"claude_pane_tooltip": "在 Claude Code 显示额度、对话与后台任务",
|
|
1530
1586
|
"claude_pane_enabled_msg": "已开启侧边面板。打开新的 Claude Code 对话(或在对话里输入 /reload-plugins)后生效;终端宽度不够时输入 /usage-dash 打开面板。",
|
|
1531
1587
|
"claude_pane_fullscreen_enabled_note": "已一并开启 Claude Code 的全屏界面,让面板显示在右侧。取消勾选侧边面板时会改回。",
|
|
@@ -1551,7 +1607,32 @@
|
|
|
1551
1607
|
"keeper_failed_line": "上次失败 {time}:{tool}({reason})",
|
|
1552
1608
|
"terse_codex_hook_untrusted": "Codex 还没批准 usage 的精简模式 hook,Codex 那边不会生效。在 Codex 里输入 /hooks 批准它。",
|
|
1553
1609
|
"terse_codex_hook_disabled": "你在 Codex 停用了 usage 的精简模式 hook。在 Codex 里输入 /hooks 重新启用。",
|
|
1554
|
-
"claude_pane_stopped": "已停止"
|
|
1610
|
+
"claude_pane_stopped": "已停止",
|
|
1611
|
+
"claude_beginner_menu": "新手模式",
|
|
1612
|
+
"claude_beginner_tooltip": "帮你看懂 Claude 的回答:回答完,在输入框上方用白话提示最多 3 个专业术语。按 9 标成“懂了”就不再提示,之后开新对话时会出选择题小测验。会用到少量 Claude 额度。",
|
|
1613
|
+
"claude_beginner_title": "术语提示",
|
|
1614
|
+
"claude_beginner_history_title": "术语提示记录",
|
|
1615
|
+
"claude_beginner_description": "打开术语提示记录",
|
|
1616
|
+
"claude_beginner_all_known": "都懂了",
|
|
1617
|
+
"claude_beginner_dismiss": "收起",
|
|
1618
|
+
"claude_beginner_known": "懂了",
|
|
1619
|
+
"claude_beginner_unknown": "还不懂",
|
|
1620
|
+
"claude_beginner_empty": "还没有术语记录。",
|
|
1621
|
+
"claude_beginner_opened": "已打开术语提示记录。",
|
|
1622
|
+
"claude_beginner_quiz_title": "小测验",
|
|
1623
|
+
"claude_beginner_quiz_question": "{term} 是什么意思?",
|
|
1624
|
+
"claude_beginner_quiz_right": "答对了!{days} 天后再考一次。",
|
|
1625
|
+
"claude_beginner_quiz_done": "答对了!这个词你已经学会了。",
|
|
1626
|
+
"claude_beginner_quiz_wrong": "答错了。正确答案:{answer}。这个词之后会再出现在术语提示里。",
|
|
1627
|
+
"claude_beginner_enabled_msg": "已开启新手模式。重启 Claude Code 后生效。",
|
|
1628
|
+
"claude_beginner_disabled_msg": "已关闭新手模式。重启 Claude Code 后生效。",
|
|
1629
|
+
"claude_beginner_action_failed": "无法切换新手模式",
|
|
1630
|
+
"quota_aware_menu": "额度感知模式",
|
|
1631
|
+
"quota_aware_tooltip": "额度快用完时,Claude 开大任务前会先告诉你还剩多少、几点重置,让你选先做小的还是等重置。",
|
|
1632
|
+
"quota_aware_enabled_msg": "已开启额度感知模式。请重启 Claude Code 应用。",
|
|
1633
|
+
"quota_aware_disabled_msg": "已关闭额度感知模式。请重启 Claude Code 应用。",
|
|
1634
|
+
"quota_aware_action_failed": "无法切换额度感知模式。",
|
|
1635
|
+
"claude_beginner_error": "新手模式:{error}"
|
|
1555
1636
|
},
|
|
1556
1637
|
"ja": {
|
|
1557
1638
|
"panel_default_name": "デフォルト",
|
|
@@ -2044,6 +2125,8 @@
|
|
|
2044
2125
|
"claude_pane_sidecar_error": "パネル設定を読み込めません: {error}",
|
|
2045
2126
|
"menu_badge_new": "新規",
|
|
2046
2127
|
"claude_pane_menu": "Claude Code サイドパネル",
|
|
2128
|
+
"claude_code_section": "Claude Code",
|
|
2129
|
+
"claude_pane_section_menu": "サイドペイン",
|
|
2047
2130
|
"claude_pane_tooltip": "Claude Code に使用量、セッション、バックグラウンド作業を表示",
|
|
2048
2131
|
"claude_pane_enabled_msg": "サイドパネルを有効にしました。新しい Claude Code セッションを開くか、会話で /reload-plugins を実行してください。ターミナルの幅が足りない場合は /usage-dash で開けます。",
|
|
2049
2132
|
"claude_pane_fullscreen_enabled_note": "Claude Code のフルスクリーンレイアウトを有効にし、パネルを右側に表示できるようにしました。サイドパネルをオフにすると元に戻ります。",
|
|
@@ -2069,7 +2152,32 @@
|
|
|
2069
2152
|
"keeper_failed_line": "前回の失敗 {time}:{tool}({reason})",
|
|
2070
2153
|
"terse_codex_hook_untrusted": "Codex が usage の簡潔モード hook をまだ承認していないため、Codex では機能しません。Codex で /hooks を入力して承認してください。",
|
|
2071
2154
|
"terse_codex_hook_disabled": "Codex で usage の簡潔モード hook が無効になっています。Codex で /hooks を入力して再び有効にしてください。",
|
|
2072
|
-
"claude_pane_stopped": "停止済み"
|
|
2155
|
+
"claude_pane_stopped": "停止済み",
|
|
2156
|
+
"claude_beginner_menu": "初心者モード",
|
|
2157
|
+
"claude_beginner_tooltip": "Claude の回答をわかりやすく:回答後、入力欄の上に最大3つの専門用語をやさしく説明します。9を押すと「理解した」と記録して表示しなくなり、後日、新しい会話で選択式のミニテストが出ます。Claude の使用枠を少量使います。",
|
|
2158
|
+
"claude_beginner_title": "用語のヒント",
|
|
2159
|
+
"claude_beginner_history_title": "用語の履歴",
|
|
2160
|
+
"claude_beginner_description": "用語の履歴を開く",
|
|
2161
|
+
"claude_beginner_all_known": "すべて理解した",
|
|
2162
|
+
"claude_beginner_dismiss": "閉じる",
|
|
2163
|
+
"claude_beginner_known": "理解した",
|
|
2164
|
+
"claude_beginner_unknown": "まだわからない",
|
|
2165
|
+
"claude_beginner_empty": "用語の記録はまだありません。",
|
|
2166
|
+
"claude_beginner_opened": "用語の履歴を開きました。",
|
|
2167
|
+
"claude_beginner_quiz_title": "ミニテスト",
|
|
2168
|
+
"claude_beginner_quiz_question": "{term} の意味は?",
|
|
2169
|
+
"claude_beginner_quiz_right": "正解!{days} 日後にもう一度出題します。",
|
|
2170
|
+
"claude_beginner_quiz_done": "正解!この用語は覚えました。",
|
|
2171
|
+
"claude_beginner_quiz_wrong": "不正解。正解は「{answer}」です。この用語はまた用語ヒントに表示されます。",
|
|
2172
|
+
"claude_beginner_enabled_msg": "初心者モードを有効にしました。Claude Code を再起動してください。",
|
|
2173
|
+
"claude_beginner_disabled_msg": "初心者モードを無効にしました。Claude Code を再起動してください。",
|
|
2174
|
+
"claude_beginner_action_failed": "初心者モードを切り替えられません",
|
|
2175
|
+
"quota_aware_menu": "クォータ認識モード",
|
|
2176
|
+
"quota_aware_tooltip": "クォータが少なくなると、大きなタスクを始める前に Claude が残量とリセット時刻を伝えます。小さな部分を先に進めるか、リセットを待つか選べます。",
|
|
2177
|
+
"quota_aware_enabled_msg": "クォータ認識モードを有効にしました。Claude Code を再起動してください。",
|
|
2178
|
+
"quota_aware_disabled_msg": "クォータ認識モードを無効にしました。Claude Code を再起動してください。",
|
|
2179
|
+
"quota_aware_action_failed": "クォータ認識モードを切り替えられませんでした。",
|
|
2180
|
+
"claude_beginner_error": "初心者モード:{error}"
|
|
2073
2181
|
},
|
|
2074
2182
|
"ko": {
|
|
2075
2183
|
"panel_default_name": "기본",
|
|
@@ -2562,6 +2670,8 @@
|
|
|
2562
2670
|
"claude_pane_sidecar_error": "패널 설정을 읽을 수 없습니다: {error}",
|
|
2563
2671
|
"menu_badge_new": "신규",
|
|
2564
2672
|
"claude_pane_menu": "Claude Code 사이드 패널",
|
|
2673
|
+
"claude_code_section": "Claude Code",
|
|
2674
|
+
"claude_pane_section_menu": "사이드 패널",
|
|
2565
2675
|
"claude_pane_tooltip": "Claude Code에 사용량, 대화 및 백그라운드 작업 표시",
|
|
2566
2676
|
"claude_pane_enabled_msg": "사이드 패널을 켰습니다. 새 Claude Code 대화를 열거나 대화에서 /reload-plugins를 실행하세요. 터미널이 좁으면 /usage-dash로 패널을 여세요.",
|
|
2567
2677
|
"claude_pane_fullscreen_enabled_note": "Claude Code의 전체 화면 레이아웃도 켜서 패널이 오른쪽에 표시됩니다. 사이드 패널을 끄면 원래대로 돌아갑니다.",
|
|
@@ -2587,6 +2697,31 @@
|
|
|
2587
2697
|
"keeper_failed_line": "마지막 실패 {time}: {tool} ({reason})",
|
|
2588
2698
|
"terse_codex_hook_untrusted": "Codex가 usage의 간결 모드 hook을 아직 승인하지 않아 Codex에서 작동하지 않습니다. Codex에서 /hooks를 입력해 승인하세요.",
|
|
2589
2699
|
"terse_codex_hook_disabled": "Codex에서 usage의 간결 모드 hook을 비활성화했습니다. Codex에서 /hooks를 입력해 다시 활성화하세요.",
|
|
2590
|
-
"claude_pane_stopped": "중지됨"
|
|
2700
|
+
"claude_pane_stopped": "중지됨",
|
|
2701
|
+
"claude_beginner_menu": "초보자 모드",
|
|
2702
|
+
"claude_beginner_tooltip": "Claude의 답변을 쉽게 이해하세요. 답변 후 입력창 위에 전문 용어를 최대 3개까지 쉽게 설명합니다. 9를 누르면 이해한 것으로 기록하여 더 이상 표시하지 않고, 나중에 새 대화에서 객관식 퀴즈를 냅니다. Claude 사용량을 조금 씁니다.",
|
|
2703
|
+
"claude_beginner_title": "용어 안내",
|
|
2704
|
+
"claude_beginner_history_title": "용어 안내 기록",
|
|
2705
|
+
"claude_beginner_description": "용어 안내 기록 열기",
|
|
2706
|
+
"claude_beginner_all_known": "모두 이해했어요",
|
|
2707
|
+
"claude_beginner_dismiss": "닫기",
|
|
2708
|
+
"claude_beginner_known": "이해했어요",
|
|
2709
|
+
"claude_beginner_unknown": "아직 모르겠어요",
|
|
2710
|
+
"claude_beginner_empty": "아직 용어 기록이 없습니다.",
|
|
2711
|
+
"claude_beginner_opened": "용어 안내 기록을 열었습니다.",
|
|
2712
|
+
"claude_beginner_quiz_title": "퀴즈",
|
|
2713
|
+
"claude_beginner_quiz_question": "{term}의 뜻은?",
|
|
2714
|
+
"claude_beginner_quiz_right": "정답! {days}일 후에 다시 출제합니다.",
|
|
2715
|
+
"claude_beginner_quiz_done": "정답! 이 용어를 익혔어요.",
|
|
2716
|
+
"claude_beginner_quiz_wrong": "오답이에요. 정답: {answer}. 이 용어는 용어 힌트에 다시 나타납니다.",
|
|
2717
|
+
"claude_beginner_enabled_msg": "초보자 모드를 켰습니다. Claude Code를 다시 시작하세요.",
|
|
2718
|
+
"claude_beginner_disabled_msg": "초보자 모드를 껐습니다. Claude Code를 다시 시작하세요.",
|
|
2719
|
+
"claude_beginner_action_failed": "초보자 모드를 전환할 수 없습니다",
|
|
2720
|
+
"quota_aware_menu": "할당량 인식 모드",
|
|
2721
|
+
"quota_aware_tooltip": "할당량이 부족해지면 Claude가 큰 작업을 시작하기 전에 남은 양과 초기화 시간을 알려 줍니다. 작은 부분을 먼저 진행하거나 초기화를 기다릴 수 있습니다.",
|
|
2722
|
+
"quota_aware_enabled_msg": "할당량 인식 모드를 켰습니다. Claude Code를 다시 시작해 주세요.",
|
|
2723
|
+
"quota_aware_disabled_msg": "할당량 인식 모드를 껐습니다. Claude Code를 다시 시작해 주세요.",
|
|
2724
|
+
"quota_aware_action_failed": "할당량 인식 모드를 변경할 수 없습니다.",
|
|
2725
|
+
"claude_beginner_error": "초보자 모드: {error}"
|
|
2591
2726
|
}
|
|
2592
2727
|
}
|
|
@@ -33,10 +33,18 @@ def _remove_path(value: str, path: str) -> str:
|
|
|
33
33
|
return os.pathsep.join(parts) if any(parts) else ""
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def
|
|
36
|
+
def _install_dir(mod: str) -> Path:
|
|
37
|
+
return INSTALL_DIR.with_name("usage-beginner") if mod == "usage-beginner" else INSTALL_DIR
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _sidecar_name(mod: str) -> str:
|
|
41
|
+
return "usage-beginner.json" if mod == "usage-beginner" else "usage-pane.json"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _resolve_source(mod: str = "usage-dash") -> Path:
|
|
37
45
|
paths = [
|
|
38
|
-
Path(__file__).resolve().parent.parent / "claude_pane" /
|
|
39
|
-
Path(sys.executable).resolve().parent.parent / "Resources" / "claude_pane" /
|
|
46
|
+
Path(__file__).resolve().parent.parent / "claude_pane" / mod,
|
|
47
|
+
Path(sys.executable).resolve().parent.parent / "Resources" / "claude_pane" / mod,
|
|
40
48
|
]
|
|
41
49
|
for path in paths:
|
|
42
50
|
if path.is_dir():
|
|
@@ -72,7 +80,7 @@ def _status_argv() -> list[str]:
|
|
|
72
80
|
]
|
|
73
81
|
|
|
74
82
|
|
|
75
|
-
def _sidecar_text() -> str:
|
|
83
|
+
def _sidecar_text(mod: str = "usage-dash") -> str:
|
|
76
84
|
from i18n import I18N_PATH
|
|
77
85
|
|
|
78
86
|
bundle = json.loads(I18N_PATH.read_text(encoding="utf-8"))
|
|
@@ -81,30 +89,36 @@ def _sidecar_text() -> str:
|
|
|
81
89
|
strings = {
|
|
82
90
|
key: table.get(key) or value
|
|
83
91
|
for key, value in english.items()
|
|
84
|
-
if key.startswith("claude_pane_")
|
|
92
|
+
if key.startswith("claude_beginner_" if mod == "usage-beginner" else "claude_pane_")
|
|
85
93
|
}
|
|
86
94
|
return (
|
|
87
95
|
json.dumps(
|
|
88
|
-
|
|
96
|
+
(
|
|
97
|
+
{"strings": strings, "lang": detect_lang()}
|
|
98
|
+
if mod == "usage-beginner"
|
|
99
|
+
else {"strings": strings, "status_argv": _status_argv()}
|
|
100
|
+
),
|
|
101
|
+
ensure_ascii=False,
|
|
102
|
+
indent=2,
|
|
89
103
|
)
|
|
90
104
|
+ "\n"
|
|
91
105
|
)
|
|
92
106
|
|
|
93
107
|
|
|
94
|
-
def _write_sidecar() -> None:
|
|
95
|
-
setup_hook._atomic_write_text(
|
|
108
|
+
def _write_sidecar(mod: str = "usage-dash") -> None:
|
|
109
|
+
setup_hook._atomic_write_text(_install_dir(mod) / _sidecar_name(mod), _sidecar_text(mod))
|
|
96
110
|
|
|
97
111
|
|
|
98
|
-
def _copy_pane() -> None:
|
|
99
|
-
source = _resolve_source()
|
|
100
|
-
if
|
|
101
|
-
shutil.rmtree(
|
|
102
|
-
shutil.copytree(source,
|
|
103
|
-
_write_sidecar()
|
|
112
|
+
def _copy_pane(mod: str = "usage-dash") -> None:
|
|
113
|
+
source = _resolve_source(mod)
|
|
114
|
+
if _install_dir(mod).exists():
|
|
115
|
+
shutil.rmtree(_install_dir(mod))
|
|
116
|
+
shutil.copytree(source, _install_dir(mod), ignore=shutil.ignore_patterns("*.test.ts"))
|
|
117
|
+
_write_sidecar() if mod == "usage-dash" else _write_sidecar(mod)
|
|
104
118
|
|
|
105
119
|
|
|
106
|
-
def _installed_is_current() -> bool:
|
|
107
|
-
source = _resolve_source()
|
|
120
|
+
def _installed_is_current(mod: str = "usage-dash") -> bool:
|
|
121
|
+
source = _resolve_source(mod)
|
|
108
122
|
shipped = {
|
|
109
123
|
path.relative_to(source)
|
|
110
124
|
for path in source.rglob("*")
|
|
@@ -113,16 +127,20 @@ def _installed_is_current() -> bool:
|
|
|
113
127
|
# Claude Code writes its own type stubs under .claude-plugin/types; they are not ours.
|
|
114
128
|
generated = Path(".claude-plugin", "types")
|
|
115
129
|
installed = {
|
|
116
|
-
path.relative_to(
|
|
117
|
-
for path in
|
|
118
|
-
if path.is_file() and generated not in path.relative_to(
|
|
119
|
-
} - {Path(
|
|
130
|
+
path.relative_to(_install_dir(mod))
|
|
131
|
+
for path in _install_dir(mod).rglob("*")
|
|
132
|
+
if path.is_file() and generated not in path.relative_to(_install_dir(mod)).parents
|
|
133
|
+
} - {Path(_sidecar_name(mod))}
|
|
120
134
|
if shipped != installed:
|
|
121
135
|
return False
|
|
122
|
-
if not all(
|
|
136
|
+
if not all(
|
|
137
|
+
filecmp.cmp(source / path, _install_dir(mod) / path, shallow=False) for path in shipped
|
|
138
|
+
):
|
|
123
139
|
return False
|
|
124
140
|
try:
|
|
125
|
-
return (
|
|
141
|
+
return (_install_dir(mod) / _sidecar_name(mod)).read_text(
|
|
142
|
+
encoding="utf-8"
|
|
143
|
+
) == _sidecar_text(mod)
|
|
126
144
|
except OSError:
|
|
127
145
|
return False
|
|
128
146
|
|
|
@@ -134,11 +152,13 @@ def _env(settings: dict[str, object]) -> dict[str, str]:
|
|
|
134
152
|
return env
|
|
135
153
|
|
|
136
154
|
|
|
137
|
-
def enable_claude_pane() -> int:
|
|
138
|
-
_copy_pane()
|
|
155
|
+
def enable_claude_pane(mod: str = "usage-dash") -> int:
|
|
156
|
+
_copy_pane() if mod == "usage-dash" else _copy_pane(mod)
|
|
139
157
|
settings = setup_hook._load_settings()
|
|
140
158
|
env = _env(settings)
|
|
141
|
-
env[PLUGIN_DIRS_KEY] = _add_path(
|
|
159
|
+
env[PLUGIN_DIRS_KEY] = _add_path(
|
|
160
|
+
env.get(PLUGIN_DIRS_KEY, ""), str(_install_dir(mod).absolute())
|
|
161
|
+
)
|
|
142
162
|
settings["env"] = env
|
|
143
163
|
setup_hook._save_settings(settings)
|
|
144
164
|
return 0
|
|
@@ -159,12 +179,12 @@ def enable_fullscreen_layout() -> None:
|
|
|
159
179
|
setup_hook._save_settings(settings)
|
|
160
180
|
|
|
161
181
|
|
|
162
|
-
def disable_claude_pane() -> int:
|
|
182
|
+
def disable_claude_pane(mod: str = "usage-dash") -> int:
|
|
163
183
|
settings = setup_hook._load_settings()
|
|
164
184
|
env = _env(settings)
|
|
165
185
|
changed = False
|
|
166
186
|
if PLUGIN_DIRS_KEY in env:
|
|
167
|
-
value = _remove_path(env[PLUGIN_DIRS_KEY], str(
|
|
187
|
+
value = _remove_path(env[PLUGIN_DIRS_KEY], str(_install_dir(mod).absolute()))
|
|
168
188
|
if value:
|
|
169
189
|
env[PLUGIN_DIRS_KEY] = value
|
|
170
190
|
else:
|
|
@@ -173,7 +193,11 @@ def disable_claude_pane() -> int:
|
|
|
173
193
|
settings.pop("env", None)
|
|
174
194
|
changed = True
|
|
175
195
|
usage = settings.get("usage")
|
|
176
|
-
if
|
|
196
|
+
if (
|
|
197
|
+
mod == "usage-dash"
|
|
198
|
+
and isinstance(usage, dict)
|
|
199
|
+
and usage.get("claudePaneSetFullscreen") is True
|
|
200
|
+
):
|
|
177
201
|
if settings.get("tui") == "fullscreen":
|
|
178
202
|
settings.pop("tui")
|
|
179
203
|
usage.pop("claudePaneSetFullscreen")
|
|
@@ -182,18 +206,19 @@ def disable_claude_pane() -> int:
|
|
|
182
206
|
changed = True
|
|
183
207
|
if changed:
|
|
184
208
|
setup_hook._save_settings(settings)
|
|
185
|
-
if
|
|
186
|
-
shutil.rmtree(
|
|
209
|
+
if _install_dir(mod).exists():
|
|
210
|
+
shutil.rmtree(_install_dir(mod))
|
|
187
211
|
return 0
|
|
188
212
|
|
|
189
213
|
|
|
190
|
-
def is_claude_pane_enabled() -> bool:
|
|
214
|
+
def is_claude_pane_enabled(mod: str = "usage-dash") -> bool:
|
|
191
215
|
try:
|
|
192
216
|
value = _env(setup_hook._load_settings()).get(PLUGIN_DIRS_KEY, "")
|
|
193
217
|
except SystemExit:
|
|
194
218
|
return False
|
|
195
|
-
return
|
|
196
|
-
part and _same_path(part, str(
|
|
219
|
+
return _install_dir(mod).is_dir() and any(
|
|
220
|
+
part and _same_path(part, str(_install_dir(mod).absolute()))
|
|
221
|
+
for part in value.split(os.pathsep)
|
|
197
222
|
)
|
|
198
223
|
|
|
199
224
|
|
|
@@ -201,3 +226,17 @@ def refresh_claude_pane() -> None:
|
|
|
201
226
|
"""Re-copy an enabled pane whose files differ from this build's, e.g. after an app upgrade."""
|
|
202
227
|
if is_claude_pane_enabled() and not _installed_is_current():
|
|
203
228
|
_copy_pane()
|
|
229
|
+
if is_claude_beginner_enabled() and not _installed_is_current("usage-beginner"):
|
|
230
|
+
_copy_pane("usage-beginner")
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def enable_claude_beginner() -> int:
|
|
234
|
+
return enable_claude_pane("usage-beginner")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def disable_claude_beginner() -> int:
|
|
238
|
+
return disable_claude_pane("usage-beginner")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def is_claude_beginner_enabled() -> bool:
|
|
242
|
+
return is_claude_pane_enabled("usage-beginner")
|
|
@@ -92,6 +92,12 @@ TERSE_REMINDER_HOOK_VERSION = "1.2"
|
|
|
92
92
|
TERSE_REMINDER_MATCHER = ""
|
|
93
93
|
_TERSE_REMINDER_MARKER = "usage-terse-reminder"
|
|
94
94
|
_TERSE_REMINDER_MARKERS = (_TERSE_REMINDER_MARKER, "usage_terse_reminder")
|
|
95
|
+
# Independent, opt-in Claude Code UserPromptSubmit hook.
|
|
96
|
+
QUOTA_AWARE_HOOK_TARGET = Path(os.path.expanduser("~/.claude/usage-quota-aware.py"))
|
|
97
|
+
QUOTA_AWARE_HOOK_VERSION = "1.0"
|
|
98
|
+
QUOTA_AWARE_MATCHER = ""
|
|
99
|
+
_QUOTA_AWARE_MARKER = "usage-quota-aware"
|
|
100
|
+
_QUOTA_AWARE_MARKERS = (_QUOTA_AWARE_MARKER, "usage_quota_aware")
|
|
95
101
|
|
|
96
102
|
|
|
97
103
|
def _migrate_bundled_python_commands_if_needed(
|
|
@@ -1252,9 +1258,119 @@ def self_heal() -> None:
|
|
|
1252
1258
|
raise
|
|
1253
1259
|
_debug_self_heal_failure("resume_hook", exc)
|
|
1254
1260
|
|
|
1261
|
+
try:
|
|
1262
|
+
_self_heal_quota_aware()
|
|
1263
|
+
except BaseException as exc:
|
|
1264
|
+
if isinstance(exc, KeyboardInterrupt):
|
|
1265
|
+
raise
|
|
1266
|
+
_debug_self_heal_failure("quota_aware_hook", exc)
|
|
1267
|
+
|
|
1255
1268
|
try:
|
|
1256
1269
|
_self_heal_terse_mode()
|
|
1257
1270
|
except BaseException as exc:
|
|
1258
1271
|
if isinstance(exc, KeyboardInterrupt):
|
|
1259
1272
|
raise
|
|
1260
1273
|
_debug_self_heal_failure("terse_hook", exc)
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
def _resolve_quota_aware_source() -> Path:
|
|
1277
|
+
paths = [
|
|
1278
|
+
Path(__file__).resolve().parent.parent / "usage_quota_aware.py",
|
|
1279
|
+
Path(sys.executable).resolve().parent.parent / "Resources" / "usage_quota_aware.py",
|
|
1280
|
+
]
|
|
1281
|
+
for path in paths:
|
|
1282
|
+
if path.exists():
|
|
1283
|
+
return path
|
|
1284
|
+
raise SystemExit(_t("setup_hook_source_missing", tried=", ".join(map(str, paths))))
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
def _quota_aware_command() -> str:
|
|
1288
|
+
return f"{_shell_arg(_find_system_python())} {_shell_arg(str(QUOTA_AWARE_HOOK_TARGET))}"
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
def _copy_quota_aware_script() -> None:
|
|
1292
|
+
QUOTA_AWARE_HOOK_TARGET.parent.mkdir(parents=True, exist_ok=True)
|
|
1293
|
+
shutil.copyfile(_resolve_quota_aware_source(), QUOTA_AWARE_HOOK_TARGET)
|
|
1294
|
+
QUOTA_AWARE_HOOK_TARGET.chmod(
|
|
1295
|
+
QUOTA_AWARE_HOOK_TARGET.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
|
1296
|
+
)
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
def is_quota_aware_enabled() -> bool:
|
|
1300
|
+
try:
|
|
1301
|
+
entries = _user_prompt_submit_list(_load_settings())
|
|
1302
|
+
except SystemExit:
|
|
1303
|
+
return False
|
|
1304
|
+
return any(_is_hook_entry(entry, _QUOTA_AWARE_MARKERS) for entry in entries or [])
|
|
1305
|
+
|
|
1306
|
+
|
|
1307
|
+
def enable_quota_aware() -> int:
|
|
1308
|
+
if not setup_hook._claude_settings_dir_exists():
|
|
1309
|
+
print(_t("setup_no_agents"), file=sys.stderr)
|
|
1310
|
+
return 1
|
|
1311
|
+
_copy_quota_aware_script()
|
|
1312
|
+
settings = _load_settings()
|
|
1313
|
+
hooks = settings.get("hooks")
|
|
1314
|
+
if not isinstance(hooks, dict):
|
|
1315
|
+
hooks = {}
|
|
1316
|
+
settings["hooks"] = hooks
|
|
1317
|
+
entries = _user_prompt_submit_list(settings) or []
|
|
1318
|
+
hooks["UserPromptSubmit"] = [
|
|
1319
|
+
kept
|
|
1320
|
+
for entry in entries
|
|
1321
|
+
if (kept := _strip_hook_entries(entry, _QUOTA_AWARE_MARKERS)) is not None
|
|
1322
|
+
] + [
|
|
1323
|
+
{
|
|
1324
|
+
"matcher": QUOTA_AWARE_MATCHER,
|
|
1325
|
+
"hooks": [{"type": "command", "command": _quota_aware_command()}],
|
|
1326
|
+
}
|
|
1327
|
+
]
|
|
1328
|
+
_save_settings(settings)
|
|
1329
|
+
print(_t("quota_aware_enabled_msg"))
|
|
1330
|
+
print(_t("setup_claude_restart_required"))
|
|
1331
|
+
return 0
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
def disable_quota_aware() -> int:
|
|
1335
|
+
settings = _load_settings()
|
|
1336
|
+
entries = _user_prompt_submit_list(settings)
|
|
1337
|
+
if entries is not None:
|
|
1338
|
+
kept = [
|
|
1339
|
+
item
|
|
1340
|
+
for entry in entries
|
|
1341
|
+
if (item := _strip_hook_entries(entry, _QUOTA_AWARE_MARKERS)) is not None
|
|
1342
|
+
]
|
|
1343
|
+
if kept != entries:
|
|
1344
|
+
hooks = settings["hooks"]
|
|
1345
|
+
if kept:
|
|
1346
|
+
hooks["UserPromptSubmit"] = kept
|
|
1347
|
+
else:
|
|
1348
|
+
hooks.pop("UserPromptSubmit", None)
|
|
1349
|
+
if not hooks:
|
|
1350
|
+
settings.pop("hooks", None)
|
|
1351
|
+
_save_settings(settings)
|
|
1352
|
+
if QUOTA_AWARE_HOOK_TARGET.exists():
|
|
1353
|
+
QUOTA_AWARE_HOOK_TARGET.unlink()
|
|
1354
|
+
print(_t("quota_aware_disabled_msg"))
|
|
1355
|
+
return 0
|
|
1356
|
+
|
|
1357
|
+
|
|
1358
|
+
def _installed_quota_aware_version() -> str | None:
|
|
1359
|
+
try:
|
|
1360
|
+
for line in QUOTA_AWARE_HOOK_TARGET.read_text(encoding="utf-8").splitlines():
|
|
1361
|
+
if line.startswith("__version__"):
|
|
1362
|
+
return line.partition("=")[2].strip().strip("\"'")
|
|
1363
|
+
except (OSError, UnicodeError):
|
|
1364
|
+
pass
|
|
1365
|
+
return None
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
def _self_heal_quota_aware() -> None:
|
|
1369
|
+
if not is_quota_aware_enabled():
|
|
1370
|
+
return
|
|
1371
|
+
old = _installed_quota_aware_version()
|
|
1372
|
+
if old != QUOTA_AWARE_HOOK_VERSION:
|
|
1373
|
+
_copy_quota_aware_script()
|
|
1374
|
+
_append_self_heal_log(
|
|
1375
|
+
"update_quota_aware_hook", f"{old or 'unknown'} -> {QUOTA_AWARE_HOOK_VERSION}"
|
|
1376
|
+
)
|