busy-crew 1.1.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 (48) hide show
  1. busy_crew-1.1.0/.github/workflows/release.yml +89 -0
  2. busy_crew-1.1.0/.github/workflows/test.yml +33 -0
  3. busy_crew-1.1.0/.gitignore +9 -0
  4. busy_crew-1.1.0/CHANGELOG.md +85 -0
  5. busy_crew-1.1.0/CONTRIBUTING.md +63 -0
  6. busy_crew-1.1.0/LICENSE +21 -0
  7. busy_crew-1.1.0/PKG-INFO +189 -0
  8. busy_crew-1.1.0/README.md +164 -0
  9. busy_crew-1.1.0/SECURITY.md +19 -0
  10. busy_crew-1.1.0/agentdeck/__init__.py +1 -0
  11. busy_crew-1.1.0/agentdeck/cli.py +144 -0
  12. busy_crew-1.1.0/agentdeck/device.py +55 -0
  13. busy_crew-1.1.0/agentdeck/health.py +37 -0
  14. busy_crew-1.1.0/agentdeck/hook.py +283 -0
  15. busy_crew-1.1.0/agentdeck/install.py +648 -0
  16. busy_crew-1.1.0/agentdeck/menubar/BusyCrewMenu.swift +342 -0
  17. busy_crew-1.1.0/agentdeck/model.py +24 -0
  18. busy_crew-1.1.0/agentdeck/observe.py +622 -0
  19. busy_crew-1.1.0/agentdeck/render.py +198 -0
  20. busy_crew-1.1.0/agentdeck/service.py +329 -0
  21. busy_crew-1.1.0/agentdeck/storage.py +86 -0
  22. busy_crew-1.1.0/agentdeck/updates.py +193 -0
  23. busy_crew-1.1.0/agentdeck/usage.py +192 -0
  24. busy_crew-1.1.0/docs/COMPATIBILITY.md +30 -0
  25. busy_crew-1.1.0/docs/busy-bar.jpg +0 -0
  26. busy_crew-1.1.0/docs/claude-team.png +0 -0
  27. busy_crew-1.1.0/docs/codex-team.png +0 -0
  28. busy_crew-1.1.0/docs/config.example.json +5 -0
  29. busy_crew-1.1.0/docs/how-it-works.md +80 -0
  30. busy_crew-1.1.0/docs/menu-bar.md +39 -0
  31. busy_crew-1.1.0/docs/menu-bar.png +0 -0
  32. busy_crew-1.1.0/docs/privacy.md +53 -0
  33. busy_crew-1.1.0/docs/tokens-month.png +0 -0
  34. busy_crew-1.1.0/docs/tokens-today.png +0 -0
  35. busy_crew-1.1.0/docs/troubleshooting.md +34 -0
  36. busy_crew-1.1.0/pyproject.toml +44 -0
  37. busy_crew-1.1.0/scripts/homebrew_formula.py +89 -0
  38. busy_crew-1.1.0/tests/dist_contents.py +49 -0
  39. busy_crew-1.1.0/tests/test_agentdeck.py +311 -0
  40. busy_crew-1.1.0/tests/test_cli.py +142 -0
  41. busy_crew-1.1.0/tests/test_homebrew_formula.py +114 -0
  42. busy_crew-1.1.0/tests/test_install.py +516 -0
  43. busy_crew-1.1.0/tests/test_menubar.py +375 -0
  44. busy_crew-1.1.0/tests/test_observer_lifecycle.py +816 -0
  45. busy_crew-1.1.0/tests/test_service_reliability.py +589 -0
  46. busy_crew-1.1.0/tests/test_updates.py +303 -0
  47. busy_crew-1.1.0/tests/wheel_smoke.py +47 -0
  48. busy_crew-1.1.0/uv.lock +654 -0
