cc-menubar 0.2.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.
Files changed (32) hide show
  1. cc_menubar-0.2.0/.github/workflows/release.yml +91 -0
  2. cc_menubar-0.2.0/.github/workflows/test.yml +21 -0
  3. cc_menubar-0.2.0/.gitignore +8 -0
  4. cc_menubar-0.2.0/.release-please-manifest.json +3 -0
  5. cc_menubar-0.2.0/CHANGELOG.md +15 -0
  6. cc_menubar-0.2.0/CLAUDE.md +71 -0
  7. cc_menubar-0.2.0/CONTRIBUTING.md +58 -0
  8. cc_menubar-0.2.0/LICENSE +21 -0
  9. cc_menubar-0.2.0/PKG-INFO +156 -0
  10. cc_menubar-0.2.0/README.md +129 -0
  11. cc_menubar-0.2.0/demo/capture.sh +55 -0
  12. cc_menubar-0.2.0/pyproject.toml +53 -0
  13. cc_menubar-0.2.0/release-please-config.json +16 -0
  14. cc_menubar-0.2.0/src/cc_menubar/__init__.py +0 -0
  15. cc_menubar-0.2.0/src/cc_menubar/bash_utils.py +51 -0
  16. cc_menubar-0.2.0/src/cc_menubar/classifier.py +122 -0
  17. cc_menubar-0.2.0/src/cc_menubar/cli.py +299 -0
  18. cc_menubar-0.2.0/src/cc_menubar/collectors/__init__.py +0 -0
  19. cc_menubar-0.2.0/src/cc_menubar/collectors/blocks.py +51 -0
  20. cc_menubar-0.2.0/src/cc_menubar/collectors/cache.py +39 -0
  21. cc_menubar-0.2.0/src/cc_menubar/collectors/jsonl.py +178 -0
  22. cc_menubar-0.2.0/src/cc_menubar/collectors/quota.py +64 -0
  23. cc_menubar-0.2.0/src/cc_menubar/config.py +191 -0
  24. cc_menubar-0.2.0/src/cc_menubar/constants.py +120 -0
  25. cc_menubar-0.2.0/src/cc_menubar/defaults.toml +46 -0
  26. cc_menubar-0.2.0/src/cc_menubar/render.py +481 -0
  27. cc_menubar-0.2.0/tests/__init__.py +0 -0
  28. cc_menubar-0.2.0/tests/test_bash_utils.py +51 -0
  29. cc_menubar-0.2.0/tests/test_config.py +82 -0
  30. cc_menubar-0.2.0/tests/test_quota.py +50 -0
  31. cc_menubar-0.2.0/tests/test_render.py +125 -0
  32. cc_menubar-0.2.0/uv.lock +191 -0
