sessiontools 0.2.0__tar.gz → 0.3.1__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 (91) hide show
  1. sessiontools-0.3.1/PKG-INFO +330 -0
  2. sessiontools-0.3.1/README.md +314 -0
  3. {sessiontools-0.2.0 → sessiontools-0.3.1}/pyproject.toml +1 -1
  4. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/__init__.py +2 -2
  5. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/cli.py +4 -1
  6. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/status.py +36 -7
  7. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/tokenfile.py +22 -2
  8. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/use.py +25 -8
  9. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/busy.py +268 -23
  10. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/claude_code.py +1 -1
  11. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/cli.py +77 -8
  12. sessiontools-0.3.1/src/sessiontools/clockline.py +180 -0
  13. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/hooks.py +1 -1
  14. sessiontools-0.3.1/src/sessiontools/default_model.py +480 -0
  15. sessiontools-0.3.1/src/sessiontools/default_model_cli.py +280 -0
  16. sessiontools-0.3.1/src/sessiontools/default_model_watch.py +314 -0
  17. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/get.py +1 -1
  18. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/listing.py +1 -1
  19. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/operator.py +78 -11
  20. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/park.py +1 -1
  21. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/procs.py +61 -4
  22. sessiontools-0.3.1/src/sessiontools/session_info.py +174 -0
  23. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/settings.py +33 -13
  24. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/settle.py +82 -0
  25. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/sound/service.py +1 -1
  26. sessiontools-0.3.1/src/sessiontools/start/__init__.py +557 -0
  27. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/agentfile.py +7 -2
  28. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/agents.py +58 -11
  29. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/args.py +26 -13
  30. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/context.py +43 -12
  31. sessiontools-0.3.1/src/sessiontools/start/default_seat.md +3 -0
  32. sessiontools-0.3.1/src/sessiontools/start/default_seat.py +534 -0
  33. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/environment.py +33 -1
  34. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/errors.py +3 -0
  35. sessiontools-0.3.1/src/sessiontools/start/holders.py +418 -0
  36. sessiontools-0.3.1/src/sessiontools/start/homefolder.py +220 -0
  37. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/identity.py +4 -2
  38. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/placement.py +29 -4
  39. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/plan.py +100 -24
  40. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/policy.py +10 -7
  41. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/resume.py +6 -4
  42. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/secrets.py +2 -2
  43. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/vendor.py +15 -3
  44. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/tmux.py +2 -2
  45. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/window.py +11 -3
  46. sessiontools-0.2.0/PKG-INFO +0 -166
  47. sessiontools-0.2.0/README.md +0 -150
  48. sessiontools-0.2.0/src/sessiontools/start/__init__.py +0 -151
  49. sessiontools-0.2.0/src/sessiontools/start/holders.py +0 -193
  50. {sessiontools-0.2.0 → sessiontools-0.3.1}/LICENSE +0 -0
  51. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/__init__.py +0 -0
  52. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/keychain.py +0 -0
  53. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/leakscan.py +0 -0
  54. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/limits.py +0 -0
  55. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/movehome.py +0 -0
  56. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/new.py +0 -0
  57. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/registry.py +0 -0
  58. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/setuptoken.py +0 -0
  59. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/account/tokenstore.py +0 -0
  60. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/band.py +0 -0
  61. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/completion.py +0 -0
  62. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/__init__.py +0 -0
  63. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/boot.py +0 -0
  64. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/breakdown.py +0 -0
  65. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/nnls.py +0 -0
  66. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/render.py +0 -0
  67. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/slash_context.py +0 -0
  68. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/tools.py +0 -0
  69. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/context_parts/transcript.py +0 -0
  70. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/ledger/__init__.py +0 -0
  71. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/ledger/_tiktoken_child.py +0 -0
  72. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/ledger/count.py +0 -0
  73. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/ledger/render.py +0 -0
  74. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/ledger/transcript.py +0 -0
  75. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/pane_state.py +0 -0
  76. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/pane_state_agent.py +0 -0
  77. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/py.typed +0 -0
  78. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/reap.py +0 -0
  79. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/send.py +0 -0
  80. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/snapshot.py +0 -0
  81. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/sound/__init__.py +0 -0
  82. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/sound/engine.swift +0 -0
  83. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/sound/event.swift +0 -0
  84. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/claude_settings.py +0 -0
  85. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/record.py +0 -0
  86. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/start/run.py +0 -0
  87. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/statusline/__init__.py +0 -0
  88. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/statusline/sample.json +0 -0
  89. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/statusline/statusline.sh +0 -0
  90. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/turn.py +0 -0
  91. {sessiontools-0.2.0 → sessiontools-0.3.1}/src/sessiontools/verbs.py +0 -0
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.4
2
+ Name: sessiontools
3
+ Version: 0.3.1
4
+ Summary: The harness layer: sessions, status and idle, one driver per harness - a library first
5
+ Author: Mike Bailey
6
+ Author-email: Mike Bailey <mike@sessionkit.ai>
7
+ License-Expression: AGPL-3.0-or-later
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Requires-Dist: pyyaml>=6
11
+ Requires-Dist: jsonschema>=4
12
+ Requires-Python: >=3.11, <3.15
13
+ Project-URL: Homepage, https://github.com/sessionkit/sessiontools
14
+ Project-URL: Repository, https://github.com/sessionkit/sessiontools
15
+ Description-Content-Type: text/markdown
16
+
17
+ # sessiontools
18
+
19
+ `session`: one command for the coding-agent sessions on this machine.
20
+ Which are live, whether each is busy or idle, how full its context is,
21
+ and how to start one from an agent file. For anyone running several
22
+ Claude Code sessions side by side, and for the tools that watch them.
23
+
24
+ ## Install
25
+
26
+ This puts `session` on the PATH:
27
+
28
+ ```console
29
+ uv tool install sessiontools
30
+ ```
31
+
32
+ Python 3.11 to 3.14. Optional: [the Claude Code plugin][plugin] adds
33
+ the `session` skill and four hooks.
34
+
35
+ Shell completion, one line per shell ([shell completion][completion]).
36
+ For zsh, in `~/.zshrc` after compinit:
37
+
38
+ ```console
39
+ eval "$(session completion zsh)"
40
+ ```
41
+
42
+ For bash with the bash-completion package (2.x), once:
43
+
44
+ ```console
45
+ session completion bash > ~/.local/share/bash-completion/completions/session
46
+ ```
47
+
48
+ For bash 3.2, or any bash without that package, in `~/.bashrc`:
49
+
50
+ ```console
51
+ eval "$(session completion bash)"
52
+ ```
53
+
54
+ ## Quick start
55
+
56
+ - `session list`: the live sessions, with their panes, state and context.
57
+ - `session get state`: this session's state: busy, idle with children,
58
+ or idle.
59
+ - `session context`: how full this session's context window is.
60
+ - `session context --parts`: what filled it: boot, then growth by
61
+ category.
62
+ - `session ledger --short`: each file read and command run, by tokens.
63
+ - `session account status`: which Claude account this Mac is on.
64
+ - `session start --list-agents`: the agents it can start.
65
+ - `session reap`: seats idle past 1h, each CLOSE or KEEP, and why. A dry
66
+ run.
67
+ - `session default-model`: the model every new `claude` starts on, and
68
+ whether a pin keeps it there. Off until you set `model.default`.
69
+ - `session --help`: every verb.
70
+
71
+ ## Requirements
72
+
73
+ - [x] macOS (tested on macOS 27, Apple silicon)
74
+ - [ ] Linux
75
+ - [ ] WSL
76
+ - [ ] Windows
77
+
78
+ What sessiontools depends on, what needs each one, and how to upgrade
79
+ it: [dependencies][dependencies].
80
+
81
+ ## Switching accounts
82
+
83
+ `session account use NAME` moves this Mac to another Claude account from
84
+ a token you already hold: no browser, no restart, and it never prints a
85
+ token. On a Keychain host it moves **every** seat, not one:
86
+
87
+ | route | moves | cache cost | reversible by | gate |
88
+ |---|---|---|---|---|
89
+ | `account use` (Keychain) | every seat on the Mac with no env token, within ~50 s | the sum of their contexts (2.07M / 1.10M measured) | `use` back, or restore from the backup | witness, `--yes`, preflight, backup |
90
+ | `account use` (token file) | no live seat; each one that relaunches from a shell that reads the file | none now; each seat's context when it restarts | `use` back, or restore from the backup | witness, `--yes`, preflight, backup |
91
+
92
+ A host whose shell exports `CLAUDE_CODE_OAUTH_TOKEN` from a file names it
93
+ in the `session` setting `account.token_file`, and `use` swaps the file.
94
+ A seat `session start` launches does not follow the file unless its agent
95
+ file says `inherit: [CLAUDE_CODE_OAUTH_TOKEN]`.
96
+ Without `--yes`, `use` shows the plan and writes nothing. To add an
97
+ account, `session account new NAME --email E` signs in through `claude
98
+ setup-token`, saves the token to passage, reads it back and registers
99
+ it; the token is never shown. See [Switching Claude accounts][account].
100
+
101
+ If you sign in by token, `CLAUDE_CODE_OAUTH_TOKEN` in your shell, a
102
+ session `session start` launches does not get it by default. To carry
103
+ it, name it under `inherit:` in the agent file ([credentials are never
104
+ handed down][credentials]). A `/login` sign-in needs nothing:
105
+
106
+ ```yaml
107
+ environment:
108
+ inherit:
109
+ - CLAUDE_CODE_OAUTH_TOKEN
110
+ ```
111
+
112
+ ## What an install changes
113
+
114
+ `uv tool install sessiontools` changes two places and fills uv's own
115
+ cache, and it touches no shell startup file and nothing in `~/.claude`.
116
+ Everything else below appears only when you run the verb named beside
117
+ it. Each line says what puts it there and what takes it away.
118
+
119
+ - **PATH:** `~/.local/bin/session`, the one command, or in
120
+ `UV_TOOL_BIN_DIR` when that is set. uv says so if the folder is not
121
+ on your PATH, and edits no shell file. `uv tool uninstall` removes it.
122
+ - **The program:** `~/.local/share/uv/tools/sessiontools/`, its own
123
+ Python environment with PyYAML and jsonschema, or in `UV_TOOL_DIR`.
124
+ `uv tool uninstall` removes it.
125
+ - **uv's cache:** the install fills it with what it downloaded and
126
+ built. It is uv's, shared by everything uv installs, not sessiontools'.
127
+ `uv cache dir` prints where it is (`~/.cache/uv` by default, or
128
+ `UV_CACHE_DIR`). `uv tool uninstall` leaves it; `uv cache clean
129
+ sessiontools` drops this package's entries, and `uv cache prune` drops
130
+ the ones nothing uses.
131
+ - **Folders, each made by a verb you run:**
132
+ - `~/.session/state/list/`: the list cache, `snapshot.json` and
133
+ `list.lock`, at the first `session list`.
134
+ - `~/.session/sessions/<session-id>/`: one for each `session start`,
135
+ holding `launch/`, `log/settings.jsonl` and `session-id`. It also
136
+ gets `settings/<tool>.d/50-env.json`, one for each tool whose
137
+ settings name a variable that is set when the session starts. Each
138
+ is written once, so a resumed session keeps its file, and
139
+ `log/settings.jsonl` names each variable it read, never its value.
140
+ The plugin's hooks and `session park` write only inside a folder
141
+ that is already there.
142
+ - `~/.session/state/reap.log`: `session reap --yes`.
143
+ - `~/.session/state/pane-state/watch.lock`: the pane signal's service.
144
+ - `~/.session/state/default-model/`: `backups/` and `log.jsonl`, the
145
+ first time `session default-model enforce` changes settings.json.
146
+ - `~/.sessionkit/tools/accounts/`: `session account new` and `use`.
147
+ - `~/.local/share/sessiontools/sound/bin/`: `session sound engine on`
148
+ builds the sound engine there with `swiftc`.
149
+ - `~/Library/Logs/sessiontools/`: the two services' logs.
150
+ - `SESSIONS_DIR`, `SESSION_STATE_DIR` and `SESSIONTOOLS_SOUND_HOME`
151
+ move these folders when set.
152
+ - **Services (launchd agents):** none until you turn one on. Each `on`
153
+ writes a plist in `~/Library/LaunchAgents/` and loads it, and it
154
+ starts again at every login. Each `off` unloads it and deletes the
155
+ plist. Both `on` and `off` also retire a job an older release set up
156
+ under its old label: they unload it and delete its plist. `status`
157
+ names one that is still there, and `--dry-run` lists the retire and
158
+ changes nothing.
159
+ - `dev.sessionkit.tools.sound-engine`: `session sound engine on`.
160
+ `off --purge` also deletes the engine it built.
161
+ - `dev.sessionkit.tools.pane-state`: `session pane-state agent on`.
162
+ It sets `@session_state` and `@session_state_why` on each seat's
163
+ tmux pane.
164
+ - `dev.sessionkit.tools.default-model`: no verb writes or loads it.
165
+ `session default-model unit launchd` prints it, and it is there only
166
+ if you save and load it yourself
167
+ ([the default model][default-model]).
168
+ - **Plugin and hooks, optional:** `claude plugin marketplace add` and
169
+ `claude plugin install` ([the Claude Code plugin][plugin]). Claude
170
+ Code names the marketplace and the plugin in `~/.claude/settings.json`
171
+ (`extraKnownMarketplaces`, `enabledPlugins`), lists them in
172
+ `~/.claude/plugins/`, and keeps a copy in
173
+ `~/.claude/plugins/cache/sessiontools/`. The plugin brings the
174
+ `session` skill and nine hook entries: SessionStart (two),
175
+ PreToolUse, PostToolBatch (three), Stop, UserPromptSubmit and
176
+ SessionEnd. Each is silent unless set.
177
+ Uninstalling the plugin takes it out of the settings and leaves the
178
+ cached copy.
179
+ - **Settings:** the install writes none, and none are needed.
180
+ `session statusline install` writes `statusline.sh` and
181
+ `claude-code.d/50-statusline.json` in `~/.sessionkit/tools/settings/`;
182
+ with `--force` it first keeps a `.bak-<stamp>` copy of each file it
183
+ replaces. Any other file there is one you wrote.
184
+ - **Outside these folders, only on a verb:** `session account use NAME
185
+ --yes` replaces the Keychain item `Claude Code-credentials`, or the
186
+ token file named by `account.token_file`, after a backup in the
187
+ accounts folder. `session account new` saves a token to passage.
188
+ With `model.default` set, `session default-model enforce` (and the
189
+ unit or `watch` that runs it) rewrites the top-level `"model"` in
190
+ `~/.claude/settings.json`, after a backup.
191
+
192
+ ## Uninstall
193
+
194
+ Turn off any service you turned on first: the pane signal's service
195
+ and the default model's agent run the `session` command that the last
196
+ line removes. Then remove the
197
+ plugin, if you installed it, and the command. Skip the lines for what
198
+ you never installed:
199
+
200
+ ```console
201
+ session pane-state agent off
202
+ session sound engine off --purge
203
+ launchctl bootout gui/$(id -u)/dev.sessionkit.tools.default-model
204
+ rm ~/Library/LaunchAgents/dev.sessionkit.tools.default-model.plist
205
+ claude plugin uninstall sessiontools@sessiontools
206
+ claude plugin marketplace remove sessiontools
207
+ uv tool uninstall sessiontools
208
+ ```
209
+
210
+ That leaves what the verbs made, listed above. The plugin's cached
211
+ copy, the services' logs and the engine's folder are sessiontools'
212
+ alone. To delete them:
213
+
214
+ ```bash
215
+ rm -rf "${HOME:?}/.claude/plugins/cache/sessiontools"
216
+ rm -rf "${HOME:?}/Library/Logs/sessiontools"
217
+ rm -rf "${HOME:?}/.local/share/sessiontools"
218
+ ```
219
+
220
+ uv's cache stays too, with the other things uv installed; `uv cache
221
+ clean sessiontools` drops this package's entries.
222
+
223
+ The rest is your data: delete it by hand only if you want it gone.
224
+ `~/.sessionkit/tools/` holds your settings and accounts, and the
225
+ accounts folder keeps the backups of every token `session account use`
226
+ replaced. `~/.session/` holds the session folders and the list cache,
227
+ and the kit's other packages keep their files in it too.
228
+
229
+ ## Docs
230
+
231
+ - [The `session` command][verbs]: what it does, every verb, and an
232
+ example of each
233
+ - [Starting a session][start]: agent files, flags, tmux, one holder
234
+ - [Switching Claude accounts][account]: `session account`, its blast
235
+ radius, and `new`, which adds an account
236
+ - [The `session list` cache][cache]: one computation, many readers
237
+ - [Operator and privileged seats][operator]: who seats ask
238
+ - [Closing idle seats][reap]: `session reap`, a dry run by default
239
+ - [Shell completion][completion]: `session completion bash|zsh`, a static
240
+ script built from the verbs' own parsers
241
+ - [The Claude Code plugin][plugin]: the skill and the hooks
242
+ - [The settle alert][settle]: one line past a context threshold
243
+ - [The clock line][clock]: the time and the context on every tool batch
244
+ - [The busy sound][busy]: a sound while a tool batch runs long
245
+ - [The session-info hooks][session-info]: the short session id as the
246
+ session title, and the session's facts in context, both off by default
247
+ - [The sound engine][engine]: the busy sound from one resident process
248
+ - [The status line][status-line]: `session statusline install` puts it in the
249
+ kit's claude-code layer, never `~/.claude`. `session start` seats show it;
250
+ a bare `claude` does not (`alias claude='session start -'`)
251
+ - [The pane signal][pane-state]: BUSY / FREE on each seat's tmux pane,
252
+ and `session pane-state agent`, the launchd agent that publishes it
253
+ - [The default model][default-model]: `session default-model`, opt-in,
254
+ puts settings.json's `"model"` back when something rewrites it
255
+ - [The Python library][library]: `from sessiontools import sessions`
256
+ - [Dependencies][dependencies]: what sessiontools needs, what needs each
257
+ one, and how to upgrade it
258
+ - [Building and testing][building]: make targets, specs, releases
259
+ - [CHANGELOG][changelog]; licence AGPL-3.0-or-later ([LICENSE][license])
260
+
261
+ ## Configuration
262
+
263
+ Nothing needs configuring: unset, every part uses its default. What can
264
+ be set, and where each one lives ([configuration][configuration]):
265
+
266
+ - **Settings files**, JSON in layers: `~/.sessionkit/tools/settings/session.json`
267
+ (the operator, the list cache, the pane signal, accounts, a session's
268
+ home, the default model), `~/.sessionkit/tools/settings/sounds.json` (the busy sound), and
269
+ `~/.sessionkit/tools/settings/claude-code.json` (the model, effort
270
+ and status line `session start` passes on). Every key, its default and
271
+ which layers it is read from: [settings][settings].
272
+ - **Folders of its own:** `~/.session/` (sessions, seats, agent files, the
273
+ state dir) and `~/.sessionkit/tools/accounts/` (`session account`).
274
+ - **Environment variables:** the settle alert's `SESSION_SETTLE_AT`, the
275
+ clock line's `SESSION_CLOCK_WARN_AT`, the hooks' `SESSIONTOOLS_HOOK_*`
276
+ switches (`SESSIONTOOLS_HOOK_CLOCK=on` switches the clock line on), and
277
+ the paths it reads, such as `SESSIONS_DIR` and `SESSION_STATE_DIR`.
278
+ The configuration page lists each one with its default, under the part
279
+ that reads it.
280
+
281
+ ## Logs
282
+
283
+ - **The sound engine:** `~/Library/Logs/sessiontools/sound-engine.log`,
284
+ once `session sound engine on` has run.
285
+ - **The pane signal's agent:** `~/Library/Logs/sessiontools/pane-state.log`,
286
+ once `session pane-state agent on` has run.
287
+ - **The default model:** `~/.session/state/default-model/log.jsonl`, one
288
+ line per write to settings.json, and
289
+ `~/Library/Logs/sessiontools/default-model.log`, once you have loaded
290
+ the launchd agent `session default-model unit launchd` prints.
291
+ - **`session reap --yes`:** `~/.session/state/reap.log`, one line for each
292
+ seat it closed or failed to close.
293
+ - **Each session's settings at birth:**
294
+ `~/.session/sessions/<session-id>/log/settings.jsonl`, what `session
295
+ start` read into that session's settings: each environment variable
296
+ by name, never its value.
297
+ - **The status line:** `~/.config/session/statusline-log.jsonl`, one line
298
+ per render, only when `~/.config/session/` already exists.
299
+
300
+ Follow one as it grows with `tail -F`, for example
301
+ `tail -F ~/Library/Logs/sessiontools/pane-state.log`.
302
+
303
+ - **Health:** `session list` prints a row for each live session, with its
304
+ state and context. A session you know is running that is not listed is
305
+ one sessiontools cannot see.
306
+
307
+ [verbs]: docs/session.md
308
+ [start]: docs/session-start-launcher.md
309
+ [credentials]: docs/session-start-launcher.md#credentials-are-never-handed-down
310
+ [account]: docs/account.md
311
+ [cache]: docs/list-snapshot-cache.md
312
+ [operator]: docs/operator-privileged-seats.md
313
+ [reap]: docs/session-reap.md
314
+ [completion]: docs/shell-completion.md
315
+ [plugin]: docs/claude-code-plugin.md
316
+ [settle]: docs/settle-alert-hook.md
317
+ [clock]: docs/clock-line-hook.md
318
+ [busy]: docs/busy-sound-hook.md
319
+ [session-info]: docs/session-info-hook.md
320
+ [engine]: docs/sound-engine-agent.md
321
+ [status-line]: docs/status-line-script.md
322
+ [pane-state]: docs/pane-state-signal.md
323
+ [default-model]: docs/default-model.md
324
+ [library]: docs/python-library-api.md
325
+ [building]: docs/building-and-testing.md
326
+ [dependencies]: docs/dependencies.md
327
+ [changelog]: CHANGELOG.md
328
+ [license]: LICENSE
329
+ [configuration]: docs/configuration.md
330
+ [settings]: docs/settings.md
@@ -0,0 +1,314 @@
1
+ # sessiontools
2
+
3
+ `session`: one command for the coding-agent sessions on this machine.
4
+ Which are live, whether each is busy or idle, how full its context is,
5
+ and how to start one from an agent file. For anyone running several
6
+ Claude Code sessions side by side, and for the tools that watch them.
7
+
8
+ ## Install
9
+
10
+ This puts `session` on the PATH:
11
+
12
+ ```console
13
+ uv tool install sessiontools
14
+ ```
15
+
16
+ Python 3.11 to 3.14. Optional: [the Claude Code plugin][plugin] adds
17
+ the `session` skill and four hooks.
18
+
19
+ Shell completion, one line per shell ([shell completion][completion]).
20
+ For zsh, in `~/.zshrc` after compinit:
21
+
22
+ ```console
23
+ eval "$(session completion zsh)"
24
+ ```
25
+
26
+ For bash with the bash-completion package (2.x), once:
27
+
28
+ ```console
29
+ session completion bash > ~/.local/share/bash-completion/completions/session
30
+ ```
31
+
32
+ For bash 3.2, or any bash without that package, in `~/.bashrc`:
33
+
34
+ ```console
35
+ eval "$(session completion bash)"
36
+ ```
37
+
38
+ ## Quick start
39
+
40
+ - `session list`: the live sessions, with their panes, state and context.
41
+ - `session get state`: this session's state: busy, idle with children,
42
+ or idle.
43
+ - `session context`: how full this session's context window is.
44
+ - `session context --parts`: what filled it: boot, then growth by
45
+ category.
46
+ - `session ledger --short`: each file read and command run, by tokens.
47
+ - `session account status`: which Claude account this Mac is on.
48
+ - `session start --list-agents`: the agents it can start.
49
+ - `session reap`: seats idle past 1h, each CLOSE or KEEP, and why. A dry
50
+ run.
51
+ - `session default-model`: the model every new `claude` starts on, and
52
+ whether a pin keeps it there. Off until you set `model.default`.
53
+ - `session --help`: every verb.
54
+
55
+ ## Requirements
56
+
57
+ - [x] macOS (tested on macOS 27, Apple silicon)
58
+ - [ ] Linux
59
+ - [ ] WSL
60
+ - [ ] Windows
61
+
62
+ What sessiontools depends on, what needs each one, and how to upgrade
63
+ it: [dependencies][dependencies].
64
+
65
+ ## Switching accounts
66
+
67
+ `session account use NAME` moves this Mac to another Claude account from
68
+ a token you already hold: no browser, no restart, and it never prints a
69
+ token. On a Keychain host it moves **every** seat, not one:
70
+
71
+ | route | moves | cache cost | reversible by | gate |
72
+ |---|---|---|---|---|
73
+ | `account use` (Keychain) | every seat on the Mac with no env token, within ~50 s | the sum of their contexts (2.07M / 1.10M measured) | `use` back, or restore from the backup | witness, `--yes`, preflight, backup |
74
+ | `account use` (token file) | no live seat; each one that relaunches from a shell that reads the file | none now; each seat's context when it restarts | `use` back, or restore from the backup | witness, `--yes`, preflight, backup |
75
+
76
+ A host whose shell exports `CLAUDE_CODE_OAUTH_TOKEN` from a file names it
77
+ in the `session` setting `account.token_file`, and `use` swaps the file.
78
+ A seat `session start` launches does not follow the file unless its agent
79
+ file says `inherit: [CLAUDE_CODE_OAUTH_TOKEN]`.
80
+ Without `--yes`, `use` shows the plan and writes nothing. To add an
81
+ account, `session account new NAME --email E` signs in through `claude
82
+ setup-token`, saves the token to passage, reads it back and registers
83
+ it; the token is never shown. See [Switching Claude accounts][account].
84
+
85
+ If you sign in by token, `CLAUDE_CODE_OAUTH_TOKEN` in your shell, a
86
+ session `session start` launches does not get it by default. To carry
87
+ it, name it under `inherit:` in the agent file ([credentials are never
88
+ handed down][credentials]). A `/login` sign-in needs nothing:
89
+
90
+ ```yaml
91
+ environment:
92
+ inherit:
93
+ - CLAUDE_CODE_OAUTH_TOKEN
94
+ ```
95
+
96
+ ## What an install changes
97
+
98
+ `uv tool install sessiontools` changes two places and fills uv's own
99
+ cache, and it touches no shell startup file and nothing in `~/.claude`.
100
+ Everything else below appears only when you run the verb named beside
101
+ it. Each line says what puts it there and what takes it away.
102
+
103
+ - **PATH:** `~/.local/bin/session`, the one command, or in
104
+ `UV_TOOL_BIN_DIR` when that is set. uv says so if the folder is not
105
+ on your PATH, and edits no shell file. `uv tool uninstall` removes it.
106
+ - **The program:** `~/.local/share/uv/tools/sessiontools/`, its own
107
+ Python environment with PyYAML and jsonschema, or in `UV_TOOL_DIR`.
108
+ `uv tool uninstall` removes it.
109
+ - **uv's cache:** the install fills it with what it downloaded and
110
+ built. It is uv's, shared by everything uv installs, not sessiontools'.
111
+ `uv cache dir` prints where it is (`~/.cache/uv` by default, or
112
+ `UV_CACHE_DIR`). `uv tool uninstall` leaves it; `uv cache clean
113
+ sessiontools` drops this package's entries, and `uv cache prune` drops
114
+ the ones nothing uses.
115
+ - **Folders, each made by a verb you run:**
116
+ - `~/.session/state/list/`: the list cache, `snapshot.json` and
117
+ `list.lock`, at the first `session list`.
118
+ - `~/.session/sessions/<session-id>/`: one for each `session start`,
119
+ holding `launch/`, `log/settings.jsonl` and `session-id`. It also
120
+ gets `settings/<tool>.d/50-env.json`, one for each tool whose
121
+ settings name a variable that is set when the session starts. Each
122
+ is written once, so a resumed session keeps its file, and
123
+ `log/settings.jsonl` names each variable it read, never its value.
124
+ The plugin's hooks and `session park` write only inside a folder
125
+ that is already there.
126
+ - `~/.session/state/reap.log`: `session reap --yes`.
127
+ - `~/.session/state/pane-state/watch.lock`: the pane signal's service.
128
+ - `~/.session/state/default-model/`: `backups/` and `log.jsonl`, the
129
+ first time `session default-model enforce` changes settings.json.
130
+ - `~/.sessionkit/tools/accounts/`: `session account new` and `use`.
131
+ - `~/.local/share/sessiontools/sound/bin/`: `session sound engine on`
132
+ builds the sound engine there with `swiftc`.
133
+ - `~/Library/Logs/sessiontools/`: the two services' logs.
134
+ - `SESSIONS_DIR`, `SESSION_STATE_DIR` and `SESSIONTOOLS_SOUND_HOME`
135
+ move these folders when set.
136
+ - **Services (launchd agents):** none until you turn one on. Each `on`
137
+ writes a plist in `~/Library/LaunchAgents/` and loads it, and it
138
+ starts again at every login. Each `off` unloads it and deletes the
139
+ plist. Both `on` and `off` also retire a job an older release set up
140
+ under its old label: they unload it and delete its plist. `status`
141
+ names one that is still there, and `--dry-run` lists the retire and
142
+ changes nothing.
143
+ - `dev.sessionkit.tools.sound-engine`: `session sound engine on`.
144
+ `off --purge` also deletes the engine it built.
145
+ - `dev.sessionkit.tools.pane-state`: `session pane-state agent on`.
146
+ It sets `@session_state` and `@session_state_why` on each seat's
147
+ tmux pane.
148
+ - `dev.sessionkit.tools.default-model`: no verb writes or loads it.
149
+ `session default-model unit launchd` prints it, and it is there only
150
+ if you save and load it yourself
151
+ ([the default model][default-model]).
152
+ - **Plugin and hooks, optional:** `claude plugin marketplace add` and
153
+ `claude plugin install` ([the Claude Code plugin][plugin]). Claude
154
+ Code names the marketplace and the plugin in `~/.claude/settings.json`
155
+ (`extraKnownMarketplaces`, `enabledPlugins`), lists them in
156
+ `~/.claude/plugins/`, and keeps a copy in
157
+ `~/.claude/plugins/cache/sessiontools/`. The plugin brings the
158
+ `session` skill and nine hook entries: SessionStart (two),
159
+ PreToolUse, PostToolBatch (three), Stop, UserPromptSubmit and
160
+ SessionEnd. Each is silent unless set.
161
+ Uninstalling the plugin takes it out of the settings and leaves the
162
+ cached copy.
163
+ - **Settings:** the install writes none, and none are needed.
164
+ `session statusline install` writes `statusline.sh` and
165
+ `claude-code.d/50-statusline.json` in `~/.sessionkit/tools/settings/`;
166
+ with `--force` it first keeps a `.bak-<stamp>` copy of each file it
167
+ replaces. Any other file there is one you wrote.
168
+ - **Outside these folders, only on a verb:** `session account use NAME
169
+ --yes` replaces the Keychain item `Claude Code-credentials`, or the
170
+ token file named by `account.token_file`, after a backup in the
171
+ accounts folder. `session account new` saves a token to passage.
172
+ With `model.default` set, `session default-model enforce` (and the
173
+ unit or `watch` that runs it) rewrites the top-level `"model"` in
174
+ `~/.claude/settings.json`, after a backup.
175
+
176
+ ## Uninstall
177
+
178
+ Turn off any service you turned on first: the pane signal's service
179
+ and the default model's agent run the `session` command that the last
180
+ line removes. Then remove the
181
+ plugin, if you installed it, and the command. Skip the lines for what
182
+ you never installed:
183
+
184
+ ```console
185
+ session pane-state agent off
186
+ session sound engine off --purge
187
+ launchctl bootout gui/$(id -u)/dev.sessionkit.tools.default-model
188
+ rm ~/Library/LaunchAgents/dev.sessionkit.tools.default-model.plist
189
+ claude plugin uninstall sessiontools@sessiontools
190
+ claude plugin marketplace remove sessiontools
191
+ uv tool uninstall sessiontools
192
+ ```
193
+
194
+ That leaves what the verbs made, listed above. The plugin's cached
195
+ copy, the services' logs and the engine's folder are sessiontools'
196
+ alone. To delete them:
197
+
198
+ ```bash
199
+ rm -rf "${HOME:?}/.claude/plugins/cache/sessiontools"
200
+ rm -rf "${HOME:?}/Library/Logs/sessiontools"
201
+ rm -rf "${HOME:?}/.local/share/sessiontools"
202
+ ```
203
+
204
+ uv's cache stays too, with the other things uv installed; `uv cache
205
+ clean sessiontools` drops this package's entries.
206
+
207
+ The rest is your data: delete it by hand only if you want it gone.
208
+ `~/.sessionkit/tools/` holds your settings and accounts, and the
209
+ accounts folder keeps the backups of every token `session account use`
210
+ replaced. `~/.session/` holds the session folders and the list cache,
211
+ and the kit's other packages keep their files in it too.
212
+
213
+ ## Docs
214
+
215
+ - [The `session` command][verbs]: what it does, every verb, and an
216
+ example of each
217
+ - [Starting a session][start]: agent files, flags, tmux, one holder
218
+ - [Switching Claude accounts][account]: `session account`, its blast
219
+ radius, and `new`, which adds an account
220
+ - [The `session list` cache][cache]: one computation, many readers
221
+ - [Operator and privileged seats][operator]: who seats ask
222
+ - [Closing idle seats][reap]: `session reap`, a dry run by default
223
+ - [Shell completion][completion]: `session completion bash|zsh`, a static
224
+ script built from the verbs' own parsers
225
+ - [The Claude Code plugin][plugin]: the skill and the hooks
226
+ - [The settle alert][settle]: one line past a context threshold
227
+ - [The clock line][clock]: the time and the context on every tool batch
228
+ - [The busy sound][busy]: a sound while a tool batch runs long
229
+ - [The session-info hooks][session-info]: the short session id as the
230
+ session title, and the session's facts in context, both off by default
231
+ - [The sound engine][engine]: the busy sound from one resident process
232
+ - [The status line][status-line]: `session statusline install` puts it in the
233
+ kit's claude-code layer, never `~/.claude`. `session start` seats show it;
234
+ a bare `claude` does not (`alias claude='session start -'`)
235
+ - [The pane signal][pane-state]: BUSY / FREE on each seat's tmux pane,
236
+ and `session pane-state agent`, the launchd agent that publishes it
237
+ - [The default model][default-model]: `session default-model`, opt-in,
238
+ puts settings.json's `"model"` back when something rewrites it
239
+ - [The Python library][library]: `from sessiontools import sessions`
240
+ - [Dependencies][dependencies]: what sessiontools needs, what needs each
241
+ one, and how to upgrade it
242
+ - [Building and testing][building]: make targets, specs, releases
243
+ - [CHANGELOG][changelog]; licence AGPL-3.0-or-later ([LICENSE][license])
244
+
245
+ ## Configuration
246
+
247
+ Nothing needs configuring: unset, every part uses its default. What can
248
+ be set, and where each one lives ([configuration][configuration]):
249
+
250
+ - **Settings files**, JSON in layers: `~/.sessionkit/tools/settings/session.json`
251
+ (the operator, the list cache, the pane signal, accounts, a session's
252
+ home, the default model), `~/.sessionkit/tools/settings/sounds.json` (the busy sound), and
253
+ `~/.sessionkit/tools/settings/claude-code.json` (the model, effort
254
+ and status line `session start` passes on). Every key, its default and
255
+ which layers it is read from: [settings][settings].
256
+ - **Folders of its own:** `~/.session/` (sessions, seats, agent files, the
257
+ state dir) and `~/.sessionkit/tools/accounts/` (`session account`).
258
+ - **Environment variables:** the settle alert's `SESSION_SETTLE_AT`, the
259
+ clock line's `SESSION_CLOCK_WARN_AT`, the hooks' `SESSIONTOOLS_HOOK_*`
260
+ switches (`SESSIONTOOLS_HOOK_CLOCK=on` switches the clock line on), and
261
+ the paths it reads, such as `SESSIONS_DIR` and `SESSION_STATE_DIR`.
262
+ The configuration page lists each one with its default, under the part
263
+ that reads it.
264
+
265
+ ## Logs
266
+
267
+ - **The sound engine:** `~/Library/Logs/sessiontools/sound-engine.log`,
268
+ once `session sound engine on` has run.
269
+ - **The pane signal's agent:** `~/Library/Logs/sessiontools/pane-state.log`,
270
+ once `session pane-state agent on` has run.
271
+ - **The default model:** `~/.session/state/default-model/log.jsonl`, one
272
+ line per write to settings.json, and
273
+ `~/Library/Logs/sessiontools/default-model.log`, once you have loaded
274
+ the launchd agent `session default-model unit launchd` prints.
275
+ - **`session reap --yes`:** `~/.session/state/reap.log`, one line for each
276
+ seat it closed or failed to close.
277
+ - **Each session's settings at birth:**
278
+ `~/.session/sessions/<session-id>/log/settings.jsonl`, what `session
279
+ start` read into that session's settings: each environment variable
280
+ by name, never its value.
281
+ - **The status line:** `~/.config/session/statusline-log.jsonl`, one line
282
+ per render, only when `~/.config/session/` already exists.
283
+
284
+ Follow one as it grows with `tail -F`, for example
285
+ `tail -F ~/Library/Logs/sessiontools/pane-state.log`.
286
+
287
+ - **Health:** `session list` prints a row for each live session, with its
288
+ state and context. A session you know is running that is not listed is
289
+ one sessiontools cannot see.
290
+
291
+ [verbs]: docs/session.md
292
+ [start]: docs/session-start-launcher.md
293
+ [credentials]: docs/session-start-launcher.md#credentials-are-never-handed-down
294
+ [account]: docs/account.md
295
+ [cache]: docs/list-snapshot-cache.md
296
+ [operator]: docs/operator-privileged-seats.md
297
+ [reap]: docs/session-reap.md
298
+ [completion]: docs/shell-completion.md
299
+ [plugin]: docs/claude-code-plugin.md
300
+ [settle]: docs/settle-alert-hook.md
301
+ [clock]: docs/clock-line-hook.md
302
+ [busy]: docs/busy-sound-hook.md
303
+ [session-info]: docs/session-info-hook.md
304
+ [engine]: docs/sound-engine-agent.md
305
+ [status-line]: docs/status-line-script.md
306
+ [pane-state]: docs/pane-state-signal.md
307
+ [default-model]: docs/default-model.md
308
+ [library]: docs/python-library-api.md
309
+ [building]: docs/building-and-testing.md
310
+ [dependencies]: docs/dependencies.md
311
+ [changelog]: CHANGELOG.md
312
+ [license]: LICENSE
313
+ [configuration]: docs/configuration.md
314
+ [settings]: docs/settings.md
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sessiontools"
3
- version = "0.2.0"
3
+ version = "0.3.1"
4
4
  description = "The harness layer: sessions, status and idle, one driver per harness - a library first"
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-or-later"
@@ -2,9 +2,9 @@
2
2
  they have been idle - one driver per harness, a library first.
3
3
 
4
4
  Import it, or use the command it ships: `session` (since
5
- 2030-07-29, when the session repo was retired). `session list` and
5
+ 2030-06-09, when the session repo was retired). `session list` and
6
6
  `session get` are built on the functions below; `session start` is its own
7
- package, `sessiontools.start`; `session send` is disabled (2030-07-30).
7
+ package, `sessiontools.start`; `session send` is disabled (2030-12-08).
8
8
 
9
9
  from sessiontools import sessions, live, find, idle_seconds
10
10
  from sessiontools import session_id, current, SESSION_ID_VARS
@@ -103,7 +103,10 @@ A host whose login shell exports CLAUDE_CODE_OAUTH_TOKEN from a file
103
103
  names that file in the `session` setting account.token_file (no
104
104
  default). `use` then swaps the file and never runs `security`: live
105
105
  seats keep the token they launched with until they restart, and the
106
- plan names them.
106
+ plan names them. A seat `session start` launches does NOT follow the
107
+ file unless its agent file says inherit: [CLAUDE_CODE_OAUTH_TOKEN];
108
+ without it, it signs in from Claude's stored credentials, and `status`
109
+ says so.
107
110
 
108
111
  `new` runs `claude setup-token` in a masked copy of its screen, saves the
109
112
  token to passage at <PREFIX>/<E>, reads it back, and registers it. PREFIX