@@ -0,0 +1,89 @@
1
+ name: Release
2
+ on:
3
+ release:
4
+ types: [published]
5
+ permissions:
6
+ contents: read
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ timeout-minutes: 10
11
+ steps:
12
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
13
+ with:
14
+ persist-credentials: false
15
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
16
+ - name: Check that the tag matches the package version
17
+ env:
18
+ TAG: ${{ github.event.release.tag_name }}
19
+ run: |
20
+ version=$(uv version --short)
21
+ if [ "${TAG#v}" != "$version" ]; then
22
+ echo "Release tag $TAG does not match version $version in pyproject.toml" >&2
23
+ exit 1
24
+ fi
25
+ - run: uv sync --locked
26
+ - run: uv run python -m unittest discover -s tests
27
+ - run: uv build
28
+ - run: python3 -I tests/dist_contents.py dist
29
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+ if-no-files-found: error
34
+ pypi:
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ timeout-minutes: 10
38
+ environment:
39
+ name: pypi
40
+ url: https://pypi.org/project/busy-crew/
41
+ permissions:
42
+ id-token: write # Trusted publishing: no stored PyPI token.
43
+ steps:
44
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
49
+ homebrew:
50
+ needs: pypi
51
+ runs-on: ubuntu-latest
52
+ timeout-minutes: 15
53
+ steps:
54
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
55
+ with:
56
+ persist-credentials: false
57
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
58
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
59
+ with:
60
+ repository: cmdai-hq/homebrew-tap
61
+ path: tap
62
+ token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
63
+ - name: Point the formula at the PyPI release
64
+ env:
65
+ TAG: ${{ github.event.release.tag_name }}
66
+ run: uv run --locked python scripts/homebrew_formula.py tap/Formula/busy-crew.rb "${TAG#v}" uv.lock
67
+ # Safe to re-run: an update already on main stops here, and the branch and pull request are reused.
68
+ - name: Open the tap pull request, merged once its tests pass
69
+ working-directory: tap
70
+ env:
71
+ GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
72
+ TAG: ${{ github.event.release.tag_name }}
73
+ run: |
74
+ version="${TAG#v}"
75
+ branch="busy-crew-$version"
76
+ if git diff --quiet; then
77
+ echo "The tap's main already has busy-crew $version"
78
+ exit 0
79
+ fi
80
+ git config user.name "github-actions[bot]"
81
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
82
+ git switch -C "$branch"
83
+ git commit -am "busy-crew $version"
84
+ git push --force origin "$branch" # This bot owns the branch; a re-run replaces its own commit.
85
+ if [ "$(gh pr view "$branch" --json state --jq .state 2>/dev/null)" != "OPEN" ]; then
86
+ gh pr create --base main --head "$branch" --title "busy-crew $version" \
87
+ --body "Release https://github.com/cmdai-hq/busy-crew/releases/tag/$TAG, from its PyPI sdist and uv.lock."
88
+ fi
89
+ gh pr merge "$branch" --auto --squash --delete-branch
@@ -0,0 +1,33 @@
1
+ name: Test
2
+ on: [push, pull_request]
3
+ permissions:
4
+ contents: read
5
+ jobs:
6
+ test:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os: [ubuntu-latest, macos-latest]
11
+ python: ['3.11', '3.14', '3.15'] # 3.15: Homebrew's python@3.15
12
+ runs-on: ${{ matrix.os }}
13
+ timeout-minutes: 10
14
+ steps:
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
16
+ with:
17
+ persist-credentials: false
18
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
19
+ - run: uv sync --locked --python '${{ matrix.python }}'
20
+ - run: uv run python -m unittest discover -s tests -v
21
+ - name: Build and smoke test the installed wheel outside the repository
22
+ if: matrix.os == 'ubuntu-latest' && matrix.python == '3.11'
23
+ shell: bash
24
+ run: |
25
+ uv build
26
+ python3 -I tests/dist_contents.py dist
27
+ uv venv "$RUNNER_TEMP/agent-deck-wheel" --python 3.11
28
+ uv pip install --python "$RUNNER_TEMP/agent-deck-wheel/bin/python" dist/*.whl
29
+ cd "$RUNNER_TEMP"
30
+ "$RUNNER_TEMP/agent-deck-wheel/bin/busy-crew" --help
31
+ "$RUNNER_TEMP/agent-deck-wheel/bin/agent-deck" --help # pre-1.0 alias
32
+ "$RUNNER_TEMP/agent-deck-wheel/bin/busy-crew" demo "$RUNNER_TEMP/agent-deck-demo"
33
+ "$RUNNER_TEMP/agent-deck-wheel/bin/python" -I "$GITHUB_WORKSPACE/tests/wheel_smoke.py"
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .DS_Store
5
+ *.egg-info/
6
+ dist/
7
+ .env
8
+ state/
9
+ .playwright-mcp/
@@ -0,0 +1,85 @@
1
+ # Changelog
2
+
3
+ ## 1.1.0 — 2026-10-11
4
+
5
+ - Install without a checkout: `brew install cmdai-hq/tap/busy-crew` or
6
+ `uv tool install busy-crew`, then `busy-crew install`. Each GitHub release
7
+ now publishes the package to PyPI and updates the Homebrew formula.
8
+ - Offer new releases: once a day the collector asks GitHub for the latest
9
+ release (no credentials, no data about your agents; `"update_check": false`
10
+ in `config.json` turns it off). The menu bar icon then shows "Version X
11
+ available" and **Update to X**, which runs the new `busy-crew upgrade`:
12
+ `brew upgrade` or `uv tool upgrade`, then `install` with the new version and
13
+ a reinstall of the menu bar app. A failed check never degrades the status.
14
+ - `install` and `menubar install` write an interpreter path that survives
15
+ package upgrades: inside Homebrew's versioned `Cellar` folder, they use the
16
+ formula's `opt` link instead. `--python` or `BUSY_CREW_PYTHON` choose
17
+ another interpreter, and `install` checks that it can run Busy Crew before
18
+ changing anything.
19
+ - The README describes the Homebrew, uv and checkout installs, and its links
20
+ work on the PyPI page.
21
+
22
+ ## 1.0.1 — 2026-10-11
23
+
24
+ - A Codex conversation with a journal line over 2 MB (a large tool output)
25
+ no longer shows `?` for the whole Codex family for up to a day: the line is
26
+ left out of the turn. Turn events and function calls that large still fail
27
+ the read.
28
+ - An unreadable Codex journal is no longer rescanned every second until it
29
+ changes, and the service log names why a source failed.
30
+ - Read less: Codex journal records that cannot change a turn (messages,
31
+ reasoning, tool calls other than questions, tool outputs) are recognized from
32
+ their first bytes and never decoded, which also makes reading about four times faster; only the
33
+ needed Codex columns are queried. Claude hook fingerprints are keyed with a
34
+ per-installation secret (fingerprints recorded before the upgrade are migrated), and `front.png` and `service.lock` are private.
35
+ - Document what Busy Crew reads, keeps and sends in
36
+ [PRIVACY](docs/privacy.md).
37
+
38
+ ## 1.0.0 — 2026-10-11
39
+
40
+ First public release, under the MIT License.
41
+
42
+ - Rename the command to `busy-crew`, the package to `busy-crew` and the menu
43
+ bar app to Busy Crew. `agent-deck` stays as an alias and `AGENT_DECK_STATE`
44
+ is still honored next to `BUSY_CREW_STATE`. New installations use
45
+ `~/Library/Application Support/BusyCrew`; existing ones keep their folder,
46
+ LaunchAgent and hooks. After pulling, run `uv sync --locked` to get the new
47
+ command, and `uv run busy-crew menubar install` to rename the menu bar app.
48
+ - License the project under MIT instead of PolyForm Noncommercial.
49
+ - Add a month-to-date token view: Start / Pause cycles sessions, today and
50
+ this month, and the scroll wheel steps between them. Each token view names
51
+ its period at the bottom.
52
+ - Treat a finished or failed Codex conversation silent for two hours as dead:
53
+ it leaves the count with its `✓`, instead of piling up until acknowledged.
54
+ - Keep open Claude sessions visible after work completes and after acknowledgment;
55
+ show a sleeping indicator when the Claude family is entirely idle.
56
+ - Isolate global hook imports, record owned hook commands, respect custom roots,
57
+ and make installation conflicts/failures recoverable.
58
+ - Refuse credential-bearing redirects and scope display cleanup to Busy Crew.
59
+ - Validate local source and command data, isolate malformed records, and preserve
60
+ successful completion identities across monitor restarts.
61
+ - Correlate pending interactions, recover long-turn boundaries, and distinguish
62
+ failed/aborted work from successful responses.
63
+ - Limit acknowledgment to displayed results, recover from WebSocket closure and
64
+ token-worker errors, bound commands/logs, and close SQLite connections promptly.
65
+ - Add macOS/Python CI, package metadata, wheel smoke tests and recovery/security
66
+ documentation. Adapter versions and limitations are listed in
67
+ [COMPATIBILITY](docs/COMPATIBILITY.md).
68
+ - Verify Claude process identity through libproc on macOS instead of forking `ps`
69
+ every poll, keeping `ps` as the fallback where libproc is unavailable; skip
70
+ fsync for the per-poll status snapshot and rewrite `front.png` only when the
71
+ frame changes.
72
+ - Center the Codex character on Clawd in the duo view and keep its face visible
73
+ when dimmed.
74
+ - Add an optional macOS menu bar icon (`uv run busy-crew menubar install`) showing the
75
+ collector's health (stopped, degraded, update available, paused), with
76
+ acknowledge, reload and open-log actions. The collector publishes `health`,
77
+ `code_changed`, `pid` and `started_at` in `status.json`.
78
+ - Add `agent-deck refresh` and a **Refresh tokens now** menu bar action to recount
79
+ today's tokens immediately, instead of waiting up to two minutes. The menu also
80
+ shows the token totals and when they were counted.
81
+
82
+ ## 0.1.0
83
+
84
+ Initial local Claude/Codex front-display monitor, controls and optional daily
85
+ ccusage token view. No remote-machine tracking.
@@ -0,0 +1,63 @@
1
+ # Contributing
2
+
3
+ Forks, bug reports, and pull requests are welcome. By submitting a
4
+ contribution, you agree to license it under the project's [MIT License](LICENSE).
5
+ You retain your copyright and must have the right to submit the contribution
6
+ under these terms.
7
+ Keep any required third-party license and copyright notices intact.
8
+
9
+ Use Python 3.11 or newer and [uv](https://docs.astral.sh/uv/):
10
+
11
+ ```sh
12
+ uv sync --locked
13
+ uv run python -m unittest discover -s tests -v
14
+ uv build
15
+ ```
16
+
17
+ Tests must use temporary directories and synthetic Claude/Codex data. Mock
18
+ launchctl, processes, network requests and the device. Do not install hooks,
19
+ restart the user's service, modify real agent databases or contact a BUSY Bar
20
+ from tests. Keep conversation contents, IDs, local paths, API tokens and runtime
21
+ files out of commits, issue attachments and test fixtures.
22
+
23
+ For changes to an adapter, include a regression covering its observable result
24
+ and failure behavior. Record the tested source version and distinguish internal
25
+ file formats from supported public APIs. For render changes, generate the
26
+ fictional previews with `uv run busy-crew demo /tmp/busy-crew-demo` and inspect
27
+ them at the native 72 × 16 resolution as well as enlarged. The README images in
28
+ `docs/` are real captures (for example `uv run busy-crew snapshot docs/tokens-today.png --leds`); do not overwrite them
29
+ with fictional previews.
30
+
31
+ CI tests Linux and macOS on Python 3.11, 3.14 and 3.15. Device integration and real
32
+ LaunchAgent lifecycle checks require an explicitly chosen test device/account;
33
+ unit tests and wheel smoke tests do not establish those results.
34
+
35
+ ## Releasing
36
+
37
+ 1. In a pull request, bump `version` in `pyproject.toml`, run `uv lock`, and
38
+ move the CHANGELOG's "Unreleased" notes under the new version.
39
+ 2. Merge it, then publish a GitHub release whose tag is `v` plus that version
40
+ (`gh release create v1.2.3 --generate-notes` or the web form).
41
+ 3. The Release workflow does the rest. It checks that the tag matches the
42
+ version, tests, builds and publishes to PyPI. Then it opens a pull request on
43
+ [cmdai-hq/homebrew-tap](https://github.com/cmdai-hq/homebrew-tap) that points
44
+ the formula at the new sdist and regenerates its resources from `uv.lock`.
45
+ That pull request merges itself once `brew test-bot` passes.
46
+ 4. Installed copies see the release within a day (see
47
+ [docs/privacy.md](docs/privacy.md)) and offer **Update to X** in the menu
48
+ bar icon.
49
+
50
+ Before the first release, set up:
51
+
52
+ - the `pypi` environment in this repository, with required reviewers;
53
+ - a PyPI trusted publisher for `busy-crew`: owner `cmdai-hq`, repository
54
+ `busy-crew`, workflow `release.yml`, environment `pypi`;
55
+ - a `HOMEBREW_TAP_TOKEN` secret: a fine-grained token limited to
56
+ `cmdai-hq/homebrew-tap`, with read and write access to contents and pull
57
+ requests;
58
+ - on `cmdai-hq/homebrew-tap`: **Allow auto-merge** in the settings, and a
59
+ branch rule on `main` that requires the `test-bot (macos-latest)` check.
60
+ Without the required check, auto-merge does not wait for `brew test-bot`.
61
+
62
+ Re-running a failed Release job is safe: the tap step reuses its branch and
63
+ pull request, and stops if the tap already has the version.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tristan Bessoussa
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,189 @@
1
+ Metadata-Version: 2.5
2
+ Name: busy-crew
3
+ Version: 1.1.0
4
+ Summary: Claude and Codex sessions on a BUSY Bar's front LED display
5
+ Project-URL: Homepage, https://github.com/cmdai-hq/busy-crew
6
+ Project-URL: Issues, https://github.com/cmdai-hq/busy-crew/issues
7
+ Project-URL: Source, https://github.com/cmdai-hq/busy-crew
8
+ Project-URL: Changelog, https://github.com/cmdai-hq/busy-crew/blob/main/CHANGELOG.md
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: busy-bar,claude-code,codex,led,macos,menu-bar,status
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Environment :: MacOS X
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: MacOS :: MacOS X
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: busylib==2.6.0
23
+ Requires-Dist: pillow==12.3.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Busy Crew
27
+
28
+ **See what your Claude Code and Codex agents are doing at a glance, on the LED strip of your [BUSY Bar](https://busy.app).** Each session is a little character: working, waiting for you, or done.
29
+
30
+ <p align="center">
31
+ <img src="https://raw.githubusercontent.com/cmdai-hq/busy-crew/main/docs/busy-bar.jpg" width="576" alt="A BUSY Bar running Busy Crew: 4 Claude sessions and 3 Codex conversations, with their states">
32
+ </p>
33
+
34
+ Your Mac reads Claude Code and Codex activity locally and sends a 72 × 16 pixel frame to the bar. No conversation content leaves your Mac and no cloud service is involved. Busy Crew is an independent project, not affiliated with BUSY, Anthropic or OpenAI.
35
+
36
+ ## What you see
37
+
38
+ When both families are active, the **duo** view above shows each family's count and up to four state dots. With only one family, each session gets its own character. Turn the scroll wheel to see the tokens your agents used.
39
+
40
+ <table>
41
+ <tr>
42
+ <td align="center"><img src="https://raw.githubusercontent.com/cmdai-hq/busy-crew/main/docs/claude-team.png" width="360" alt="Claude only: one working session, four finished, +2 more"><br>Claude only</td>
43
+ <td align="center"><img src="https://raw.githubusercontent.com/cmdai-hq/busy-crew/main/docs/codex-team.png" width="360" alt="Codex only: one working, one finished, one failed"><br>Codex only</td>
44
+ </tr>
45
+ <tr>
46
+ <td align="center"><img src="https://raw.githubusercontent.com/cmdai-hq/busy-crew/main/docs/tokens-today.png" width="360" alt="Tokens today: Claude 1.0G, Codex 396M"><br>Tokens today</td>
47
+ <td align="center"><img src="https://raw.githubusercontent.com/cmdai-hq/busy-crew/main/docs/tokens-month.png" width="360" alt="Tokens this month: Claude 9.1G, Codex 1.7G"><br>Tokens this month</td>
48
+ </tr>
49
+ </table>
50
+
51
+ | On the bar | Meaning |
52
+ | --- | --- |
53
+ | Terracotta (Claude) or blue (Codex), legs moving | Working |
54
+ | Yellow `!` | Waiting for you: a question or a permission |
55
+ | Green `✓` | Finished, not yet acknowledged |
56
+ | Gray, `zzz` | Open but idle Claude session; `zzz` when all of them are |
57
+ | Gray `?` | Failed turn, unknown state or unreadable source |
58
+ | `+N` | More sessions than the six slots |
59
+
60
+ Token totals are shown in K (thousand), M (million) or G (billion) and include cache tokens. These captures are real, taken on the author's bar with commands like `busy-crew snapshot docs/tokens-today.png --leds`.
61
+
62
+ ## Requirements
63
+
64
+ - A Mac (macOS) and a [BUSY Bar](https://busy.app) on the same network
65
+ - [Homebrew](https://brew.sh), or Python 3.11+ and [uv](https://docs.astral.sh/uv/)
66
+ - [Claude Code](https://code.claude.com) and/or the [Codex app](https://openai.com/codex/), used on this Mac
67
+ - Optional: [ccusage](https://ccusage.com/) 20+ for the token views
68
+
69
+ ## Install
70
+
71
+ ```sh
72
+ brew install cmdai-hq/tap/busy-crew # or: uv tool install busy-crew
73
+ busy-crew install --address http://busybar.local --timezone Europe/Paris
74
+ busy-crew status
75
+ ```
76
+
77
+ Use your bar's address. Your agents appear on the bar within a few seconds.
78
+
79
+ **Updating:** `brew upgrade busy-crew` or `uv tool upgrade busy-crew`, then `busy-crew install`, which keeps your settings, refreshes the LaunchAgent and hooks and restarts the collector. If you use the [menu bar icon](#menu-bar-icon), run `busy-crew menubar install` too. **Removing:** run `busy-crew uninstall` first, then `brew uninstall busy-crew` or `uv tool uninstall busy-crew`.
80
+
81
+ **From a checkout**, to contribute or try unreleased changes, prefix every command in this README with `uv run`, and update with `git pull --ff-only && uv sync --locked` before the same `install`:
82
+
83
+ ```sh
84
+ git clone https://github.com/cmdai-hq/busy-crew.git
85
+ cd busy-crew
86
+ uv sync --locked
87
+ uv run busy-crew install --address http://busybar.local --timezone Europe/Paris
88
+ ```
89
+
90
+ <details>
91
+ <summary>What <code>install</code> changes on your Mac</summary>
92
+
93
+ - **A LaunchAgent** (`local.busybar.agent-deck`) starts the collector at login and restarts it if it exits. It runs the interpreter given to `install` with `--python` or `BUSY_CREW_PYTHON`, else the Python that ran `install`. From a checkout, that is the checkout's environment, so **keep the repository folder where it is**. From a Homebrew package, it is the formula's `opt` path, which survives upgrades.
94
+ - **Claude Code hooks**, added to Claude's settings after a private backup. They record only status identifiers and timestamps, never prompts, and never block Claude. Existing hooks and settings are preserved; a conflicting edit fails with recovery copies. Use `--no-claude-hooks` to rely on Claude's session registry alone, with coarser waiting and result detection.
95
+ - **A private state folder**, `~/Library/Application Support/BusyCrew`, for the configuration, backups and the log. Installations made before 1.0 keep `BusybarAgentDeck`.
96
+
97
+ Re-running `install` keeps the saved address and time zone unless you pass new ones. `busy-crew uninstall` removes the LaunchAgent and the hooks, and keeps the state folder.
98
+ </details>
99
+
100
+ ## Using it
101
+
102
+ | On the bar | Action |
103
+ | --- | --- |
104
+ | **Start / Pause** | Cycle: sessions → tokens today → tokens this month |
105
+ | **Scroll wheel**, turn | One view forward (+) or back (−) |
106
+ | **Scroll wheel**, press | Acknowledge the `✓` on screen |
107
+ | **Back** | Return to sessions |
108
+ | Selector on Off or Settings | Pause the display |
109
+
110
+ A session waiting for you always brings the sessions view back. No button answers a question or grants a permission: do that in Claude or Codex.
111
+
112
+ The same from the terminal:
113
+
114
+ ```sh
115
+ busy-crew view sessions # or: tokens, month
116
+ busy-crew ack # acknowledge the results on screen
117
+ busy-crew refresh # recount tokens now
118
+ busy-crew snapshot front.png --leds
119
+ ```
120
+
121
+ A `✓` counts toward its family's total until you acknowledge it, the session starts new work, or it goes away (a closed Claude process, an archived Codex conversation, or a Codex response finished more than two hours ago). [How results and acknowledgment work](https://github.com/cmdai-hq/busy-crew/blob/main/docs/how-it-works.md#results-and-acknowledgment).
122
+
123
+ ### Menu bar icon
124
+
125
+ Optional. When the bar goes dark, the icon in your macOS menu bar says why: running, paused by the selector, degraded, stopped, or waiting for a reload after an update. Its menu shows each family's sessions and today's tokens, and lets you acknowledge results, recount tokens, reload the collector, open the log, or install a new release.
126
+
127
+ <p align="center">
128
+ <img src="https://raw.githubusercontent.com/cmdai-hq/busy-crew/main/docs/menu-bar.png" width="540" alt="Busy Crew's menu, open from the macOS menu bar: Running, started 6 minutes ago; Claude 1 working, 2 done, 1 idle; Codex 3 working, 1 done; today's tokens; Acknowledge results, Refresh tokens now, Reload collector, Open log, Quit menu icon">
129
+ </p>
130
+
131
+ ```sh
132
+ busy-crew menubar install # needs the Swift compiler: xcode-select --install
133
+ ```
134
+
135
+ [Icon states, actions and updates](https://github.com/cmdai-hq/busy-crew/blob/main/docs/menu-bar.md). The picture is a simulation of the real menu.
136
+
137
+ ## Configuration
138
+
139
+ `install` writes `config.json` in the state folder ([example](https://github.com/cmdai-hq/busy-crew/blob/main/docs/config.example.json)):
140
+
141
+ | Key | Purpose |
142
+ | --- | --- |
143
+ | `address` | The bar's URL, e.g. `http://busybar.local` |
144
+ | `timezone` | Time zone for "today" and "this month" |
145
+ | `ccusage` | Absolute path to `ccusage`, if the LaunchAgent cannot find it |
146
+ | `token` | The bar's local API key, if LAN access requires one |
147
+ | `update_check` | `false` stops the daily check for a new release on GitHub |
148
+
149
+ Over HTTP the key travels unencrypted: use a trusted network, or HTTPS if your device supports it. The busylib WebSocket SDK puts the key in a URL query and honors proxy environment variables; don't share those URLs or proxy logs.
150
+
151
+ `CLAUDE_CONFIG_DIR`, `CODEX_HOME` and `BUSY_CREW_STATE` (formerly `AGENT_DECK_STATE`, still honored) are supported; set them before installing, which pins them in the LaunchAgent and the hook command.
152
+
153
+ ## Troubleshooting
154
+
155
+ Start with `busy-crew status`.
156
+
157
+ - **The bar went dark:** check `status` first. The collector may have stopped, or it is running while the selector is on Off or Settings, or a higher-priority app owns the display. The [menu bar icon](https://github.com/cmdai-hq/busy-crew/blob/main/docs/menu-bar.md) tells these apart at a glance.
158
+ - **Token views show `?`:** ccusage is missing or failed; sessions still work. Set `ccusage` in `config.json`.
159
+ - **`busy-crew` not found, or `invalid choice`:** you're running an older copy. Update it as above; from a checkout, use `uv run busy-crew`. The pre-1.0 `agent-deck` command still works.
160
+
161
+ More, including Claude settings conflicts and recovery: [troubleshooting](https://github.com/cmdai-hq/busy-crew/blob/main/docs/troubleshooting.md).
162
+
163
+ ## How it works
164
+
165
+ - **Claude Code:** the session registry (`~/.claude/sessions`) gives each session, its state and its process; hooks sharpen "waiting for you" and finished results.
166
+ - **Codex:** the app's local SQLite databases and conversation logs, opened read-only.
167
+ - **Tokens:** one ccusage daily report from the 1st of the month, refreshed every two minutes.
168
+ - **The bar:** a frame through the BUSY Bar local API, with a 15-second lease so the bar recovers its own display if your Mac goes away.
169
+
170
+ Limits worth knowing: Codex approval requests are only partly visible, cloud tasks and other machines aren't tracked. Details: [how it works](https://github.com/cmdai-hq/busy-crew/blob/main/docs/how-it-works.md), [tested versions](https://github.com/cmdai-hq/busy-crew/blob/main/docs/COMPATIBILITY.md).
171
+
172
+ **Privacy:** Busy Crew talks to your bar: frames, display cleanup and its button events, with the bar's API key if you set one. Once a day it asks GitHub for the number of its latest release; `"update_check": false` turns that off. Apart from turn events and the questions an agent asks you, conversation content is never parsed, and nothing from it is stored, logged or sent. [What is read, kept and sent](https://github.com/cmdai-hq/busy-crew/blob/main/docs/privacy.md).
173
+
174
+ ## Development
175
+
176
+ ```sh
177
+ uv sync --locked
178
+ uv run python -m unittest discover -s tests -v
179
+ uv run busy-crew run # run the collector in the foreground
180
+ uv run busy-crew demo /tmp/demo # fictional previews of each layout
181
+ ```
182
+
183
+ The tests never contact a device, run a model or touch your real agent data. CI runs them on macOS and Linux with Python 3.11, 3.14 and 3.15. Verified on BUSY Bar firmware 1.2.4 (API 27.5.0). See [CONTRIBUTING](https://github.com/cmdai-hq/busy-crew/blob/main/CONTRIBUTING.md) and [SECURITY](https://github.com/cmdai-hq/busy-crew/blob/main/SECURITY.md).
184
+
185
+ ## License
186
+
187
+ [MIT](https://github.com/cmdai-hq/busy-crew/blob/main/LICENSE). Contributions are welcome; see [CONTRIBUTING](https://github.com/cmdai-hq/busy-crew/blob/main/CONTRIBUTING.md).
188
+
189
+ The characters are drawn in code, inspired by Clawd and Codex. References: [BUSY Bar developer docs](https://docs.busy.app/bar/dev), [busylib](https://github.com/busy-app/busylib-py), [Claude Code hooks](https://code.claude.com/docs/en/hooks), [ccusage](https://ccusage.com/).