roost-top 0.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.
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ *.egg-info/
roost_top-0.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 George M. Howard
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.
roost_top-0.2/PKG-INFO ADDED
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.4
2
+ Name: roost-top
3
+ Version: 0.2
4
+ Summary: top for Claude Code -- live sessions, models, context, and the subagents they spawn
5
+ Project-URL: Homepage, https://github.com/gmhoward9289-ops/roost
6
+ Project-URL: Issues, https://github.com/gmhoward9289-ops/roost/issues
7
+ Author-email: "George M. Howard" <dev@swamplink.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: claude,claude-code,context,monitoring,top,tui
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: MacOS :: MacOS X
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: System :: Monitoring
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+
25
+ # roost
26
+
27
+ [![ci](https://github.com/gmhoward9289-ops/roost/actions/workflows/ci.yml/badge.svg)](https://github.com/gmhoward9289-ops/roost/actions/workflows/ci.yml)
28
+
29
+ `top` for Claude Code. Every live session, the model it is on, how much context
30
+ it has burned — and, unlike anything else, **the subagents it spawned**.
31
+
32
+ One file, no dependencies, Python 3.9+. Runs on macOS, Linux and Windows.
33
+
34
+ ```
35
+ WORKER MODEL CTX IDLE TASK
36
+ NEAR LIMIT
37
+ demo-a1 opus-5 85% 12s refactor the parser
38
+ PARKED + COSTLY
39
+ demo-b2 opus-5 61% 4h10m audit the build scripts
40
+ WORKING NOW
41
+ demo-c3 fable-5 22% 3s add integration tests
42
+ STARTING
43
+ demo-d4 - - -
44
+
45
+ QUIET (4) demo-e5 . demo-f6 . demo-g7 . demo-h8
46
+
47
+ 8 worker(s) | fable-5, opus-5
48
+
49
+ SUBAGENTS
50
+ STATE AGENT MODEL CTX IDLE TASK
51
+ working a812aca59f opus-5 33% 2s survey the config loaders
52
+ idle adaffaba4b sonnet-5 67% 1h22m draft the migration notes
53
+
54
+ 2 subagent(s), 1 working
55
+
56
+ INFRA ollama:11434 up qwen2.5-coder:14b (9.2 GB) litellm:4000 up openwebui:8080 DOWN
57
+ ```
58
+
59
+ Sessions are grouped by what it costs to ignore them, not by size: `NEAR LIMIT`
60
+ is about to stop working, `PARKED + COSTLY` bills its whole context on the next
61
+ turn, and everything quiet collapses to a single line.
62
+
63
+ ## Install
64
+
65
+ Homebrew (macOS and Linux):
66
+
67
+ ```bash
68
+ brew install gmhoward9289-ops/tap/roost
69
+ ```
70
+
71
+ Debian and Ubuntu — grab `roost_<version>_all.deb` from the
72
+ [latest release](https://github.com/gmhoward9289-ops/roost/releases/latest):
73
+
74
+ ```bash
75
+ sudo apt install ./roost_0.2_all.deb
76
+ ```
77
+
78
+ There is no PPA and no apt repository; the `.deb` is a release artifact, and
79
+ `apt install ./file.deb` resolves `python3` exactly as a repo install would.
80
+
81
+ With pipx:
82
+
83
+ ```bash
84
+ pipx install roost-top
85
+ ```
86
+
87
+ PyPI holds the bare name `roost` in reserve — a prior project's name, retained
88
+ after deletion — so the *package* is `roost-top`; the command it installs is
89
+ plain `roost`. Installing straight from the repo skips the index entirely:
90
+
91
+ ```bash
92
+ pipx install git+https://github.com/gmhoward9289-ops/roost
93
+ ```
94
+
95
+ Or just take the file. It is one script, stdlib only, no dependencies:
96
+
97
+ ```bash
98
+ curl -o roost https://raw.githubusercontent.com/gmhoward9289-ops/roost/main/roost.py
99
+ chmod +x roost && ./roost
100
+ ```
101
+
102
+ Windows: save it as `roost.py` and run it — `.PY` is in `PATHEXT`, so `roost.py`
103
+ works from anywhere on `PATH`. There is no Windows package; the single file is
104
+ the install.
105
+
106
+ The man page (`man roost`) ships with the Homebrew and `.deb` installs. A pipx
107
+ install puts it under the venv's own `share/man`, which is not on the default
108
+ `MANPATH`; read it in place with
109
+ `man "$(pipx environment --value PIPX_LOCAL_VENVS)/roost-top/share/man/man1/roost.1"`.
110
+
111
+ ## Use
112
+
113
+ ```
114
+ roost live, refreshing every second
115
+ roost -w 5 slower refresh
116
+ roost -1 one frame, then exit
117
+ roost --json joined records, for piping
118
+ ```
119
+
120
+ While running: `space` refresh now · `a` advice panel · `s` subagents panel · `q` quit
121
+
122
+ ## Acting on a session
123
+
124
+ > **Experimental.** Interactive mode carries an `EXPERIMENTAL` marker in the
125
+ > top-right corner, and it means it — `x` ends a real process. Reading the
126
+ > dashboard has never been the risky half.
127
+
128
+ `j`/`k` (or the arrow keys) raise a cursor. Raising it expands the `QUIET`
129
+ group, because a session idle for hours is exactly what a sweep is looking for
130
+ and it is unreachable while collapsed.
131
+
132
+ | key | does |
133
+ | --- | --- |
134
+ | `j` `k` `↓` `↑` | move the cursor |
135
+ | `x` | stop the selected session — confirms first, and only `y` proceeds |
136
+ | `y` | copy its sessionId, for `claude --resume <id>` |
137
+ | `esc` | drop the cursor, re-collapse `QUIET` |
138
+
139
+ `x` ends a process. It does not compact, save, or otherwise negotiate with the
140
+ session — **there is no local control channel into a running Claude Code
141
+ session**, so nothing gentler is available from outside it. On Unix that is a
142
+ `SIGTERM` and the session exits on its own terms; on Windows there is no
143
+ cross-process equivalent, so it is a `TerminateProcess` hard kill. Transcripts
144
+ are written a turn at a time, so at most an in-flight turn is lost.
145
+
146
+ Both keys act on the row object that was on screen when you pressed them, never
147
+ on an index re-resolved afterwards. Rows reorder between frames as sessions go
148
+ quiet, and an index that outlived its frame would eventually stop the wrong one.
149
+
150
+ roost refuses to stop its own process or its parent — run it from inside the
151
+ session it is pointed at and the cursor can land on the row that owns your
152
+ terminal.
153
+
154
+ Only one panel is open at a time: `a` and `s` flip between ADVICE and SUBAGENTS
155
+ rather than stacking. With two dozen sessions on screen a stacked second panel
156
+ lands below the bottom of the terminal, which is indistinguishable from the key
157
+ not working. For the same reason the frame now says `... N more line(s) below`
158
+ instead of quietly truncating.
159
+
160
+ ## What it logs
161
+
162
+ Every session stopped with `x` appends one JSON line to
163
+ `~/.claude/logs/roost.jsonl` — same shape and the same 5000-line cap as the hook
164
+ logs beside it:
165
+
166
+ ```json
167
+ {"ts":"2026-07-31T00:22:57-0400","action":"stop","ok":true,"host":"COOPER",
168
+ "name":"models-ca","pid":4321,"session_id":"abc-123","model":"claude-opus-5",
169
+ "ctx_tokens":484030,"idle_secs":92500}
170
+ ```
171
+
172
+ The session's **task text is deliberately not recorded.** It is free-form prose
173
+ out of a transcript, and an audit trail of what was stopped should not become a
174
+ copy of what was being worked on.
175
+
176
+ Because each record carries the context that session was holding, the log
177
+ answers afterwards what a sweep actually reclaimed rather than just how many
178
+ rows you closed. `--no-log` records nothing. A log that cannot be written is
179
+ ignored rather than raised — losing the log is survivable, losing the display
180
+ is not.
181
+
182
+ ## Why subagents are the interesting part
183
+
184
+ Subagents have **no process of their own** — they run as sidechains inside the
185
+ parent's process. Every pid-based view is structurally blind to them.
186
+
187
+ They do each get a transcript, one directory deeper than the session transcripts:
188
+
189
+ ```
190
+ ~/.claude/projects/<slug>/<sessionId>/subagents/agent-<id>.jsonl
191
+ ```
192
+
193
+ The short task description ("Scout source URLs") lives only in the *parent's*
194
+ `toolUseResult`, keyed by `agentId`. roost joins the two, and falls back to the
195
+ opening words of the subagent's own first message when the parent's record has
196
+ scrolled out of reach.
197
+
198
+ ## Where the numbers come from
199
+
200
+ Three local, read-only sources. Nothing is sent anywhere; there is no network
201
+ call except a localhost probe of the infra ports.
202
+
203
+ | source | gives |
204
+ | --- | --- |
205
+ | `~/.claude/sessions/<pid>.json` | live sessions: pid, sessionId, launch cwd, name |
206
+ | `~/.claude/projects/*/<sid>.jsonl` | model in use, token usage |
207
+ | `127.0.0.1` ports | ollama / litellm / openwebui |
208
+
209
+ **Context** is the last assistant turn's `input_tokens + cache_read_input_tokens
210
+ + cache_creation_input_tokens`. Cross-checked against an independent tool on the
211
+ same session: 77% vs 77.29%.
212
+
213
+ **The context window is inferred, not recorded.** Nothing on disk states which
214
+ window a session opened with, and a session on the 1M window will read 480k+
215
+ cache tokens in a single call — scoring that against 200k yields a nonsense
216
+ "242%". roost picks the smallest standard tier the usage fits and prints it in
217
+ the `WIN` column, so the assumption is visible rather than silent. If a new tier
218
+ ships, `WINDOW_TIERS` is the one line to edit.
219
+
220
+ ## Caveats
221
+
222
+ - It reads an **undocumented on-disk format** that can change without warning.
223
+ That is the whole foundation; treat breakage as expected, not exceptional.
224
+ - The window inference above is a heuristic.
225
+ - The `ADVICE` panel's thresholds are tuned to one person's usage. Read
226
+ `EXPENSIVE_TOKENS` and friends before trusting the advice.
227
+ - Daily-driven on macOS and Windows. CI runs the suite and a smoke frame on
228
+ Linux, and it detects live sessions there — but nobody lives on it yet.
229
+ Field reports welcome.
230
+
231
+ ## License
232
+
233
+ MIT — see [LICENSE](LICENSE). Contact: dev@swamplink.com
234
+
235
+ Built in a Digital Swamp. From my swamp to yours.
@@ -0,0 +1,211 @@
1
+ # roost
2
+
3
+ [![ci](https://github.com/gmhoward9289-ops/roost/actions/workflows/ci.yml/badge.svg)](https://github.com/gmhoward9289-ops/roost/actions/workflows/ci.yml)
4
+
5
+ `top` for Claude Code. Every live session, the model it is on, how much context
6
+ it has burned — and, unlike anything else, **the subagents it spawned**.
7
+
8
+ One file, no dependencies, Python 3.9+. Runs on macOS, Linux and Windows.
9
+
10
+ ```
11
+ WORKER MODEL CTX IDLE TASK
12
+ NEAR LIMIT
13
+ demo-a1 opus-5 85% 12s refactor the parser
14
+ PARKED + COSTLY
15
+ demo-b2 opus-5 61% 4h10m audit the build scripts
16
+ WORKING NOW
17
+ demo-c3 fable-5 22% 3s add integration tests
18
+ STARTING
19
+ demo-d4 - - -
20
+
21
+ QUIET (4) demo-e5 . demo-f6 . demo-g7 . demo-h8
22
+
23
+ 8 worker(s) | fable-5, opus-5
24
+
25
+ SUBAGENTS
26
+ STATE AGENT MODEL CTX IDLE TASK
27
+ working a812aca59f opus-5 33% 2s survey the config loaders
28
+ idle adaffaba4b sonnet-5 67% 1h22m draft the migration notes
29
+
30
+ 2 subagent(s), 1 working
31
+
32
+ INFRA ollama:11434 up qwen2.5-coder:14b (9.2 GB) litellm:4000 up openwebui:8080 DOWN
33
+ ```
34
+
35
+ Sessions are grouped by what it costs to ignore them, not by size: `NEAR LIMIT`
36
+ is about to stop working, `PARKED + COSTLY` bills its whole context on the next
37
+ turn, and everything quiet collapses to a single line.
38
+
39
+ ## Install
40
+
41
+ Homebrew (macOS and Linux):
42
+
43
+ ```bash
44
+ brew install gmhoward9289-ops/tap/roost
45
+ ```
46
+
47
+ Debian and Ubuntu — grab `roost_<version>_all.deb` from the
48
+ [latest release](https://github.com/gmhoward9289-ops/roost/releases/latest):
49
+
50
+ ```bash
51
+ sudo apt install ./roost_0.2_all.deb
52
+ ```
53
+
54
+ There is no PPA and no apt repository; the `.deb` is a release artifact, and
55
+ `apt install ./file.deb` resolves `python3` exactly as a repo install would.
56
+
57
+ With pipx:
58
+
59
+ ```bash
60
+ pipx install roost-top
61
+ ```
62
+
63
+ PyPI holds the bare name `roost` in reserve — a prior project's name, retained
64
+ after deletion — so the *package* is `roost-top`; the command it installs is
65
+ plain `roost`. Installing straight from the repo skips the index entirely:
66
+
67
+ ```bash
68
+ pipx install git+https://github.com/gmhoward9289-ops/roost
69
+ ```
70
+
71
+ Or just take the file. It is one script, stdlib only, no dependencies:
72
+
73
+ ```bash
74
+ curl -o roost https://raw.githubusercontent.com/gmhoward9289-ops/roost/main/roost.py
75
+ chmod +x roost && ./roost
76
+ ```
77
+
78
+ Windows: save it as `roost.py` and run it — `.PY` is in `PATHEXT`, so `roost.py`
79
+ works from anywhere on `PATH`. There is no Windows package; the single file is
80
+ the install.
81
+
82
+ The man page (`man roost`) ships with the Homebrew and `.deb` installs. A pipx
83
+ install puts it under the venv's own `share/man`, which is not on the default
84
+ `MANPATH`; read it in place with
85
+ `man "$(pipx environment --value PIPX_LOCAL_VENVS)/roost-top/share/man/man1/roost.1"`.
86
+
87
+ ## Use
88
+
89
+ ```
90
+ roost live, refreshing every second
91
+ roost -w 5 slower refresh
92
+ roost -1 one frame, then exit
93
+ roost --json joined records, for piping
94
+ ```
95
+
96
+ While running: `space` refresh now · `a` advice panel · `s` subagents panel · `q` quit
97
+
98
+ ## Acting on a session
99
+
100
+ > **Experimental.** Interactive mode carries an `EXPERIMENTAL` marker in the
101
+ > top-right corner, and it means it — `x` ends a real process. Reading the
102
+ > dashboard has never been the risky half.
103
+
104
+ `j`/`k` (or the arrow keys) raise a cursor. Raising it expands the `QUIET`
105
+ group, because a session idle for hours is exactly what a sweep is looking for
106
+ and it is unreachable while collapsed.
107
+
108
+ | key | does |
109
+ | --- | --- |
110
+ | `j` `k` `↓` `↑` | move the cursor |
111
+ | `x` | stop the selected session — confirms first, and only `y` proceeds |
112
+ | `y` | copy its sessionId, for `claude --resume <id>` |
113
+ | `esc` | drop the cursor, re-collapse `QUIET` |
114
+
115
+ `x` ends a process. It does not compact, save, or otherwise negotiate with the
116
+ session — **there is no local control channel into a running Claude Code
117
+ session**, so nothing gentler is available from outside it. On Unix that is a
118
+ `SIGTERM` and the session exits on its own terms; on Windows there is no
119
+ cross-process equivalent, so it is a `TerminateProcess` hard kill. Transcripts
120
+ are written a turn at a time, so at most an in-flight turn is lost.
121
+
122
+ Both keys act on the row object that was on screen when you pressed them, never
123
+ on an index re-resolved afterwards. Rows reorder between frames as sessions go
124
+ quiet, and an index that outlived its frame would eventually stop the wrong one.
125
+
126
+ roost refuses to stop its own process or its parent — run it from inside the
127
+ session it is pointed at and the cursor can land on the row that owns your
128
+ terminal.
129
+
130
+ Only one panel is open at a time: `a` and `s` flip between ADVICE and SUBAGENTS
131
+ rather than stacking. With two dozen sessions on screen a stacked second panel
132
+ lands below the bottom of the terminal, which is indistinguishable from the key
133
+ not working. For the same reason the frame now says `... N more line(s) below`
134
+ instead of quietly truncating.
135
+
136
+ ## What it logs
137
+
138
+ Every session stopped with `x` appends one JSON line to
139
+ `~/.claude/logs/roost.jsonl` — same shape and the same 5000-line cap as the hook
140
+ logs beside it:
141
+
142
+ ```json
143
+ {"ts":"2026-07-31T00:22:57-0400","action":"stop","ok":true,"host":"COOPER",
144
+ "name":"models-ca","pid":4321,"session_id":"abc-123","model":"claude-opus-5",
145
+ "ctx_tokens":484030,"idle_secs":92500}
146
+ ```
147
+
148
+ The session's **task text is deliberately not recorded.** It is free-form prose
149
+ out of a transcript, and an audit trail of what was stopped should not become a
150
+ copy of what was being worked on.
151
+
152
+ Because each record carries the context that session was holding, the log
153
+ answers afterwards what a sweep actually reclaimed rather than just how many
154
+ rows you closed. `--no-log` records nothing. A log that cannot be written is
155
+ ignored rather than raised — losing the log is survivable, losing the display
156
+ is not.
157
+
158
+ ## Why subagents are the interesting part
159
+
160
+ Subagents have **no process of their own** — they run as sidechains inside the
161
+ parent's process. Every pid-based view is structurally blind to them.
162
+
163
+ They do each get a transcript, one directory deeper than the session transcripts:
164
+
165
+ ```
166
+ ~/.claude/projects/<slug>/<sessionId>/subagents/agent-<id>.jsonl
167
+ ```
168
+
169
+ The short task description ("Scout source URLs") lives only in the *parent's*
170
+ `toolUseResult`, keyed by `agentId`. roost joins the two, and falls back to the
171
+ opening words of the subagent's own first message when the parent's record has
172
+ scrolled out of reach.
173
+
174
+ ## Where the numbers come from
175
+
176
+ Three local, read-only sources. Nothing is sent anywhere; there is no network
177
+ call except a localhost probe of the infra ports.
178
+
179
+ | source | gives |
180
+ | --- | --- |
181
+ | `~/.claude/sessions/<pid>.json` | live sessions: pid, sessionId, launch cwd, name |
182
+ | `~/.claude/projects/*/<sid>.jsonl` | model in use, token usage |
183
+ | `127.0.0.1` ports | ollama / litellm / openwebui |
184
+
185
+ **Context** is the last assistant turn's `input_tokens + cache_read_input_tokens
186
+ + cache_creation_input_tokens`. Cross-checked against an independent tool on the
187
+ same session: 77% vs 77.29%.
188
+
189
+ **The context window is inferred, not recorded.** Nothing on disk states which
190
+ window a session opened with, and a session on the 1M window will read 480k+
191
+ cache tokens in a single call — scoring that against 200k yields a nonsense
192
+ "242%". roost picks the smallest standard tier the usage fits and prints it in
193
+ the `WIN` column, so the assumption is visible rather than silent. If a new tier
194
+ ships, `WINDOW_TIERS` is the one line to edit.
195
+
196
+ ## Caveats
197
+
198
+ - It reads an **undocumented on-disk format** that can change without warning.
199
+ That is the whole foundation; treat breakage as expected, not exceptional.
200
+ - The window inference above is a heuristic.
201
+ - The `ADVICE` panel's thresholds are tuned to one person's usage. Read
202
+ `EXPENSIVE_TOKENS` and friends before trusting the advice.
203
+ - Daily-driven on macOS and Windows. CI runs the suite and a smoke frame on
204
+ Linux, and it detects live sessions there — but nobody lives on it yet.
205
+ Field reports welcome.
206
+
207
+ ## License
208
+
209
+ MIT — see [LICENSE](LICENSE). Contact: dev@swamplink.com
210
+
211
+ Built in a Digital Swamp. From my swamp to yours.
@@ -0,0 +1,52 @@
1
+ #!/bin/sh
2
+ # Build a .deb for roost. Usage: packaging/build-deb.sh [version]
3
+ #
4
+ # Deliberately a plain dpkg-deb tree rather than a debian/ source package: roost
5
+ # is one architecture-independent script with no build step and no dependencies
6
+ # beyond python3 itself, so debhelper would add ceremony and no correctness.
7
+ # The result is meant to be attached to a GitHub release and installed with
8
+ # sudo apt install ./roost_<version>_all.deb
9
+ # which resolves python3 the same way an apt repo would. There is no PPA.
10
+ set -eu
11
+
12
+ ROOT=$(cd "$(dirname "$0")/.." && pwd)
13
+ VERSION=${1:-$(sed -n 's/^__version__ = "\(.*\)"/\1/p' "$ROOT/roost.py")}
14
+ [ -n "$VERSION" ] || { echo "could not determine version" >&2; exit 1; }
15
+
16
+ BUILD=$(mktemp -d)
17
+ trap 'rm -rf "$BUILD"' EXIT
18
+ PKG="$BUILD/roost_${VERSION}_all"
19
+
20
+ mkdir -p "$PKG/DEBIAN" "$PKG/usr/bin" "$PKG/usr/share/man/man1" \
21
+ "$PKG/usr/share/doc/roost"
22
+
23
+ # Installed as `roost`, not `roost.py`: the shebang and the executable bit are
24
+ # what make it a command, and the .py suffix only matters on Windows.
25
+ install -m 0755 "$ROOT/roost.py" "$PKG/usr/bin/roost"
26
+ gzip -9nc "$ROOT/roost.1" > "$PKG/usr/share/man/man1/roost.1.gz"
27
+ chmod 0644 "$PKG/usr/share/man/man1/roost.1.gz"
28
+ install -m 0644 "$ROOT/LICENSE" "$PKG/usr/share/doc/roost/copyright"
29
+
30
+ cat > "$PKG/DEBIAN/control" <<EOF
31
+ Package: roost
32
+ Version: $VERSION
33
+ Section: utils
34
+ Priority: optional
35
+ Architecture: all
36
+ Depends: python3 (>= 3.9)
37
+ Maintainer: George M. Howard <dev@swamplink.com>
38
+ Homepage: https://github.com/gmhoward9289-ops/roost
39
+ Description: top for Claude Code
40
+ Shows every live Claude Code session on the machine, the model each is
41
+ running, how much of its context window it has consumed, and the subagents
42
+ it has spawned -- which have no process of their own and are invisible to
43
+ any pid-based view.
44
+ .
45
+ Reads only local Claude Code state and probes localhost inference ports.
46
+ It sends nothing anywhere.
47
+ EOF
48
+
49
+ dpkg-deb --build --root-owner-group "$PKG" > /dev/null
50
+ mkdir -p "$ROOT/dist"
51
+ mv "$BUILD/roost_${VERSION}_all.deb" "$ROOT/dist/"
52
+ echo "dist/roost_${VERSION}_all.deb"
@@ -0,0 +1,38 @@
1
+ # Homebrew formula for roost.
2
+ #
3
+ # This is the master copy; it is consumed by copying it to Formula/roost.rb in
4
+ # the tap repo (gmhoward9289-ops/homebrew-tap), which is what `brew install`
5
+ # reads. It lives here so the formula is versioned alongside the code it builds.
6
+ #
7
+ # homebrew-core is not an option yet -- it requires notability thresholds
8
+ # (stars/forks/watchers) that this project has not met.
9
+ #
10
+ # After tagging a release, refresh the checksum with:
11
+ # curl -sL https://github.com/gmhoward9289-ops/roost/archive/refs/tags/v0.2.tar.gz | shasum -a 256
12
+ class Roost < Formula
13
+ include Language::Python::Shebang
14
+
15
+ desc "top for Claude Code: live sessions, context use, and their subagents"
16
+ homepage "https://github.com/gmhoward9289-ops/roost"
17
+ url "https://github.com/gmhoward9289-ops/roost/archive/refs/tags/v0.2.tar.gz"
18
+ sha256 "d7c5c1fec7438a25c4c412ebf716c0e40a70513dcd3bc330cdb3df6c25592402"
19
+ license "MIT"
20
+
21
+ depends_on "python@3.13"
22
+
23
+ def install
24
+ bin.install "roost.py" => "roost"
25
+ # The shipped shebang is `/usr/bin/env python3`, which would resolve to
26
+ # whatever python happens to be first on PATH -- including a virtualenv the
27
+ # user activated for something else. Pin it to the formula's interpreter.
28
+ rewrite_shebang detected_python_shebang(use_python_from_path: false), bin/"roost"
29
+ man1.install "roost.1"
30
+ end
31
+
32
+ test do
33
+ assert_match "roost #{version}", shell_output("#{bin}/roost --version")
34
+ # -1 renders a frame and exits; with no Claude Code sessions present it
35
+ # still has to produce the empty-state line rather than fail.
36
+ assert_match(/roost|session/i, shell_output("#{bin}/roost -1 --no-color"))
37
+ end
38
+ end
@@ -0,0 +1,56 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ # PyPI holds the bare name "roost" in reserve (a prior project's name, retained
7
+ # after deletion), so the distribution is roost-top. The command, the module,
8
+ # the repo, the formula and the .deb are all still plain roost.
9
+ name = "roost-top"
10
+ dynamic = ["version"]
11
+ description = "top for Claude Code -- live sessions, models, context, and the subagents they spawn"
12
+ readme = "README.md"
13
+ requires-python = ">=3.9"
14
+ license = { text = "MIT" }
15
+ authors = [{ name = "George M. Howard", email = "dev@swamplink.com" }]
16
+ keywords = ["claude", "claude-code", "tui", "top", "monitoring", "context"]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Environment :: Console",
20
+ "Intended Audience :: Developers",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: MacOS :: MacOS X",
23
+ "Operating System :: Microsoft :: Windows",
24
+ "Operating System :: POSIX :: Linux",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.9",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Topic :: System :: Monitoring",
29
+ ]
30
+ # No dependencies, and there will not be any: roost has to run on whatever
31
+ # Python is already on the box, including a bare system 3.9 on macOS.
32
+ dependencies = []
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/gmhoward9289-ops/roost"
36
+ Issues = "https://github.com/gmhoward9289-ops/roost/issues"
37
+
38
+ [project.scripts]
39
+ roost = "roost:main"
40
+
41
+ # One source of truth for the version: __version__ in roost.py, which is also
42
+ # what `roost --version` prints and what the man page header carries.
43
+ [tool.hatch.version]
44
+ path = "roost.py"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ only-include = ["roost.py"]
48
+
49
+ # Installs to <prefix>/share/man/man1. pipx and venv installs put that outside
50
+ # the default MANPATH, so `man roost` works after a system or Homebrew install
51
+ # but may need MANPATH help inside a venv -- see README.
52
+ [tool.hatch.build.targets.wheel.shared-data]
53
+ "roost.1" = "share/man/man1/roost.1"
54
+
55
+ [tool.hatch.build.targets.sdist]
56
+ include = ["roost.py", "roost.1", "README.md", "LICENSE", "tests/", "packaging/"]