@@ -0,0 +1,91 @@
1
+ name: release
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ permissions:
6
+ contents: write
7
+ pull-requests: write
8
+ jobs:
9
+ release-please:
10
+ runs-on: ubuntu-latest
11
+ outputs:
12
+ release_created: ${{ steps.release.outputs.release_created }}
13
+ tag_name: ${{ steps.release.outputs.tag_name }}
14
+ steps:
15
+ - uses: actions/create-github-app-token@v2
16
+ id: app-token
17
+ with:
18
+ app-id: ${{ vars.RELEASE_PLEASE_APP_ID }}
19
+ private-key: ${{ secrets.RELEASE_PLEASE_PRIVATE_KEY }}
20
+ - id: release
21
+ uses: googleapis/release-please-action@v4
22
+ with:
23
+ token: ${{ steps.app-token.outputs.token }}
24
+ config-file: release-please-config.json
25
+
26
+ - name: Checkout release PR branch
27
+ if: ${{ steps.release.outputs.pr && !steps.release.outputs.release_created }}
28
+ uses: actions/checkout@v4
29
+ with:
30
+ token: ${{ steps.app-token.outputs.token }}
31
+ ref: release-please--branches--main
32
+
33
+ - name: Setup uv
34
+ if: ${{ steps.release.outputs.pr && !steps.release.outputs.release_created }}
35
+ uses: astral-sh/setup-uv@v5
36
+
37
+ - name: Update uv.lock in release PR
38
+ if: ${{ steps.release.outputs.pr && !steps.release.outputs.release_created }}
39
+ env:
40
+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
41
+ APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
42
+ run: |
43
+ uv lock
44
+ if git diff --quiet uv.lock; then
45
+ echo "uv.lock already up to date"
46
+ else
47
+ BOT_USER_ID=$(gh api "/users/${APP_SLUG}[bot]" --jq .id)
48
+ git config user.name "${APP_SLUG}[bot]"
49
+ git config user.email "${BOT_USER_ID}+${APP_SLUG}[bot]@users.noreply.github.com"
50
+ git add uv.lock
51
+ git commit -m "chore: update uv.lock"
52
+ git push
53
+ fi
54
+
55
+ bump-tap:
56
+ needs: release-please
57
+ if: ${{ needs.release-please.outputs.release_created == 'true' }}
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - uses: actions/create-github-app-token@v2
61
+ id: app-token
62
+ with:
63
+ app-id: ${{ vars.RELEASE_PLEASE_APP_ID }}
64
+ private-key: ${{ secrets.RELEASE_PLEASE_PRIVATE_KEY }}
65
+ repositories: homebrew-tap
66
+ - name: Dispatch formula update
67
+ uses: peter-evans/repository-dispatch@v3
68
+ with:
69
+ token: ${{ steps.app-token.outputs.token }}
70
+ repository: calvindotsg/homebrew-tap
71
+ event-type: formula-update
72
+ client-payload: |-
73
+ {
74
+ "formula": "cc-menubar",
75
+ "version": "${{ needs.release-please.outputs.tag_name }}",
76
+ "url": "https://github.com/calvindotsg/cc-menubar/archive/refs/tags/${{ needs.release-please.outputs.tag_name }}.tar.gz",
77
+ "type": "python"
78
+ }
79
+
80
+ pypi-publish:
81
+ needs: release-please
82
+ if: ${{ needs.release-please.outputs.release_created == 'true' }}
83
+ runs-on: ubuntu-latest
84
+ environment: pypi
85
+ permissions:
86
+ id-token: write
87
+ steps:
88
+ - uses: actions/checkout@v4
89
+ - uses: astral-sh/setup-uv@v5
90
+ - run: uv build
91
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,21 @@
1
+ name: test
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ branches: [main]
7
+ jobs:
8
+ test:
9
+ runs-on: macos-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.11", "3.12", "3.13"]
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v5
16
+ with:
17
+ python-version: ${{ matrix.python-version }}
18
+ - run: uv lock --check
19
+ - run: uv sync
20
+ - run: uv run ruff check src/ tests/
21
+ - run: uv run pytest
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .pytest_cache/
8
+ .ruff_cache/
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.2.0"
3
+ }
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0](https://github.com/calvindotsg/cc-menubar/compare/v0.1.0...v0.2.0) (2026-04-16)
4
+
5
+
6
+ ### Features
7
+
8
+ * initial cc-menubar package ([f15018d](https://github.com/calvindotsg/cc-menubar/commit/f15018d521334bf0749c8369c08cf1c1ddded253))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * extract bash commands from JSONL and fix Top Commands aggregation ([3c3a70f](https://github.com/calvindotsg/cc-menubar/commit/3c3a70f144ff865c7db2b11768cae9b1376cee41))
14
+
15
+ ## Changelog
@@ -0,0 +1,71 @@
1
+ # CLAUDE.md
2
+
3
+ ## Quick Commands
4
+
5
+ ```bash
6
+ uv sync # Install dependencies
7
+ uv run ruff check src/ tests/ # Lint
8
+ uv run ruff format src/ tests/ # Format
9
+ uv run pytest # Test
10
+ uv run cc-menubar render # Test render output
11
+ uv run cc-menubar config # Show merged config
12
+ uv run cc-menubar install # Install SwiftBar wrapper
13
+ ```
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ cc-menubar/
19
+ ├── src/cc_menubar/
20
+ │ ├── cli.py # Typer CLI: render, install, uninstall, init, config
21
+ │ ├── config.py # 3-layer TOML config merge (defaults → user → env)
22
+ │ ├── defaults.toml # Bundled default config
23
+ │ ├── render.py # SwiftBar output formatter + Theme class
24
+ │ ├── constants.py # Categories, tool sets, theme presets
25
+ │ ├── classifier.py # Activity classifier (CodeBurn port)
26
+ │ ├── bash_utils.py # Shell command extraction (CodeBurn port)
27
+ │ └── collectors/
28
+ │ ├── quota.py # Read /tmp/claude-statusline-usage.json
29
+ │ ├── blocks.py # ccusage blocks --json subprocess
30
+ │ ├── jsonl.py # Single-pass JSONL parser
31
+ │ └── cache.py # Aggregate cache (300s TTL)
32
+ └── tests/
33
+ ```
34
+
35
+ ## Key Patterns
36
+
37
+ ### TOML Config Merge
38
+ 3 layers: bundled `defaults.toml` → user `~/.config/cc-menubar/config.toml` → `CC_MENUBAR_*` env vars. Deep merge at section level.
39
+
40
+ ### SwiftBar Output DSL
41
+ - stdout = SwiftBar display, stderr = diagnostics
42
+ - `---` separates title from dropdown
43
+ - `sfimage=NAME` for SF Symbols, `sfvalue=0.0-1.0` for variable fill
44
+ - `color=#light,#dark` for automatic appearance switching
45
+ - `fold=true` for collapsible sections
46
+ - `dropdown=false` to hide cycling lines from dropdown
47
+ - `font=Menlo size=11` for monospace data rows
48
+
49
+ ### Caching
50
+ - Quota: reads `/tmp/claude-statusline-usage.json` (written by statusline.py)
51
+ - Blocks: `ccusage blocks --json --active` subprocess with timeout
52
+ - JSONL: single-pass parse of `~/.claude/projects/*/**.jsonl`, mtime-filtered
53
+ - Aggregate cache: `/tmp/cc-menubar-cache.json`, configurable TTL
54
+
55
+ ### Graceful Degradation
56
+ - Each collector returns `None` on failure; render skips that section
57
+ - Render catches all exceptions; fallback to static `-- | sfimage=gauge.with.needle.fill`
58
+ - `sfvalue` requires macOS 13.0+ — older versions get static icon
59
+ - `fold=true` degrades to expanded sections on older SwiftBar versions
60
+
61
+ ## Constraints
62
+
63
+ - **Minimal PATH**: Wrapper sets `/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$PATH`
64
+ - **sfimage always monochrome**: SwiftBar forces `isTemplate = true` on all sfimage icons
65
+ - **ccusage optional**: blocks section gracefully skips if ccusage not installed
66
+ - **Quota read-only**: Only reads statusline.py cache, never calls OAuth directly
67
+ - **Typer sole dependency**: Rich is transitive via Typer
68
+
69
+ ## Release Process
70
+
71
+ Conventional commits → release-please PR → PyPI (OIDC) → tap dispatch. Same flow as mac-upkeep.
@@ -0,0 +1,58 @@
1
+ # Contributing
2
+
3
+ ## Development Setup
4
+
5
+ > **Note:** macOS is required for development and testing.
6
+
7
+ ```bash
8
+ git clone https://github.com/calvindotsg/cc-menubar.git
9
+ cd cc-menubar
10
+ uv sync
11
+ ```
12
+
13
+ ## Code Style
14
+
15
+ ```bash
16
+ uv run ruff check src/ tests/ # Lint
17
+ uv run ruff format src/ tests/ # Format
18
+ ```
19
+
20
+ ## Testing
21
+
22
+ ```bash
23
+ uv run pytest # Run tests
24
+ uv run pytest --cov # With coverage
25
+ ```
26
+
27
+ ## Dependencies
28
+
29
+ After modifying `pyproject.toml` dependencies, run `uv lock` to update the lockfile. CI runs `uv lock --check` and will fail on stale lockfiles.
30
+
31
+ ## Commit Conventions
32
+
33
+ This project uses [Conventional Commits v1.0.0](https://www.conventionalcommits.org/en/v1.0.0/).
34
+
35
+ | Type | When |
36
+ |------|------|
37
+ | `feat` | New CLI subcommand, dropdown section, or user-facing behavior |
38
+ | `fix` | Bug fix (render error, config parse issue, etc.) |
39
+ | `docs` | README, CLAUDE.md, CONTRIBUTING.md changes |
40
+ | `chore` | Dependencies, config files |
41
+ | `ci` | GitHub Actions workflows |
42
+ | `test` | Test additions or fixes |
43
+ | `refactor` | Code restructuring without behavior change |
44
+
45
+ Examples:
46
+
47
+ ```
48
+ feat: add opusplan health dropdown section
49
+ fix: handle missing ccusage gracefully
50
+ docs: update README with config reference
51
+ chore: update typer to 0.13
52
+ ci: add macOS runner to test matrix
53
+ test: add test for quota parsing edge case
54
+ ```
55
+
56
+ ## Release Process
57
+
58
+ Automated via [release-please](https://github.com/googleapis/release-please). Use conventional commits — `feat:` and `fix:` trigger version bumps. See CLAUDE.md for full flow.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 calvindotsg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: cc-menubar
3
+ Version: 0.2.0
4
+ Summary: Pace your quota — proactive forecasting for Claude Code Max, not retroactive tracking
5
+ Project-URL: Homepage, https://github.com/calvindotsg/cc-menubar
6
+ Project-URL: Repository, https://github.com/calvindotsg/cc-menubar
7
+ Project-URL: Issues, https://github.com/calvindotsg/cc-menubar/issues
8
+ Project-URL: Changelog, https://github.com/calvindotsg/cc-menubar/blob/main/CHANGELOG.md
9
+ Author: Calvin
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: claude-code,cli,developer-tools,forecasting,macos,menubar,quota,swiftbar
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Environment :: MacOS X
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: System :: Systems Administration
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: typer>=0.12
26
+ Description-Content-Type: text/markdown
27
+
28
+ # cc-menubar
29
+
30
+ Pace your quota — proactive forecasting for Claude Code Max, not retroactive tracking.
31
+
32
+ 30+ Claude Code usage tools exist. Almost all show what you already spent. cc-menubar inverts this: instead of "how much did I use?", it answers "how far will my quota take me?"
33
+
34
+ ![cc-menubar menu bar icon](https://raw.githubusercontent.com/calvindotsg/cc-menubar/main/demo/menubar.png)
35
+
36
+ ![cc-menubar dropdown](https://raw.githubusercontent.com/calvindotsg/cc-menubar/main/demo/dropdown.png)
37
+
38
+ ## Three Principles
39
+
40
+ | Principle | What it means |
41
+ |-----------|--------------|
42
+ | **Forecast remaining, don't sum spent** | Show what's LEFT (runway), not what's USED (cost). The gauge depletes like fuel — 1.0 to 0.0. |
43
+ | **Pace by phase** | Different work phases burn tokens differently. Activity classifier shows where tokens go. |
44
+ | **Maintain headroom** | Don't run hot. Context Efficiency and quota pacing give early awareness, not late alerts. |
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ brew install calvindotsg/tap/cc-menubar
50
+ brew install --cask swiftbar
51
+ cc-menubar install
52
+ open -a SwiftBar
53
+ ```
54
+
55
+ Or with [uv](https://docs.astral.sh/uv/):
56
+
57
+ ```bash
58
+ uv tool install cc-menubar
59
+ cc-menubar install
60
+ ```
61
+
62
+ ## Menu Bar Icon
63
+
64
+ A gauge icon showing remaining quota. The needle position reflects how much quota is left in the current window (default: 5-hour). Configurable text, color thresholds, and metric cycling.
65
+
66
+ ## Dropdown Sections
67
+
68
+ | Section | Content | Visibility |
69
+ |---------|---------|------------|
70
+ | Quota & Runway | 5h/7d remaining, pace vs reset, burn rate | Always |
71
+ | Activity (7d) | Category bars with one-shot rate | Always |
72
+ | Projects | Per-project calls + subagent % | Always |
73
+ | Tools & Commands | Top tools, top bash commands | Always |
74
+ | Opusplan Health | Opus vs Haiku substitution % | When Opus model detected |
75
+ | Context Efficiency | >150K session %, P50/P90, cache hit % | When sufficient data exists |
76
+
77
+ ## Configuration
78
+
79
+ Config at `~/.config/cc-menubar/config.toml`. Built-in defaults apply automatically.
80
+
81
+ ```bash
82
+ cc-menubar init # Generate commented config
83
+ cc-menubar config # Show merged config
84
+ cc-menubar config --default # Show all defaults
85
+ ```
86
+
87
+ ### Title Options
88
+
89
+ ```toml
90
+ [title]
91
+ symbol = "gauge.with.needle.fill" # SF Symbol name
92
+ text = "none" # "none" | "percent" | "label"
93
+ color = "monochrome" # "monochrome" | "threshold" | "always"
94
+ metric = "5h" # "5h" | "7d"
95
+ cycle = [] # ["5h", "7d", "opusplan", "context"]
96
+ ```
97
+
98
+ ### Theme
99
+
100
+ ```toml
101
+ [theme]
102
+ preset = "ayu" # "ayu" (default) or "catppuccin"
103
+
104
+ # Override individual roles
105
+ [theme.light]
106
+ success = "#custom"
107
+
108
+ [theme.dark]
109
+ success = "#custom"
110
+ ```
111
+
112
+ ### Sections
113
+
114
+ ```toml
115
+ [quota]
116
+ enabled = true
117
+
118
+ [activity]
119
+ enabled = true
120
+ days = 7
121
+
122
+ [tools]
123
+ enabled = true
124
+ top_n = 10
125
+
126
+ [projects]
127
+ enabled = true
128
+ [projects.aliases]
129
+ # "-Users-me-myproject" = "My Project"
130
+ ```
131
+
132
+ ## CLI Commands
133
+
134
+ | Command | Purpose |
135
+ |---------|---------|
136
+ | `render` | Output SwiftBar text (called by wrapper) |
137
+ | `install` | Write SwiftBar wrapper + create config |
138
+ | `uninstall` | Remove SwiftBar wrapper |
139
+ | `init` | Generate config file |
140
+ | `config` | Show merged config |
141
+
142
+ ## Data Sources
143
+
144
+ - **Quota**: Reads `/tmp/claude-statusline-usage.json` (written by Claude Code statusline)
145
+ - **Burn rate**: `ccusage blocks --json --active` (optional, install via `brew install ccusage`)
146
+ - **Activity, tools, models, context**: JSONL files in `~/.claude/projects/`
147
+
148
+ ## Requirements
149
+
150
+ - macOS 13.0+ (for variable-value SF Symbols)
151
+ - Python 3.11+
152
+ - [SwiftBar](https://github.com/swiftbar/SwiftBar) or [xbar](https://xbarapp.com/)
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,129 @@
1
+ # cc-menubar
2
+
3
+ Pace your quota — proactive forecasting for Claude Code Max, not retroactive tracking.
4
+
5
+ 30+ Claude Code usage tools exist. Almost all show what you already spent. cc-menubar inverts this: instead of "how much did I use?", it answers "how far will my quota take me?"
6
+
7
+ ![cc-menubar menu bar icon](https://raw.githubusercontent.com/calvindotsg/cc-menubar/main/demo/menubar.png)
8
+
9
+ ![cc-menubar dropdown](https://raw.githubusercontent.com/calvindotsg/cc-menubar/main/demo/dropdown.png)
10
+
11
+ ## Three Principles
12
+
13
+ | Principle | What it means |
14
+ |-----------|--------------|
15
+ | **Forecast remaining, don't sum spent** | Show what's LEFT (runway), not what's USED (cost). The gauge depletes like fuel — 1.0 to 0.0. |
16
+ | **Pace by phase** | Different work phases burn tokens differently. Activity classifier shows where tokens go. |
17
+ | **Maintain headroom** | Don't run hot. Context Efficiency and quota pacing give early awareness, not late alerts. |
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ brew install calvindotsg/tap/cc-menubar
23
+ brew install --cask swiftbar
24
+ cc-menubar install
25
+ open -a SwiftBar
26
+ ```
27
+
28
+ Or with [uv](https://docs.astral.sh/uv/):
29
+
30
+ ```bash
31
+ uv tool install cc-menubar
32
+ cc-menubar install
33
+ ```
34
+
35
+ ## Menu Bar Icon
36
+
37
+ A gauge icon showing remaining quota. The needle position reflects how much quota is left in the current window (default: 5-hour). Configurable text, color thresholds, and metric cycling.
38
+
39
+ ## Dropdown Sections
40
+
41
+ | Section | Content | Visibility |
42
+ |---------|---------|------------|
43
+ | Quota & Runway | 5h/7d remaining, pace vs reset, burn rate | Always |
44
+ | Activity (7d) | Category bars with one-shot rate | Always |
45
+ | Projects | Per-project calls + subagent % | Always |
46
+ | Tools & Commands | Top tools, top bash commands | Always |
47
+ | Opusplan Health | Opus vs Haiku substitution % | When Opus model detected |
48
+ | Context Efficiency | >150K session %, P50/P90, cache hit % | When sufficient data exists |
49
+
50
+ ## Configuration
51
+
52
+ Config at `~/.config/cc-menubar/config.toml`. Built-in defaults apply automatically.
53
+
54
+ ```bash
55
+ cc-menubar init # Generate commented config
56
+ cc-menubar config # Show merged config
57
+ cc-menubar config --default # Show all defaults
58
+ ```
59
+
60
+ ### Title Options
61
+
62
+ ```toml
63
+ [title]
64
+ symbol = "gauge.with.needle.fill" # SF Symbol name
65
+ text = "none" # "none" | "percent" | "label"
66
+ color = "monochrome" # "monochrome" | "threshold" | "always"
67
+ metric = "5h" # "5h" | "7d"
68
+ cycle = [] # ["5h", "7d", "opusplan", "context"]
69
+ ```
70
+
71
+ ### Theme
72
+
73
+ ```toml
74
+ [theme]
75
+ preset = "ayu" # "ayu" (default) or "catppuccin"
76
+
77
+ # Override individual roles
78
+ [theme.light]
79
+ success = "#custom"
80
+
81
+ [theme.dark]
82
+ success = "#custom"
83
+ ```
84
+
85
+ ### Sections
86
+
87
+ ```toml
88
+ [quota]
89
+ enabled = true
90
+
91
+ [activity]
92
+ enabled = true
93
+ days = 7
94
+
95
+ [tools]
96
+ enabled = true
97
+ top_n = 10
98
+
99
+ [projects]
100
+ enabled = true
101
+ [projects.aliases]
102
+ # "-Users-me-myproject" = "My Project"
103
+ ```
104
+
105
+ ## CLI Commands
106
+
107
+ | Command | Purpose |
108
+ |---------|---------|
109
+ | `render` | Output SwiftBar text (called by wrapper) |
110
+ | `install` | Write SwiftBar wrapper + create config |
111
+ | `uninstall` | Remove SwiftBar wrapper |
112
+ | `init` | Generate config file |
113
+ | `config` | Show merged config |
114
+
115
+ ## Data Sources
116
+
117
+ - **Quota**: Reads `/tmp/claude-statusline-usage.json` (written by Claude Code statusline)
118
+ - **Burn rate**: `ccusage blocks --json --active` (optional, install via `brew install ccusage`)
119
+ - **Activity, tools, models, context**: JSONL files in `~/.claude/projects/`
120
+
121
+ ## Requirements
122
+
123
+ - macOS 13.0+ (for variable-value SF Symbols)
124
+ - Python 3.11+
125
+ - [SwiftBar](https://github.com/swiftbar/SwiftBar) or [xbar](https://xbarapp.com/)
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ # Capture cc-menubar README screenshots.
3
+ #
4
+ # Prerequisites:
5
+ # - cc-menubar installed and running in SwiftBar
6
+ # - macOS dark mode + dark wallpaper
7
+ # - Real Claude Code usage data
8
+ #
9
+ # Usage:
10
+ # ./demo/capture.sh icon # Capture menu bar strip
11
+ # ./demo/capture.sh dropdown # Capture expanded dropdown
12
+ # ./demo/capture.sh optimize # Optimize existing PNGs
13
+ set -euo pipefail
14
+
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+
17
+ optimize() {
18
+ for f in "$SCRIPT_DIR"/*.png; do
19
+ [ -f "$f" ] || continue
20
+ local before=$(stat -f%z "$f")
21
+ sips -s format png -s formatOptions best "$f" --out "$f" >/dev/null 2>&1
22
+ local after=$(stat -f%z "$f")
23
+ echo "$(basename "$f"): $(( before / 1024 ))KB → $(( after / 1024 ))KB"
24
+ done
25
+ }
26
+
27
+ case "${1:-help}" in
28
+ icon)
29
+ echo "Capture the menu bar strip showing the gauge icon."
30
+ echo "Steps:"
31
+ echo " 1. Ensure cc-menubar is running in SwiftBar"
32
+ echo " 2. A crosshair cursor will appear — select the menu bar region"
33
+ echo " around the gauge icon (include a few neighboring icons for context)"
34
+ screencapture -i "$SCRIPT_DIR/menubar.png"
35
+ echo "Saved: $SCRIPT_DIR/menubar.png"
36
+ optimize
37
+ ;;
38
+ dropdown)
39
+ echo "Capture the expanded dropdown."
40
+ echo "Steps:"
41
+ echo " 1. Click the gauge icon to open the dropdown"
42
+ echo " 2. A crosshair cursor will appear after 3 seconds — select the dropdown"
43
+ sleep 3
44
+ screencapture -i "$SCRIPT_DIR/dropdown.png"
45
+ echo "Saved: $SCRIPT_DIR/dropdown.png"
46
+ optimize
47
+ ;;
48
+ optimize)
49
+ optimize
50
+ ;;
51
+ *)
52
+ echo "Usage: $0 {icon|dropdown|optimize}"
53
+ exit 1
54
+ ;;
55
+ esac
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "cc-menubar"
3
+ version = "0.2.0"
4
+ description = "Pace your quota — proactive forecasting for Claude Code Max, not retroactive tracking"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICEN[CS]E*"]
8
+ requires-python = ">=3.11"
9
+ dependencies = ["typer>=0.12"]
10
+ authors = [{name = "Calvin"}]
11
+ keywords = ["claude-code", "macos", "menubar", "swiftbar", "quota", "forecasting", "cli", "developer-tools"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Environment :: Console",
15
+ "Environment :: MacOS X",
16
+ "Intended Audience :: Developers",
17
+ "Operating System :: MacOS",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: System :: Systems Administration",
23
+ "Typing :: Typed",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/calvindotsg/cc-menubar"
28
+ Repository = "https://github.com/calvindotsg/cc-menubar"
29
+ Issues = "https://github.com/calvindotsg/cc-menubar/issues"
30
+ Changelog = "https://github.com/calvindotsg/cc-menubar/blob/main/CHANGELOG.md"
31
+
32
+ [project.scripts]
33
+ cc-menubar = "cc_menubar.cli:app"
34
+
35
+ [dependency-groups]
36
+ dev = ["ruff>=0.8", "pytest>=8"]
37
+
38
+ [build-system]
39
+ requires = ["hatchling"]
40
+ build-backend = "hatchling.build"
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["src/cc_menubar"]
44
+
45
+ [tool.ruff]
46
+ target-version = "py311"
47
+ line-length = 100
48
+
49
+ [tool.ruff.lint]
50
+ select = ["E", "F", "I", "N", "W", "UP"]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]