agents-tree 0.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.
- agents_tree-0.1.0/LICENSE +21 -0
- agents_tree-0.1.0/PKG-INFO +150 -0
- agents_tree-0.1.0/README.md +133 -0
- agents_tree-0.1.0/pyproject.toml +72 -0
- agents_tree-0.1.0/pyproject.toml.orig +64 -0
- agents_tree-0.1.0/src/agents_tree/__init__.py +3 -0
- agents_tree-0.1.0/src/agents_tree/__main__.py +5 -0
- agents_tree-0.1.0/src/agents_tree/cli.py +105 -0
- agents_tree-0.1.0/src/agents_tree/herdr.py +326 -0
- agents_tree-0.1.0/src/agents_tree/live.py +263 -0
- agents_tree-0.1.0/src/agents_tree/model.py +111 -0
- agents_tree-0.1.0/src/agents_tree/providers/__init__.py +5 -0
- agents_tree-0.1.0/src/agents_tree/providers/claude.py +537 -0
- agents_tree-0.1.0/src/agents_tree/render.py +260 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Karel Blavka
|
|
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,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agents-tree
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Show Claude Code sessions and their subagents as a live tree: model, effort, context use, elapsed time, status.
|
|
5
|
+
Keywords: claude-code,subagents,agents,cli,tui,herdr
|
|
6
|
+
Author: Karel Blavka
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Operating System :: POSIX
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Software Development
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Project-URL: Homepage, https://github.com/blavka/agents-tree
|
|
15
|
+
Project-URL: Issues, https://github.com/blavka/agents-tree/issues
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# agents-tree
|
|
19
|
+
|
|
20
|
+
See what your coding agents are doing. `agents-tree` shows every running Claude
|
|
21
|
+
Code session and the subagents it spawned as a tree, with each agent's model and
|
|
22
|
+
effort, how full its context window is, how long it has been running, and its
|
|
23
|
+
status.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Split protocol modules [75d94683] /home/me/src/core background
|
|
27
|
+
main opus-5-5 (xhigh) 742k 74 % of 1M 42h00m working
|
|
28
|
+
├─ Explore: Study dskit modules and services opus-5-5 (high) 53k 5 % of 1M 2m46s completed
|
|
29
|
+
└─ qp-builder: Builder: query list parsing fix opus-5-5 (xhigh) 505k 50 % of 1M 20h16m running
|
|
30
|
+
|
|
31
|
+
Review flaky test [b443f8f0] /home/me/src/api interactive
|
|
32
|
+
main opus-5-5 (high) 228k 23 % of 1M 41m19s busy
|
|
33
|
+
└─ claude-code-guide: Look up hook events haiku-4-5 61k 31 % of 200k 2m34s completed
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
It reads the transcripts Claude Code keeps on disk and never talks to a session,
|
|
37
|
+
so nothing it prints ends up in an agent's context. It has no dependencies
|
|
38
|
+
beyond Python 3.10.
|
|
39
|
+
|
|
40
|
+
Claude Code is supported today; Codex and Grok are planned.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv tool install agents-tree # or: pipx install agents-tree
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
From a checkout: `uv tool install .`, or run it in place with
|
|
49
|
+
`PYTHONPATH=src python3 -m agents_tree`.
|
|
50
|
+
|
|
51
|
+
## Use
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
agents-tree # every running session on this machine
|
|
55
|
+
agents-tree . # sessions running in this directory (or its newest session)
|
|
56
|
+
agents-tree ~/src/api # same, for another directory
|
|
57
|
+
agents-tree b443f8f0 # one session, by id or a unique prefix (an ambiguous one lists the matches)
|
|
58
|
+
agents-tree -r # only running subagents and their parents
|
|
59
|
+
agents-tree -w # live view like top, refreshed every 2 s (-n 5 for 5 s)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
In the live view:
|
|
63
|
+
|
|
64
|
+
| Key | Does |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `↑` `↓` / `j` `k` / wheel | select an agent |
|
|
67
|
+
| `Enter` / `→` / click | open its detail: the prompt it was given, its tool calls (and the one it is waiting on), its last message, request and token counts, transcript path |
|
|
68
|
+
| `Esc` / `←` / `q` | back to the tree (in the tree: clear the selection, then quit) |
|
|
69
|
+
| `PgUp` `PgDn` `Home` `End` | scroll the detail, or jump through the tree |
|
|
70
|
+
| `r` | toggle running-only |
|
|
71
|
+
| `Ctrl+C` | quit |
|
|
72
|
+
|
|
73
|
+
The live view takes the mouse for clicks and the wheel; most terminals still
|
|
74
|
+
select text with Shift held, or start it with `--no-mouse`. When the tree is
|
|
75
|
+
taller than the terminal, the oldest finished agents are folded into a
|
|
76
|
+
"… N older finished agents hidden" line.
|
|
77
|
+
|
|
78
|
+
| Column | Meaning |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| label | `agent-type: description` as the spawning agent gave it; `main` for the session itself |
|
|
81
|
+
| model (effort) | model of the agent's latest request, and its effort level (models without effort show none) |
|
|
82
|
+
| context | tokens in the latest request (input + cache + output), and that as a share of the context window |
|
|
83
|
+
| elapsed | first to last transcript entry; up to now while the agent runs |
|
|
84
|
+
| status | subagents: `running` (active in the last 2 minutes, or waiting on a tool call), `completed` / `failed` (as Claude Code reported), `done` (finished, no outcome recorded), `idle` (a teammate waiting for its next message), `stale` (no sign of life and no completion marker); `main`: the session's own state, or `not running` |
|
|
85
|
+
|
|
86
|
+
### Caveats
|
|
87
|
+
|
|
88
|
+
- **Context window is inferred.** Claude Code does not record it, so it is taken
|
|
89
|
+
from the model family: 1M for Claude 5 models (Opus, Sonnet, Fable), 200k
|
|
90
|
+
otherwise. Override with `--window 200000` or `AGENTS_TREE_WINDOW`.
|
|
91
|
+
- **Transcript format is internal to Claude Code** and may change between
|
|
92
|
+
releases. Unknown lines are skipped, but a field that moves makes its column
|
|
93
|
+
show `-` until agents-tree is updated.
|
|
94
|
+
- **Elapsed** spans wall-clock time, so a session resumed over several days, or a
|
|
95
|
+
subagent woken up again by a message, shows that whole span.
|
|
96
|
+
- Running sessions come from `claude agents --json`; without `claude` on `PATH`,
|
|
97
|
+
agents-tree falls back to `~/.claude/sessions/` (interactive sessions only).
|
|
98
|
+
|
|
99
|
+
## herdr plugin
|
|
100
|
+
|
|
101
|
+
[herdr](https://herdr.dev) shows your agents, not their subagents. The plugin in
|
|
102
|
+
this repository opens the live tree for the focused agent pane over that pane;
|
|
103
|
+
closing it gives the layout back:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
herdr integration install claude # herdr learns each pane's session id
|
|
107
|
+
herdr plugin install blavka/agents-tree
|
|
108
|
+
herdr plugin action invoke agents-tree.setup-keys
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| Key | Action |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `prefix+a` | live tree of the focused agent's session; `q` or the same key closes it |
|
|
114
|
+
| `prefix+shift+a` | live tree of every running session |
|
|
115
|
+
|
|
116
|
+
On a pane without an agent, `prefix+a` shows every running session. The plugin
|
|
117
|
+
runs the copy of agents-tree bundled in the repository, so it needs only
|
|
118
|
+
`python3` 3.10+ (or `AGENTS_TREE_PYTHON` pointing at one). `setup-keys` writes
|
|
119
|
+
one marked block into your herdr `config.toml` (backed up first) and leaves keys
|
|
120
|
+
you already use alone; `agents-tree.remove-keys` takes it out again. The
|
|
121
|
+
`open-split` and `open-tab` actions show the same tree in a split or a tab.
|
|
122
|
+
|
|
123
|
+
For development, link a checkout instead: `herdr plugin link /path/to/agents-tree`.
|
|
124
|
+
|
|
125
|
+
## Development
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv sync
|
|
129
|
+
uv run pytest
|
|
130
|
+
uv run pylint src
|
|
131
|
+
uv run pylint tests --disable=redefined-outer-name,protected-access,unused-argument
|
|
132
|
+
uv run pyright
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
CI (`.github/workflows/test.yaml`) runs the same on every push and pull request:
|
|
136
|
+
lint and types on Python 3.12, then the tests, a test run and a package build on
|
|
137
|
+
3.10 to 3.14.
|
|
138
|
+
|
|
139
|
+
To release, bump `version` in `pyproject.toml`, `src/agents_tree/__init__.py` and
|
|
140
|
+
`herdr-plugin.toml`, then push a matching tag (`git tag v0.2.0 && git push origin
|
|
141
|
+
v0.2.0`). `.github/workflows/publish.yaml` checks the tag against the version,
|
|
142
|
+
tests, builds and publishes to PyPI through trusted publishing.
|
|
143
|
+
|
|
144
|
+
Providers live in `src/agents_tree/providers/`: each turns one agent CLI's files
|
|
145
|
+
into the provider-neutral `Session`/`Agent` model in `model.py`, which
|
|
146
|
+
`render.py` draws.
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# agents-tree
|
|
2
|
+
|
|
3
|
+
See what your coding agents are doing. `agents-tree` shows every running Claude
|
|
4
|
+
Code session and the subagents it spawned as a tree, with each agent's model and
|
|
5
|
+
effort, how full its context window is, how long it has been running, and its
|
|
6
|
+
status.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Split protocol modules [75d94683] /home/me/src/core background
|
|
10
|
+
main opus-5-5 (xhigh) 742k 74 % of 1M 42h00m working
|
|
11
|
+
├─ Explore: Study dskit modules and services opus-5-5 (high) 53k 5 % of 1M 2m46s completed
|
|
12
|
+
└─ qp-builder: Builder: query list parsing fix opus-5-5 (xhigh) 505k 50 % of 1M 20h16m running
|
|
13
|
+
|
|
14
|
+
Review flaky test [b443f8f0] /home/me/src/api interactive
|
|
15
|
+
main opus-5-5 (high) 228k 23 % of 1M 41m19s busy
|
|
16
|
+
└─ claude-code-guide: Look up hook events haiku-4-5 61k 31 % of 200k 2m34s completed
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
It reads the transcripts Claude Code keeps on disk and never talks to a session,
|
|
20
|
+
so nothing it prints ends up in an agent's context. It has no dependencies
|
|
21
|
+
beyond Python 3.10.
|
|
22
|
+
|
|
23
|
+
Claude Code is supported today; Codex and Grok are planned.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv tool install agents-tree # or: pipx install agents-tree
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
From a checkout: `uv tool install .`, or run it in place with
|
|
32
|
+
`PYTHONPATH=src python3 -m agents_tree`.
|
|
33
|
+
|
|
34
|
+
## Use
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
agents-tree # every running session on this machine
|
|
38
|
+
agents-tree . # sessions running in this directory (or its newest session)
|
|
39
|
+
agents-tree ~/src/api # same, for another directory
|
|
40
|
+
agents-tree b443f8f0 # one session, by id or a unique prefix (an ambiguous one lists the matches)
|
|
41
|
+
agents-tree -r # only running subagents and their parents
|
|
42
|
+
agents-tree -w # live view like top, refreshed every 2 s (-n 5 for 5 s)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
In the live view:
|
|
46
|
+
|
|
47
|
+
| Key | Does |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `↑` `↓` / `j` `k` / wheel | select an agent |
|
|
50
|
+
| `Enter` / `→` / click | open its detail: the prompt it was given, its tool calls (and the one it is waiting on), its last message, request and token counts, transcript path |
|
|
51
|
+
| `Esc` / `←` / `q` | back to the tree (in the tree: clear the selection, then quit) |
|
|
52
|
+
| `PgUp` `PgDn` `Home` `End` | scroll the detail, or jump through the tree |
|
|
53
|
+
| `r` | toggle running-only |
|
|
54
|
+
| `Ctrl+C` | quit |
|
|
55
|
+
|
|
56
|
+
The live view takes the mouse for clicks and the wheel; most terminals still
|
|
57
|
+
select text with Shift held, or start it with `--no-mouse`. When the tree is
|
|
58
|
+
taller than the terminal, the oldest finished agents are folded into a
|
|
59
|
+
"… N older finished agents hidden" line.
|
|
60
|
+
|
|
61
|
+
| Column | Meaning |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| label | `agent-type: description` as the spawning agent gave it; `main` for the session itself |
|
|
64
|
+
| model (effort) | model of the agent's latest request, and its effort level (models without effort show none) |
|
|
65
|
+
| context | tokens in the latest request (input + cache + output), and that as a share of the context window |
|
|
66
|
+
| elapsed | first to last transcript entry; up to now while the agent runs |
|
|
67
|
+
| status | subagents: `running` (active in the last 2 minutes, or waiting on a tool call), `completed` / `failed` (as Claude Code reported), `done` (finished, no outcome recorded), `idle` (a teammate waiting for its next message), `stale` (no sign of life and no completion marker); `main`: the session's own state, or `not running` |
|
|
68
|
+
|
|
69
|
+
### Caveats
|
|
70
|
+
|
|
71
|
+
- **Context window is inferred.** Claude Code does not record it, so it is taken
|
|
72
|
+
from the model family: 1M for Claude 5 models (Opus, Sonnet, Fable), 200k
|
|
73
|
+
otherwise. Override with `--window 200000` or `AGENTS_TREE_WINDOW`.
|
|
74
|
+
- **Transcript format is internal to Claude Code** and may change between
|
|
75
|
+
releases. Unknown lines are skipped, but a field that moves makes its column
|
|
76
|
+
show `-` until agents-tree is updated.
|
|
77
|
+
- **Elapsed** spans wall-clock time, so a session resumed over several days, or a
|
|
78
|
+
subagent woken up again by a message, shows that whole span.
|
|
79
|
+
- Running sessions come from `claude agents --json`; without `claude` on `PATH`,
|
|
80
|
+
agents-tree falls back to `~/.claude/sessions/` (interactive sessions only).
|
|
81
|
+
|
|
82
|
+
## herdr plugin
|
|
83
|
+
|
|
84
|
+
[herdr](https://herdr.dev) shows your agents, not their subagents. The plugin in
|
|
85
|
+
this repository opens the live tree for the focused agent pane over that pane;
|
|
86
|
+
closing it gives the layout back:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
herdr integration install claude # herdr learns each pane's session id
|
|
90
|
+
herdr plugin install blavka/agents-tree
|
|
91
|
+
herdr plugin action invoke agents-tree.setup-keys
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
| Key | Action |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `prefix+a` | live tree of the focused agent's session; `q` or the same key closes it |
|
|
97
|
+
| `prefix+shift+a` | live tree of every running session |
|
|
98
|
+
|
|
99
|
+
On a pane without an agent, `prefix+a` shows every running session. The plugin
|
|
100
|
+
runs the copy of agents-tree bundled in the repository, so it needs only
|
|
101
|
+
`python3` 3.10+ (or `AGENTS_TREE_PYTHON` pointing at one). `setup-keys` writes
|
|
102
|
+
one marked block into your herdr `config.toml` (backed up first) and leaves keys
|
|
103
|
+
you already use alone; `agents-tree.remove-keys` takes it out again. The
|
|
104
|
+
`open-split` and `open-tab` actions show the same tree in a split or a tab.
|
|
105
|
+
|
|
106
|
+
For development, link a checkout instead: `herdr plugin link /path/to/agents-tree`.
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
uv sync
|
|
112
|
+
uv run pytest
|
|
113
|
+
uv run pylint src
|
|
114
|
+
uv run pylint tests --disable=redefined-outer-name,protected-access,unused-argument
|
|
115
|
+
uv run pyright
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
CI (`.github/workflows/test.yaml`) runs the same on every push and pull request:
|
|
119
|
+
lint and types on Python 3.12, then the tests, a test run and a package build on
|
|
120
|
+
3.10 to 3.14.
|
|
121
|
+
|
|
122
|
+
To release, bump `version` in `pyproject.toml`, `src/agents_tree/__init__.py` and
|
|
123
|
+
`herdr-plugin.toml`, then push a matching tag (`git tag v0.2.0 && git push origin
|
|
124
|
+
v0.2.0`). `.github/workflows/publish.yaml` checks the tag against the version,
|
|
125
|
+
tests, builds and publishes to PyPI through trusted publishing.
|
|
126
|
+
|
|
127
|
+
Providers live in `src/agents_tree/providers/`: each turns one agent CLI's files
|
|
128
|
+
into the provider-neutral `Session`/`Agent` model in `model.py`, which
|
|
129
|
+
`render.py` draws.
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "agents-tree"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Show Claude Code sessions and their subagents as a live tree: model, effort, context use, elapsed time, status."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
keywords = [
|
|
9
|
+
"claude-code",
|
|
10
|
+
"subagents",
|
|
11
|
+
"agents",
|
|
12
|
+
"cli",
|
|
13
|
+
"tui",
|
|
14
|
+
"herdr",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Operating System :: POSIX",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Software Development",
|
|
21
|
+
]
|
|
22
|
+
requires-python = ">=3.10"
|
|
23
|
+
dependencies = []
|
|
24
|
+
|
|
25
|
+
[[project.authors]]
|
|
26
|
+
name = "Karel Blavka"
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/blavka/agents-tree"
|
|
30
|
+
Issues = "https://github.com/blavka/agents-tree/issues"
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
agents-tree = "agents_tree.cli:main"
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
37
|
+
build-backend = "uv_build"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"pylint>=4.1.2",
|
|
42
|
+
"pyright>=1.1.414",
|
|
43
|
+
"pytest>=9.1.1",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
48
|
+
pythonpath = ["tests"]
|
|
49
|
+
|
|
50
|
+
[tool.pyright]
|
|
51
|
+
venvPath = "."
|
|
52
|
+
venv = ".venv"
|
|
53
|
+
|
|
54
|
+
[tool.pylint.main]
|
|
55
|
+
py-version = "3.10"
|
|
56
|
+
|
|
57
|
+
[tool.pylint.format]
|
|
58
|
+
max-line-length = 100
|
|
59
|
+
|
|
60
|
+
[tool.pylint."messages control"]
|
|
61
|
+
disable = [
|
|
62
|
+
"missing-module-docstring",
|
|
63
|
+
"missing-class-docstring",
|
|
64
|
+
"missing-function-docstring",
|
|
65
|
+
"too-many-arguments",
|
|
66
|
+
"too-many-positional-arguments",
|
|
67
|
+
"too-many-instance-attributes",
|
|
68
|
+
"too-many-locals",
|
|
69
|
+
"too-many-branches",
|
|
70
|
+
"too-many-return-statements",
|
|
71
|
+
"too-few-public-methods",
|
|
72
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "agents-tree"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Show Claude Code sessions and their subagents as a live tree: model, effort, context use, elapsed time, status."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "Karel Blavka" }]
|
|
9
|
+
keywords = ["claude-code", "subagents", "agents", "cli", "tui", "herdr"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Environment :: Console",
|
|
12
|
+
"Operating System :: POSIX",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Topic :: Software Development",
|
|
15
|
+
]
|
|
16
|
+
requires-python = ">=3.10"
|
|
17
|
+
dependencies = []
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/blavka/agents-tree"
|
|
21
|
+
Issues = "https://github.com/blavka/agents-tree/issues"
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
agents-tree = "agents_tree.cli:main"
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
28
|
+
build-backend = "uv_build"
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"pylint>=4.1.2",
|
|
33
|
+
"pyright>=1.1.414",
|
|
34
|
+
"pytest>=9.1.1",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
39
|
+
pythonpath = ["tests"]
|
|
40
|
+
|
|
41
|
+
[tool.pyright]
|
|
42
|
+
venvPath = "."
|
|
43
|
+
venv = ".venv"
|
|
44
|
+
|
|
45
|
+
[tool.pylint.main]
|
|
46
|
+
py-version = "3.10"
|
|
47
|
+
|
|
48
|
+
[tool.pylint.format]
|
|
49
|
+
max-line-length = 100
|
|
50
|
+
|
|
51
|
+
[tool.pylint."messages control"]
|
|
52
|
+
# Docstrings where they say something, and design limits judged in review, not by count.
|
|
53
|
+
disable = [
|
|
54
|
+
"missing-module-docstring",
|
|
55
|
+
"missing-class-docstring",
|
|
56
|
+
"missing-function-docstring",
|
|
57
|
+
"too-many-arguments",
|
|
58
|
+
"too-many-positional-arguments",
|
|
59
|
+
"too-many-instance-attributes",
|
|
60
|
+
"too-many-locals",
|
|
61
|
+
"too-many-branches",
|
|
62
|
+
"too-many-return-statements",
|
|
63
|
+
"too-few-public-methods",
|
|
64
|
+
]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Command line: one-shot print, or a top-like live view with --watch."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import signal
|
|
8
|
+
import sys
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
|
|
11
|
+
from agents_tree import __version__, live
|
|
12
|
+
from agents_tree.model import Session, override_window, sanitize
|
|
13
|
+
from agents_tree.providers import PROVIDERS
|
|
14
|
+
from agents_tree.render import render, style
|
|
15
|
+
|
|
16
|
+
DESCRIPTION = "Show coding-agent sessions and their subagents as a tree: " \
|
|
17
|
+
"model (effort), context use, elapsed time and status."
|
|
18
|
+
EPILOG = """\
|
|
19
|
+
targets:
|
|
20
|
+
(none) every running session on this machine
|
|
21
|
+
DIR sessions running in DIR ("." works); if none, DIR's newest session
|
|
22
|
+
SESSION-ID one session (a unique prefix is enough)
|
|
23
|
+
|
|
24
|
+
live view keys: ↑/↓ or j/k select, enter or a click shows the agent's detail,
|
|
25
|
+
esc goes back, r toggles running-only, q quits
|
|
26
|
+
|
|
27
|
+
Reads local transcripts only. Run it in its own terminal (or the herdr plugin):
|
|
28
|
+
started with `!` inside an agent, its output lands in that agent's context.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _positive_int(value: str) -> int:
|
|
33
|
+
try:
|
|
34
|
+
n = int(value)
|
|
35
|
+
except ValueError:
|
|
36
|
+
raise argparse.ArgumentTypeError(f"not a whole number of tokens: {value!r}") from None
|
|
37
|
+
if n <= 0:
|
|
38
|
+
raise argparse.ArgumentTypeError("must be positive")
|
|
39
|
+
return n
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _env_window() -> int | None:
|
|
43
|
+
value = os.environ.get("AGENTS_TREE_WINDOW", "").strip()
|
|
44
|
+
if not value:
|
|
45
|
+
return None
|
|
46
|
+
try:
|
|
47
|
+
return _positive_int(value)
|
|
48
|
+
except argparse.ArgumentTypeError:
|
|
49
|
+
print(f"agents-tree: ignoring AGENTS_TREE_WINDOW={value!r}", file=sys.stderr)
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def parse_args(argv: list[str] | None) -> argparse.Namespace:
|
|
54
|
+
ap = argparse.ArgumentParser(prog="agents-tree", description=DESCRIPTION, epilog=EPILOG,
|
|
55
|
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
56
|
+
ap.add_argument("target", nargs="?", metavar="DIR|SESSION-ID")
|
|
57
|
+
ap.add_argument("-w", "--watch", action="store_true", help="live view, like top")
|
|
58
|
+
ap.add_argument("-n", "--interval", type=float, default=2.0, metavar="SECONDS",
|
|
59
|
+
help="live view refresh interval (default 2)")
|
|
60
|
+
ap.add_argument("-r", "--running", action="store_true",
|
|
61
|
+
help="only running subagents (and their parents)")
|
|
62
|
+
ap.add_argument("--window", type=_positive_int, metavar="TOKENS", default=_env_window(),
|
|
63
|
+
help="context window for the %% column (default: per model)")
|
|
64
|
+
ap.add_argument("--provider", choices=sorted(PROVIDERS), default="claude")
|
|
65
|
+
ap.add_argument("--color", choices=["auto", "always", "never"], default="auto")
|
|
66
|
+
ap.add_argument("--no-mouse", dest="mouse", action="store_false",
|
|
67
|
+
help="live view: leave the mouse to the terminal (text selection)")
|
|
68
|
+
ap.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}")
|
|
69
|
+
args = ap.parse_args(argv)
|
|
70
|
+
if args.interval <= 0:
|
|
71
|
+
ap.error("--interval must be positive")
|
|
72
|
+
return args
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def loader(args: argparse.Namespace) -> Callable[[], list[Session]]:
|
|
76
|
+
"""Sessions as the views get them: sanitised once, with --window applied."""
|
|
77
|
+
def load() -> list[Session]:
|
|
78
|
+
sessions = PROVIDERS[args.provider].sessions(args.target)
|
|
79
|
+
sanitize(sessions)
|
|
80
|
+
override_window(sessions, args.window)
|
|
81
|
+
return sessions
|
|
82
|
+
|
|
83
|
+
return load
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main(argv: list[str] | None = None) -> int:
|
|
87
|
+
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
88
|
+
args = parse_args(argv)
|
|
89
|
+
color = args.color == "always" or (args.color == "auto" and sys.stdout.isatty()
|
|
90
|
+
and not os.environ.get("NO_COLOR"))
|
|
91
|
+
load = loader(args)
|
|
92
|
+
if not args.watch:
|
|
93
|
+
try:
|
|
94
|
+
sessions = load()
|
|
95
|
+
except LookupError as e:
|
|
96
|
+
print(style(color, "90", str(e)))
|
|
97
|
+
return 1
|
|
98
|
+
print(render(sessions, running_only=args.running, color=color))
|
|
99
|
+
return 0
|
|
100
|
+
try:
|
|
101
|
+
live.run(load, interval=args.interval, color=color, running_only=args.running,
|
|
102
|
+
mouse=args.mouse)
|
|
103
|
+
except KeyboardInterrupt:
|
|
104
|
+
pass
|
|
105
|
+
return 0
|