usage-cli 0.32.0__tar.gz → 0.32.2__tar.gz

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