@mkhuda/agent-ps 0.1.0

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ First public release.
6
+
7
+ ### Agents
8
+
9
+ Seven, in one table: Claude Code, Pi, CommandCode, Codex CLI, OpenCode, Hermes
10
+ and GitHub Copilot. Each keeps its own colour, and the legend above the keys is
11
+ the key to the palette.
12
+
13
+ Only Claude Code records which process runs which session. Everywhere else the
14
+ two are matched on working directory and the PID is marked with a `?`, since two
15
+ sessions of one agent in one folder cannot be told apart.
16
+
17
+ Copilot has no process at all. It runs inside the VS Code extension host, so its
18
+ rows carry no PID, uptime, CPU or memory, and it reports the credits each turn
19
+ spent instead.
20
+
21
+ ### The table
22
+
23
+ Columns for agent, session, status, model, uptime, time since the last turn, cpu,
24
+ memory, disk and working directory.
25
+
26
+ `s` cycles the sort through eight columns and marks the heading it is sorting by;
27
+ `S` reverses it. `e` includes ended sessions. `/` filters. Enter opens a detail
28
+ panel for a live session, or reopens an ended one in a new terminal tab.
29
+
30
+ `k` stops a process tree after a confirmation that names the working directory,
31
+ and says when the pairing was inferred. `b` stops every background helper.
32
+
33
+ ### Disk
34
+
35
+ The DISK column counts everything a session left behind, which is often more
36
+ than its log: subagent transcripts, file history, shell snapshots, request dumps.
37
+ A Codex shell snapshot is routinely larger than the transcript it belongs to.
38
+
39
+ ### Build
40
+
41
+ A single zipapp with no dependencies, built by the standard library. The build is
42
+ reproducible, so the committed executable can be checked against the source, and
43
+ its SHA-256 is published in the README.
44
+
45
+ Tested on Python 3.8, 3.9, 3.10 and 3.14.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mkhuda
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.
package/README.md ADDED
@@ -0,0 +1,327 @@
1
+ # agent-ps
2
+
3
+ A process table for coding agent sessions, the way `ps` would look if it knew
4
+ what a session was.
5
+
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
7
+ ![Python 3.8+](https://img.shields.io/badge/python-3.8%2B-blue?style=flat-square)
8
+ ![macOS and Linux](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey?style=flat-square)
9
+ ![No dependencies](https://img.shields.io/badge/dependencies-none-brightgreen?style=flat-square)
10
+ ![7 agents](https://img.shields.io/badge/agents-7-orange?style=flat-square)
11
+
12
+ ![agent-ps listing sessions from claude, hermes, codex, opencode, pi, commandcode and copilot side by side, each with its model, uptime, idle time, cpu, memory, disk and working directory, with two hermes background helpers at the bottom](images/agent-ps-screenshot.jpg)
13
+
14
+ `ps aux | grep claude` gives you PIDs. It will not tell you which one is the
15
+ session you are talking to, what model it is spending on, whether it has been
16
+ idle for three days, or which rows are background daemons that outlived the
17
+ terminal that started them. It also misses the other six agents entirely.
18
+
19
+ agent-ps lists every session with its agent, model, working directory, uptime,
20
+ idle time and disk footprint, then stops one process tree, every background
21
+ helper, or the lot.
22
+
23
+ One file, no dependencies, reads local files only.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ curl -fsSL https://raw.githubusercontent.com/mkhuda/agent-ps/main/install.sh | sh
29
+ ```
30
+
31
+ That takes the latest release, checks it against the checksum published with it,
32
+ makes sure it runs, and drops the single file into `~/.local/bin`. Set `BIN_DIR`
33
+ to put it elsewhere, or `AGENT_PS_REF=v0.1.0` to pin a version.
34
+
35
+ If you would rather not pipe a script into a shell, the executable is the whole
36
+ program and you can fetch it yourself:
37
+
38
+ ```bash
39
+ mkdir -p ~/.local/bin
40
+ curl -fsSL -o ~/.local/bin/agent-ps \
41
+ https://github.com/mkhuda/agent-ps/releases/latest/download/agent-ps
42
+ chmod +x ~/.local/bin/agent-ps
43
+ ```
44
+
45
+ Every [release](https://github.com/mkhuda/agent-ps/releases) publishes a
46
+ `SHA256SUMS` beside the executable.
47
+
48
+ If you install your coding agents with npm, `npx agent-ps` works too. It is the
49
+ same Python program either way; the package only finds an interpreter.
50
+
51
+ From a clone, `./install.sh` builds from the tree instead of downloading, so you
52
+ install what you are looking at.
53
+
54
+ Requires Python 3.8 or later and nothing from PyPI. Tested on 3.8, 3.9, 3.10 and
55
+ 3.14. macOS and Linux; Windows is out because `curses` is not in its standard
56
+ library. It shells out to `ps`, and on macOS to `lsof`, which is how a process is
57
+ matched to a session everywhere except Claude Code.
58
+
59
+ To remove it:
60
+
61
+ ```bash
62
+ rm -f ~/.local/bin/agent-ps
63
+ ```
64
+
65
+ Nothing is written outside that directory. agent-ps only ever reads the agents'
66
+ own files.
67
+
68
+ ## Agents
69
+
70
+ Each agent keeps its own colour in the table, and the same colours label the
71
+ legend above the keys.
72
+
73
+ | Agent | | Sessions in | Paired by | Tokens | Reopened with |
74
+ |---|---|---|---|---|---|
75
+ | [Claude Code](https://claude.com/claude-code) | ![yellow](https://img.shields.io/badge/-d4a72c?style=flat-square) | JSONL | the agent itself | no | `claude --resume` |
76
+ | [Pi](https://pi.dev) | ![cyan](https://img.shields.io/badge/-00a3a3?style=flat-square) | JSONL | directory | no | `pi --session` |
77
+ | CommandCode | ![magenta](https://img.shields.io/badge/-b83fb8?style=flat-square) | JSONL | directory | no | `cmd --resume` |
78
+ | [Codex CLI](https://github.com/openai/codex) | ![green](https://img.shields.io/badge/-3f9e3f?style=flat-square) | JSONL | directory | no | `codex resume` |
79
+ | [OpenCode](https://github.com/sst/opencode) | ![blue](https://img.shields.io/badge/-3b6fd4?style=flat-square) | SQLite | directory | yes | `opencode --session` |
80
+ | Hermes | ![red](https://img.shields.io/badge/-c0392b?style=flat-square) | SQLite | directory | yes | `hermes --resume` |
81
+ | GitHub Copilot | ![white](https://img.shields.io/badge/-cfd3d8?style=flat-square) | VS Code storage | no process | yes | in the editor |
82
+
83
+ Only Claude Code records which process is running which session, so every other
84
+ pairing is matched on working directory and shown as a guess. OpenCode, Hermes
85
+ and Copilot count tokens and cost, which the detail panel shows. Agents you have
86
+ not installed are skipped, not reported as missing.
87
+
88
+ Copilot is the exception to everything. It runs inside the VS Code extension
89
+ host, so it has no process of its own: no PID, no uptime, no CPU or memory, and
90
+ nothing to stop. What it does have is the credits each turn spent, which the free
91
+ tier meters and nothing else surfaces. A chat counts as open while its workspace
92
+ is open in the editor.
93
+
94
+ ## Usage
95
+
96
+ Run without arguments for the live table:
97
+
98
+ ```bash
99
+ agent-ps
100
+ ```
101
+
102
+ It refreshes every two seconds and stays out of the way until you act on
103
+ something.
104
+
105
+ | Key | Action |
106
+ |---|---|
107
+ | up, down | move the selection |
108
+ | `j`, `K` | down and up, since `k` is taken by stop |
109
+ | home, end | jump to the first or last row |
110
+ | enter | details for a live session, or reopen an ended one |
111
+ | `s` | cycle the sort column |
112
+ | `S` | reverse the direction |
113
+ | `k` | stop the selected process and its children, after confirming |
114
+ | `b` | stop every background helper, after confirming |
115
+ | `y`, `n` | answer a confirmation |
116
+ | `e` | show or hide ended sessions |
117
+ | `/` | filter by session, title, agent, model, directory, or PID |
118
+ | esc | leave filter mode, or close the detail panel |
119
+ | space | pause refreshing |
120
+ | `r` | refresh now |
121
+ | `q` | quit |
122
+
123
+ Those keys live at the bottom of the screen, under a line naming every agent on
124
+ screen in its own colour and, on the right, what the table is sorted by:
125
+
126
+ ![The bottom of the screen: a legend naming each agent in its own colour, the sort shown in words on the right, and the key bar beneath it](images/agent-ps-footer-screenshot.jpg)
127
+
128
+ That middle line does three jobs. It is the key to the colours in the AGENT
129
+ column, it says the sort order in words so the marked heading is never a guess,
130
+ and it is where a note appears for a few seconds after you act on something.
131
+
132
+ Backspacing a filter down to nothing leaves filter mode, so the key bar comes
133
+ back without reaching for escape.
134
+
135
+ ### Sorting
136
+
137
+ `s` cycles the sort column through agent, active, disk, cpu, mem, uptime,
138
+ session and title. The sorted heading is marked in place, and the line above the
139
+ keys names it in words:
140
+
141
+ ```
142
+ PID AGENT SESSION STATUS MODEL UPTIME ACTIVE CPU MEM DISKv
143
+ 31771 claude benchmark idle sonnet-5 54m 54m ago 0.2% 51M 127M
144
+ - claude web-app ended sonnet-5 - 16d8h ago - - 102M
145
+ - claude notes ended opus-5 - 2h22m ago - - 84M
146
+
147
+ agents claude codex commandcode copilot hermes opencode pi sorted by disk, high to low
148
+ up/down select enter details k stop b background e hide ended s sort S reverse / filter q quit
149
+ ```
150
+
151
+ `v` means descending and `^` ascending; `S` flips it. Numbers start at the
152
+ largest, names start at A, and rows with nothing in that column go to the end
153
+ either way.
154
+
155
+ Sorting covers running and ended sessions together, since "which session is the
156
+ biggest" does not care whether its process is still alive. Press `e` to include
157
+ ended sessions, then `s` until DISK is marked.
158
+
159
+ ### The detail panel
160
+
161
+ Enter on a live session opens everything known about it, including tokens and
162
+ cost where the agent counts them:
163
+
164
+ ```
165
+ session details
166
+
167
+ agent hermes
168
+ session 20260905_005254_2c9f4d
169
+ status idle
170
+ model nemotron-3.5-lightning-free
171
+ directory /Users/rg/projects/agent-ps
172
+ title halo are you hermes?
173
+ pid 21098 (parent 17518)
174
+ uptime 1h17m last turn 1h15m ago
175
+ usage cpu 0.0% memory 11M disk 121K
176
+ paired matched by working directory, not reported
177
+ provider opencode-free
178
+ tokens in 24,608 out 242 reasoning 190
179
+ calls 2 api, 6 messages, 0 tool calls
180
+ cost $0.0000 estimated
181
+ command /usr/bin/python3 /Users/rg/.hermes/hermes-agent/hermes
182
+ ```
183
+
184
+ The `paired` line appears only when the pairing was inferred. The provider,
185
+ tokens, calls and cost lines appear only for agents that record them, and are
186
+ simply absent for the rest.
187
+
188
+ Enter on an ended session reopens it in a new terminal tab instead. Copilot
189
+ chats live in the editor, so they have no reopen command and say so.
190
+
191
+ ### The advisory line
192
+
193
+ A line above the keys points out whatever is worth a look: background helpers
194
+ left running, sessions untouched for a day, or ended sessions you could resume.
195
+ It names the key that acts on it, and only ever shows one thing, since a wall of
196
+ warnings teaches people to ignore the line.
197
+
198
+ ## Scripting
199
+
200
+ ```bash
201
+ agent-ps list # print the table and exit
202
+ agent-ps list --all # include ended sessions
203
+ agent-ps list --json # machine readable, with idle seconds
204
+ agent-ps list --limit 100 # how many ended sessions (default 40)
205
+ agent-ps list --filter benchmark # same match as the / key
206
+ agent-ps agents # which agents were found, and where
207
+ agent-ps --agent codex list # one agent, or a comma separated list
208
+ agent-ps stop 32244 # stop one process tree
209
+ agent-ps stop 32244 --dry-run # show what would be stopped
210
+ agent-ps stop-background # stop daemons, warm spares, and servers
211
+ agent-ps stop-background --dry-run
212
+ agent-ps resume <session> # a unique id prefix is enough
213
+ agent-ps resume <session> --print # print the command instead of running it
214
+ agent-ps --version
215
+ ```
216
+
217
+ Piping works without a subcommand: with stdout not a terminal, agent-ps prints
218
+ the table and exits.
219
+
220
+ ## What each column means
221
+
222
+ | Column | Meaning |
223
+ |---|---|
224
+ | PID | the process, or a dash where there is none |
225
+ | AGENT | which agent, and what sort of process when it is not a plain session |
226
+ | SESSION | the directory the session was started in |
227
+ | STATUS | `busy`, `idle`, `ended`, or a dash when nothing says |
228
+ | MODEL | what answered the last turn, or the launcher's routing alias |
229
+ | UPTIME | how long the process has been alive, from one `ps` call |
230
+ | ACTIVE | how long since the session last wrote a turn |
231
+ | CPU, MEM | the same `ps` call |
232
+ | DISK | everything that session left on disk |
233
+ | DIR | the working directory |
234
+ | TITLE | the session title, or its opening prompt where the agent keeps none |
235
+
236
+ UPTIME and ACTIVE often disagree, and that is the point. A process can be five
237
+ days old and have answered a minute ago. UPTIME comes from the process table,
238
+ ACTIVE from the modification time of the log, which is appended on every turn,
239
+ so an ended session shows a dash under UPTIME and only ACTIVE says how stale it
240
+ is.
241
+
242
+ Each agent gets its own colour in the AGENT column, assigned in registry order,
243
+ and the same colours appear in the legend, which makes that line the key to the
244
+ palette. A selected row keeps its own highlight rather than being broken up, so
245
+ the cursor stays unmistakable. Terminals without colour fall back to plain text.
246
+
247
+ ## A PID marked with a question mark
248
+
249
+ Only Claude Code records which process is running which session, in
250
+ `<config dir>/sessions/<pid>.json`. For every other agent the two have to be
251
+ matched on working directory, and that cannot tell apart two sessions of the same
252
+ agent started in the same folder.
253
+
254
+ So an inferred pairing is shown as one:
255
+
256
+ ```
257
+ 32244 claude form-guardian idle opus-5 ...
258
+ 9666? codex agent-ps idle gpt-5.6-luna ...
259
+ ```
260
+
261
+ `9666?` means the process is certain and the session beside it is a guess. The
262
+ guess is the most recently active session in that directory, and where several
263
+ processes of one agent share a directory they are paired newest with newest, so
264
+ the process that started four minutes ago gets the session that has been active
265
+ for four minutes rather than the one from an hour ago.
266
+
267
+ It is still a guess, so the stop confirmation names the directory and says when
268
+ the pairing was inferred:
269
+
270
+ ```
271
+ Stop 1 process(es) in ~/projects/agent-ps, session matched by directory?
272
+ ```
273
+
274
+ Where no session matches at all, the row still appears with whatever the process
275
+ itself can answer. Nothing is invented to fill the gap.
276
+
277
+ ## How it works
278
+
279
+ The parts that are not obvious have their own page:
280
+ [where each agent keeps its sessions](docs/internals.md), how busy and idle are
281
+ decided per agent, which processes count as background helpers, how a shim is
282
+ told apart from a real session, and what the DISK column is adding up.
283
+
284
+ ## Stopping behaviour
285
+
286
+ Processes are stopped depth first, children before parents, so a supervisor does
287
+ not restart a worker you just killed. Each process gets `SIGTERM`, one second to
288
+ exit, then `SIGKILL` if it is still there.
289
+
290
+ agent-ps never lists or stops itself, or the shell that launched it, so running
291
+ it from inside an agent session is safe.
292
+
293
+ Exit status is zero when everything asked for was stopped, and one when something
294
+ survived, which makes it usable in scripts.
295
+
296
+ ## When something is missing
297
+
298
+ | Symptom | Cause |
299
+ |---|---|
300
+ | An agent you use is not listed | agent-ps only looks in the roots above. `agent-ps agents` prints what it found. |
301
+ | SESSION and MODEL are dashes on macOS | pairing needs `lsof` to read a process working directory. Linux reads `/proc`. |
302
+ | A row paired to the wrong session | see the question mark section above |
303
+ | No colour | the terminal reported none, so every row falls back to plain text |
304
+ | A Copilot chat has no PID | it never had one. Copilot runs inside VS Code. |
305
+
306
+ ## Build
307
+
308
+ The executable is a zipapp, built from the package by the standard library:
309
+
310
+ ```bash
311
+ ./build.sh
312
+ ```
313
+
314
+ That writes a single `agent-ps` file with no dependencies, which is what the curl
315
+ install fetches. The build is reproducible: the same source always produces the
316
+ same bytes, so the committed executable can be checked against the tree.
317
+
318
+ ```
319
+ 3085afb9b1adaa486ab468066706bb6c5625d06afb18e46fcf97a1e4b8b02c62 agent-ps
320
+ ```
321
+
322
+ Adding an agent takes one class and one line in the registry. See
323
+ [CONTRIBUTING.md](CONTRIBUTING.md).
324
+
325
+ ## License
326
+
327
+ MIT. See [LICENSE](LICENSE).
package/agent-ps ADDED
Binary file
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ // Runs the zipapp that sits beside this file.
3
+ //
4
+ // The package exists so that people who install their coding agents with npm
5
+ // can install this the same way. It is still a Python program: this only finds
6
+ // an interpreter and gets out of the way.
7
+ "use strict";
8
+
9
+ var spawnSync = require("child_process").spawnSync;
10
+ var path = require("path");
11
+
12
+ var app = path.join(__dirname, "..", "agent-ps");
13
+ var args = process.argv.slice(2);
14
+ var tried = [];
15
+
16
+ for (var i = 0, names = ["python3", "python"]; i < names.length; i++) {
17
+ // stdio is inherited rather than piped, because the interface is a curses
18
+ // one and curses needs a real terminal on the other end.
19
+ var run = spawnSync(names[i], [app].concat(args), { stdio: "inherit" });
20
+ if (run.error && run.error.code === "ENOENT") {
21
+ tried.push(names[i]);
22
+ continue;
23
+ }
24
+ if (run.error) {
25
+ process.stderr.write("agent-ps: " + run.error.message + "\n");
26
+ process.exit(1);
27
+ }
28
+ if (run.signal) {
29
+ process.exit(1);
30
+ }
31
+ process.exit(run.status === null ? 1 : run.status);
32
+ }
33
+
34
+ process.stderr.write(
35
+ "agent-ps needs Python 3.8 or later, and none was found on PATH.\n" +
36
+ "Looked for: " + tried.join(", ") + "\n"
37
+ );
38
+ process.exit(1);
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@mkhuda/agent-ps",
3
+ "version": "0.1.0",
4
+ "description": "A process table for coding agent sessions. Claude Code, Codex, OpenCode, Hermes, Pi, CommandCode and Copilot in one view, with models, idle time, disk use and one key to stop them.",
5
+ "keywords": [
6
+ "cli",
7
+ "tui",
8
+ "process-monitor",
9
+ "developer-tools",
10
+ "claude-code",
11
+ "codex",
12
+ "opencode",
13
+ "copilot",
14
+ "ai-agents"
15
+ ],
16
+ "homepage": "https://github.com/mkhuda/agent-ps#readme",
17
+ "bugs": "https://github.com/mkhuda/agent-ps/issues",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/mkhuda/agent-ps.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": "mkhuda",
24
+ "bin": {
25
+ "agent-ps": "npm/agent-ps.js"
26
+ },
27
+ "files": [
28
+ "npm/agent-ps.js",
29
+ "agent-ps",
30
+ "CHANGELOG.md"
31
+ ],
32
+ "engines": {
33
+ "node": ">=12"
34
+ },
35
+ "os": [
36
+ "darwin",
37
+ "linux"
38
+ ],
39
+ "scripts": {
40
+ "test": "node npm/agent-ps.js --version"
41
+ }
42
+ }