sessionview 0.3.1__tar.gz → 0.3.3__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 (56) hide show
  1. {sessionview-0.3.1 → sessionview-0.3.3}/PKG-INFO +71 -30
  2. {sessionview-0.3.1 → sessionview-0.3.3}/README.md +70 -29
  3. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview +8 -5
  4. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-autostart +6 -1
  5. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-frame +35 -10
  6. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-install +13 -7
  7. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-messages +25 -10
  8. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-session +93 -33
  9. {sessionview-0.3.1 → sessionview-0.3.3}/bin/tmux-send-agent +4 -3
  10. {sessionview-0.3.1 → sessionview-0.3.3}/bin/tmux-sidebar +426 -77
  11. sessionview-0.3.3/changelog.d/README.md +102 -0
  12. {sessionview-0.3.1 → sessionview-0.3.3}/config/README.md +16 -6
  13. {sessionview-0.3.1 → sessionview-0.3.3}/config/config.json +8 -4
  14. {sessionview-0.3.1 → sessionview-0.3.3}/docs/configuration.md +28 -8
  15. sessionview-0.3.3/docs/decisions/state-and-log-home.md +557 -0
  16. {sessionview-0.3.1 → sessionview-0.3.3}/docs/dependencies.md +5 -5
  17. {sessionview-0.3.1 → sessionview-0.3.3}/docs/sessionview.md +16 -7
  18. {sessionview-0.3.1 → sessionview-0.3.3}/docs/settings.md +5 -2
  19. sessionview-0.3.3/lib/landing.sh +67 -0
  20. sessionview-0.3.3/lib/pane-menu.sh +56 -0
  21. sessionview-0.3.3/lib/state.sh +99 -0
  22. {sessionview-0.3.1 → sessionview-0.3.3}/macos/SessionView.applescript +57 -5
  23. {sessionview-0.3.1 → sessionview-0.3.3}/pyproject.toml +1 -1
  24. {sessionview-0.3.1 → sessionview-0.3.3}/src/sessionview/__init__.py +1 -1
  25. sessionview-0.3.3/tmux/agent-menu.conf +95 -0
  26. sessionview-0.3.1/lib/state.sh +0 -13
  27. sessionview-0.3.1/tmux/agent-menu.conf +0 -33
  28. {sessionview-0.3.1 → sessionview-0.3.3}/.gitignore +0 -0
  29. {sessionview-0.3.1 → sessionview-0.3.3}/LICENSE +0 -0
  30. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-app-install +0 -0
  31. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-config +0 -0
  32. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-from +0 -0
  33. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-newtab +0 -0
  34. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-send +0 -0
  35. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-sidebar-toggle +0 -0
  36. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-terminal +0 -0
  37. {sessionview-0.3.1 → sessionview-0.3.3}/bin/sessionview-usermail +0 -0
  38. {sessionview-0.3.1 → sessionview-0.3.3}/config/keys.md +0 -0
  39. {sessionview-0.3.1 → sessionview-0.3.3}/config/neomuttrc +0 -0
  40. {sessionview-0.3.1 → sessionview-0.3.3}/lib/client.sh +0 -0
  41. {sessionview-0.3.1 → sessionview-0.3.3}/lib/python.sh +0 -0
  42. {sessionview-0.3.1 → sessionview-0.3.3}/lib/sessionview-config-screen +0 -0
  43. {sessionview-0.3.1 → sessionview-0.3.3}/macos/dreamcatcher.icns +0 -0
  44. {sessionview-0.3.1 → sessionview-0.3.3}/plugins/session/plugin.toml +0 -0
  45. {sessionview-0.3.1 → sessionview-0.3.3}/plugins/session/start-agent +0 -0
  46. {sessionview-0.3.1 → sessionview-0.3.3}/plugins/taskmaster/plugin.toml +0 -0
  47. {sessionview-0.3.1 → sessionview-0.3.3}/plugins/taskmaster/tasks-tab +0 -0
  48. {sessionview-0.3.1 → sessionview-0.3.3}/plugins/voicemode/plugin.toml +0 -0
  49. {sessionview-0.3.1 → sessionview-0.3.3}/plugins/voicemode/speak +0 -0
  50. {sessionview-0.3.1 → sessionview-0.3.3}/skills/tmux/SKILL.md +0 -0
  51. {sessionview-0.3.1 → sessionview-0.3.3}/skills/tmux/scripts/tmux-peek +0 -0
  52. {sessionview-0.3.1 → sessionview-0.3.3}/skills/tmux/scripts/tmux-show +0 -0
  53. {sessionview-0.3.1 → sessionview-0.3.3}/skills/tmux/scripts/tmux-type +0 -0
  54. {sessionview-0.3.1 → sessionview-0.3.3}/skills/tmux/scripts/tmux-where +0 -0
  55. {sessionview-0.3.1 → sessionview-0.3.3}/tmux/frame.conf +0 -0
  56. {sessionview-0.3.1 → sessionview-0.3.3}/tmux/tabs-top.conf +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sessionview
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: A sidebar for tmux: your projects (sessions) and your agents, here and on remote hosts.
5
5
  Project-URL: Homepage, https://github.com/sessionkit/sessionview
6
6
  Project-URL: Repository, https://github.com/sessionkit/sessionview
@@ -26,8 +26,11 @@ Install from PyPI: `uv tool install sessionview` (see [Install](#install)).
26
26
 
27
27
  sessionview
28
28
 
29
- In this terminal, that puts the sidebar on the left and the session
30
- `main` on the right; a private outer tmux holds the two panes.
29
+ In this terminal, that puts the sidebar on the left and a tmux session
30
+ on the right; a private outer tmux holds the two panes. The session is
31
+ one tmux already has: your default assistant's, else the one you last
32
+ attached, else the newest. Only when tmux has no session at all does
33
+ sessionview make one, named `main`.
31
34
  `sessionview NAME` attaches the session NAME instead, which must exist.
32
35
  In any tmux client, `prefix H` opens the same sidebar as a popup.
33
36
  Every verb, its form and an example: [the sessionview command](docs/sessionview.md).
@@ -92,8 +95,12 @@ agents, or the menu): the names `session start` knows, pinned ones first.
92
95
  not start. An agent already running is never started twice.
93
96
  - *Agents have a home* — an agent's own file says where it wakes
94
97
  (`start: directory:`); a project's menu starts it in the project.
95
- - *Consent on first open* — Dreamcatcher asks once which agent to start
96
- when it opens (a numbered pick, or never), and remembers.
98
+ - *Consent on first open* — the first time it opens, sessionview asks
99
+ which agent to start whenever it opens, and remembers the answer. It
100
+ says *sessionview can start an agent for you each time it opens*, lists
101
+ your agents by number and asks `Which? (number, name, or "n")`. When it cannot get the
102
+ list it asks `Which agent? (a name, or "no" to never ask again)`. `n` or
103
+ `no` means never; `autostart_agent` in Settings changes it later.
97
104
 
98
105
  **Settings** — every setting, its default, its note and where its value
99
106
  comes from, in a small screen (project menu → Settings); the rows come
@@ -135,7 +142,8 @@ hand. Blink Shell on iPad is wanted and not yet connecting.
135
142
  claude-code: short, safe driving skills with tools
136
143
 
137
144
  Paths resolve from the checkout (`SESSIONVIEW_ROOT`); state lives in
138
- `~/.local/state/sessionview` (`SESSIONVIEW_STATE`). Its direct dependencies:
145
+ `~/.sessionkit/view/state` (`SESSIONVIEW_STATE`) and the log in
146
+ `~/.sessionkit/view/log` (`SESSIONVIEW_LOG`). Its direct dependencies:
139
147
  [dependencies](docs/dependencies.md).
140
148
 
141
149
  ## Install
@@ -235,14 +243,13 @@ beside it. Each line says what puts it there and what takes it away.
235
243
  sessionview` drops this package's entries, and `uv cache prune` drops
236
244
  the ones nothing uses.
237
245
  - **Folders, each made by a verb you run:**
238
- - `~/.local/state/sessionview/`: the state folder, made the first time
239
- `sessionview` opens (or `sessionview session` or `sessionview
240
- messages` runs). `SESSIONVIEW_STATE` moves it. An older release's
241
- `~/.local/state/herd/` is renamed to it then, when only the old
242
- one exists. In it:
246
+ - `~/.sessionkit/view/state/`: the state folder, mode 0700, made the
247
+ first time `sessionview` opens (or `sessionview session` or
248
+ `sessionview messages` runs). `SESSIONVIEW_STATE` names another
249
+ folder instead. Each file sessionview makes in it is private (mode
250
+ 0600). In it:
243
251
  - `frame-PID.tty`, one while each frame is open. The frame deletes
244
252
  it when it closes; a frame killed outright can leave it behind.
245
- - `sessionview.log`, the log ([Logs](#logs)).
246
253
  - `recent.tsv`, a line each time `sessionview session` opens a
247
254
  project, for the picker's order.
248
255
  - `picker-order`, when you flip the picker's sort.
@@ -254,6 +261,23 @@ beside it. Each line says what puts it there and what takes it away.
254
261
  - `sidebar-remote-HOST.json`, what the sidebar last saw from each
255
262
  host in your `remotes` setting.
256
263
  - `mutt/`, an rc file for each box `sessionview messages` opens.
264
+ - `~/.sessionkit/view/log/`: the log folder, mode 0700, made at the
265
+ same time. It holds `sessionview.log` ([Logs](#logs)).
266
+ `SESSIONVIEW_LOG` names another log file. When `SESSIONVIEW_STATE`
267
+ is set, the log is `sessionview.log` in that folder instead.
268
+ - `~/.local/state/sessionview/`: the state folder of releases before
269
+ this one. Nothing new is made there. On an upgrade, the first start
270
+ hard-links `recent.tsv`, `picker-order`, `sidebar-mail.txt` and each
271
+ file in `mutt/` into the new state folder. The old names are kept,
272
+ so the two names are one file, and a sidebar still running from
273
+ before the upgrade stays correct. The old folder is left in place,
274
+ with the old `sessionview.log`, `sidebar-mouse.log`, the frame pins
275
+ and the plugin and remote files, which are not carried over. One
276
+ line is added to the end of the old log, naming the new one. Each
277
+ start sets the old folder to mode 0700, so the old names are private
278
+ too; the files in it keep their modes. An
279
+ older release's `~/.local/state/herd/` is renamed to this folder
280
+ first, when only the herd one exists.
257
281
  - `~/Applications/SessionView.app`: `sessionview install --app` (or
258
282
  `make app` in a checkout), or in `DEST` when that is set. It is
259
283
  built in a temporary folder that is deleted after, and a rebuild
@@ -261,7 +285,9 @@ beside it. Each line says what puts it there and what takes it away.
261
285
  - **Services (launchd agents):** none. sessionview runs no daemon and
262
286
  writes nothing in `~/Library/LaunchAgents/`. While a frame is open it
263
287
  runs a tmux server of its own, `sessionview-frame`, beside yours; the
264
- server exits when the last frame closes.
288
+ server exits when the last frame closes. With `pane_menu` on, an
289
+ Option + right-click on that server's pane that holds your tmux goes
290
+ through to yours.
265
291
  - **Plugin and skills, optional:** `claude plugin marketplace add` and
266
292
  `claude plugin install` ([Install](#install)). Claude Code names the
267
293
  marketplace and the plugin in `~/.claude/settings.json`
@@ -286,9 +312,10 @@ beside it. Each line says what puts it there and what takes it away.
286
312
  mouse bindings on the status line (`MouseDown1Status`,
287
313
  `MouseDown1StatusRight`, `MouseDrag1Status`). It gives each session it
288
314
  opens the sessionview look, with options set on that session only.
289
- With `pane_menu` on, it also binds a right-click in a pane
290
- (`MouseDown3Pane`), in place of yours, and sets two global
291
- `@sessionview_agent_menu_` options. Nothing is written to
315
+ With `pane_menu` on, it also binds both right-clicks in a pane
316
+ (`MouseDown3Pane`, and `M-MouseDown3Pane` for Option + right-click),
317
+ in place of yours, and sets two global `@sessionview_agent_menu_`
318
+ options. Nothing is written to
292
319
  `~/.tmux.conf`: these last until that tmux server exits.
293
320
  - **Outside these folders, only on a verb:** with `fullscreen` on, the
294
321
  Dock app on Terminal.app needs the Accessibility permission you grant
@@ -320,21 +347,25 @@ rm ~/.local/bin/sessionview ~/.local/bin/sessionview-* ~/.local/bin/tmux-sidebar
320
347
  ```
321
348
 
322
349
  That leaves what the verbs made, listed above. The Dock app, the
323
- plugin's cached copy and the state folder are sessionview's alone. To
324
- delete them:
350
+ plugin's cached copy, and the state and log folders are sessionview's
351
+ alone. To delete them:
325
352
 
326
353
  ```bash
327
354
  rm -rf "${HOME:?}/Applications/SessionView.app"
328
355
  rm -rf "${HOME:?}/.claude/plugins/cache/sessionview"
356
+ rm -rf "${HOME:?}/.sessionkit/view/state" "${HOME:?}/.sessionkit/view/log"
329
357
  rm -rf "${HOME:?}/.local/state/sessionview"
330
358
  ```
331
359
 
360
+ The last line is for an upgrade from an earlier release: it removes the
361
+ old state folder, with the old log in it.
362
+
332
363
  uv's cache stays too, with the other things uv installed; `uv cache
333
364
  clean sessionview` drops this package's entries. The key bindings go
334
365
  when your tmux server next exits.
335
366
 
336
367
  The rest is your data: delete it by hand only if you want it gone.
337
- `~/.sessionkit/view/` holds your settings, and the kit's other packages
368
+ `~/.sessionkit/view/settings/` holds your settings, and the kit's other packages
338
369
  keep theirs beside it in `~/.sessionkit/`.
339
370
 
340
371
  ## Keys and clicks
@@ -344,17 +375,20 @@ keep theirs beside it in `~/.sessionkit/`.
344
375
  picker (type to filter, ↑↓, enter); `+` at the bottom → start an
345
376
  agent; the store name → that store's session and store view, its `⋯`
346
377
  (or `s`) → Switch store, right-click → Kick; `all · grouped` on the agents
347
- heading are toggles; drag the border to resize; `q` reloads, `Q`
378
+ heading are toggles (the first cycles all / store / project); drag the border to resize; `q` reloads, `Q`
348
379
  quits, esc backs out of any view
349
380
  - tab bar: `◧` at the far left hides/shows the sidebar; click selects,
350
381
  drag reorders, `+` new tab (the picker); a close `×`, on by default;
351
382
  `tmux set -g @sessionview_tab_close 0` turns it off
352
383
  - menu: ↑↓ / j k move, the letter or enter picks, esc or a click
353
384
  anywhere else closes
354
- - agent panes (opt-in): right-click an agent's pane for Speak, Continue,
355
- Read Clipboard, Park or Succeed; each mails that agent. Turn it on
356
- with `sessionview-config set pane_menu true`. It replaces your own tmux right-click menu, which is
357
- why it is off by default. Using tmux without session view? Add
385
+ - agent panes (opt-in): right-click or Option + right-click an agent's
386
+ pane for Speak, Continue, Read Clipboard, Park or Succeed; each mails
387
+ that agent. It opens even on an agent whose program uses the mouse, as
388
+ Claude Code does. An agent's pane is one whose title is
389
+ `<glyph> <address>`. Turn it on with
390
+ `sessionview-config set pane_menu true`. It replaces your own tmux
391
+ right-click menus, which is why it is off by default. Using tmux without session view? Add
358
392
  `source-file -q /path/to/sessionview/tmux/agent-menu.conf` to
359
393
  `~/.tmux.conf` and reload.
360
394
 
@@ -380,8 +414,9 @@ The look follows herdr's; its colours were matched from herdr's own window.
380
414
  - **Environment:** `SESSIONVIEW_CONFIG` (another file for yours),
381
415
  `SESSIONVIEW_FROM` (the name mail is sent as, else `mail_from`, else
382
416
  `$USER`; `sessionview from` prints the name in effect), `SESSIONVIEW_STATE`
383
- (state, default `~/.local/state/sessionview`), `SESSIONVIEW_PLUGINS`
384
- (another plugin directory).
417
+ (state, default `~/.sessionkit/view/state`), `SESSIONVIEW_LOG` (the log
418
+ file, default `~/.sessionkit/view/log/sessionview.log`),
419
+ `SESSIONVIEW_PLUGINS` (another plugin directory).
385
420
  - sessionview does not read the kit's settings cascade today, only these.
386
421
 
387
422
  Every file, variable and tmux option is in
@@ -391,19 +426,25 @@ and every key in
391
426
 
392
427
  ## Logs
393
428
 
394
- - `~/.local/state/sessionview/sessionview.log`: what went wrong, one
429
+ - `~/.sessionkit/view/log/sessionview.log`: what went wrong, one
395
430
  entry each, starting with the time (a failed tmux call, an uncaught
396
431
  error with its traceback). Follow it with
397
- `tail -f ~/.local/state/sessionview/sessionview.log` when a click
432
+ `tail -f ~/.sessionkit/view/log/sessionview.log` when a click
398
433
  seems to do nothing.
434
+ - **After an upgrade:** the log of earlier releases,
435
+ `~/.local/state/sessionview/sessionview.log`, stays where it is and
436
+ is never moved. Its last line names the new log, and the new log's
437
+ first line names the old one.
399
438
  - **Levels:** `log_level` in your settings (or `$SESSIONVIEW_LOG_LEVEL`
400
439
  for one run). `error`, the default, logs only what went wrong.
401
440
  `timing` adds a line per click or key with its milliseconds, and each
402
441
  command that took 50 ms or more, so a slow click names its slow part.
403
442
  `debug` adds every command and every mouse event (what the old
404
443
  `sidebar-mouse.log` held).
405
- - It sits under `$SESSIONVIEW_STATE` when that is set. It is not rotated:
406
- turn `timing` or `debug` on while you look, then back to `error`.
444
+ - `$SESSIONVIEW_LOG` names another log file. Without it, a
445
+ `$SESSIONVIEW_STATE` that names another folder keeps the log there, as
446
+ `sessionview.log`. It is not rotated: turn `timing` or `debug` on
447
+ while you look, then back to `error`.
407
448
  - **Health:** `sessionview install --check`: exit 0 and `ready` when
408
449
  every requirement is present, exit 1 with each MISSING one named
409
450
  otherwise. It checks the machine, not a running sidebar.
@@ -9,8 +9,11 @@ Install from PyPI: `uv tool install sessionview` (see [Install](#install)).
9
9
 
10
10
  sessionview
11
11
 
12
- In this terminal, that puts the sidebar on the left and the session
13
- `main` on the right; a private outer tmux holds the two panes.
12
+ In this terminal, that puts the sidebar on the left and a tmux session
13
+ on the right; a private outer tmux holds the two panes. The session is
14
+ one tmux already has: your default assistant's, else the one you last
15
+ attached, else the newest. Only when tmux has no session at all does
16
+ sessionview make one, named `main`.
14
17
  `sessionview NAME` attaches the session NAME instead, which must exist.
15
18
  In any tmux client, `prefix H` opens the same sidebar as a popup.
16
19
  Every verb, its form and an example: [the sessionview command](docs/sessionview.md).
@@ -75,8 +78,12 @@ agents, or the menu): the names `session start` knows, pinned ones first.
75
78
  not start. An agent already running is never started twice.
76
79
  - *Agents have a home* — an agent's own file says where it wakes
77
80
  (`start: directory:`); a project's menu starts it in the project.
78
- - *Consent on first open* — Dreamcatcher asks once which agent to start
79
- when it opens (a numbered pick, or never), and remembers.
81
+ - *Consent on first open* — the first time it opens, sessionview asks
82
+ which agent to start whenever it opens, and remembers the answer. It
83
+ says *sessionview can start an agent for you each time it opens*, lists
84
+ your agents by number and asks `Which? (number, name, or "n")`. When it cannot get the
85
+ list it asks `Which agent? (a name, or "no" to never ask again)`. `n` or
86
+ `no` means never; `autostart_agent` in Settings changes it later.
80
87
 
81
88
  **Settings** — every setting, its default, its note and where its value
82
89
  comes from, in a small screen (project menu → Settings); the rows come
@@ -118,7 +125,8 @@ hand. Blink Shell on iPad is wanted and not yet connecting.
118
125
  claude-code: short, safe driving skills with tools
119
126
 
120
127
  Paths resolve from the checkout (`SESSIONVIEW_ROOT`); state lives in
121
- `~/.local/state/sessionview` (`SESSIONVIEW_STATE`). Its direct dependencies:
128
+ `~/.sessionkit/view/state` (`SESSIONVIEW_STATE`) and the log in
129
+ `~/.sessionkit/view/log` (`SESSIONVIEW_LOG`). Its direct dependencies:
122
130
  [dependencies](docs/dependencies.md).
123
131
 
124
132
  ## Install
@@ -218,14 +226,13 @@ beside it. Each line says what puts it there and what takes it away.
218
226
  sessionview` drops this package's entries, and `uv cache prune` drops
219
227
  the ones nothing uses.
220
228
  - **Folders, each made by a verb you run:**
221
- - `~/.local/state/sessionview/`: the state folder, made the first time
222
- `sessionview` opens (or `sessionview session` or `sessionview
223
- messages` runs). `SESSIONVIEW_STATE` moves it. An older release's
224
- `~/.local/state/herd/` is renamed to it then, when only the old
225
- one exists. In it:
229
+ - `~/.sessionkit/view/state/`: the state folder, mode 0700, made the
230
+ first time `sessionview` opens (or `sessionview session` or
231
+ `sessionview messages` runs). `SESSIONVIEW_STATE` names another
232
+ folder instead. Each file sessionview makes in it is private (mode
233
+ 0600). In it:
226
234
  - `frame-PID.tty`, one while each frame is open. The frame deletes
227
235
  it when it closes; a frame killed outright can leave it behind.
228
- - `sessionview.log`, the log ([Logs](#logs)).
229
236
  - `recent.tsv`, a line each time `sessionview session` opens a
230
237
  project, for the picker's order.
231
238
  - `picker-order`, when you flip the picker's sort.
@@ -237,6 +244,23 @@ beside it. Each line says what puts it there and what takes it away.
237
244
  - `sidebar-remote-HOST.json`, what the sidebar last saw from each
238
245
  host in your `remotes` setting.
239
246
  - `mutt/`, an rc file for each box `sessionview messages` opens.
247
+ - `~/.sessionkit/view/log/`: the log folder, mode 0700, made at the
248
+ same time. It holds `sessionview.log` ([Logs](#logs)).
249
+ `SESSIONVIEW_LOG` names another log file. When `SESSIONVIEW_STATE`
250
+ is set, the log is `sessionview.log` in that folder instead.
251
+ - `~/.local/state/sessionview/`: the state folder of releases before
252
+ this one. Nothing new is made there. On an upgrade, the first start
253
+ hard-links `recent.tsv`, `picker-order`, `sidebar-mail.txt` and each
254
+ file in `mutt/` into the new state folder. The old names are kept,
255
+ so the two names are one file, and a sidebar still running from
256
+ before the upgrade stays correct. The old folder is left in place,
257
+ with the old `sessionview.log`, `sidebar-mouse.log`, the frame pins
258
+ and the plugin and remote files, which are not carried over. One
259
+ line is added to the end of the old log, naming the new one. Each
260
+ start sets the old folder to mode 0700, so the old names are private
261
+ too; the files in it keep their modes. An
262
+ older release's `~/.local/state/herd/` is renamed to this folder
263
+ first, when only the herd one exists.
240
264
  - `~/Applications/SessionView.app`: `sessionview install --app` (or
241
265
  `make app` in a checkout), or in `DEST` when that is set. It is
242
266
  built in a temporary folder that is deleted after, and a rebuild
@@ -244,7 +268,9 @@ beside it. Each line says what puts it there and what takes it away.
244
268
  - **Services (launchd agents):** none. sessionview runs no daemon and
245
269
  writes nothing in `~/Library/LaunchAgents/`. While a frame is open it
246
270
  runs a tmux server of its own, `sessionview-frame`, beside yours; the
247
- server exits when the last frame closes.
271
+ server exits when the last frame closes. With `pane_menu` on, an
272
+ Option + right-click on that server's pane that holds your tmux goes
273
+ through to yours.
248
274
  - **Plugin and skills, optional:** `claude plugin marketplace add` and
249
275
  `claude plugin install` ([Install](#install)). Claude Code names the
250
276
  marketplace and the plugin in `~/.claude/settings.json`
@@ -269,9 +295,10 @@ beside it. Each line says what puts it there and what takes it away.
269
295
  mouse bindings on the status line (`MouseDown1Status`,
270
296
  `MouseDown1StatusRight`, `MouseDrag1Status`). It gives each session it
271
297
  opens the sessionview look, with options set on that session only.
272
- With `pane_menu` on, it also binds a right-click in a pane
273
- (`MouseDown3Pane`), in place of yours, and sets two global
274
- `@sessionview_agent_menu_` options. Nothing is written to
298
+ With `pane_menu` on, it also binds both right-clicks in a pane
299
+ (`MouseDown3Pane`, and `M-MouseDown3Pane` for Option + right-click),
300
+ in place of yours, and sets two global `@sessionview_agent_menu_`
301
+ options. Nothing is written to
275
302
  `~/.tmux.conf`: these last until that tmux server exits.
276
303
  - **Outside these folders, only on a verb:** with `fullscreen` on, the
277
304
  Dock app on Terminal.app needs the Accessibility permission you grant
@@ -303,21 +330,25 @@ rm ~/.local/bin/sessionview ~/.local/bin/sessionview-* ~/.local/bin/tmux-sidebar
303
330
  ```
304
331
 
305
332
  That leaves what the verbs made, listed above. The Dock app, the
306
- plugin's cached copy and the state folder are sessionview's alone. To
307
- delete them:
333
+ plugin's cached copy, and the state and log folders are sessionview's
334
+ alone. To delete them:
308
335
 
309
336
  ```bash
310
337
  rm -rf "${HOME:?}/Applications/SessionView.app"
311
338
  rm -rf "${HOME:?}/.claude/plugins/cache/sessionview"
339
+ rm -rf "${HOME:?}/.sessionkit/view/state" "${HOME:?}/.sessionkit/view/log"
312
340
  rm -rf "${HOME:?}/.local/state/sessionview"
313
341
  ```
314
342
 
343
+ The last line is for an upgrade from an earlier release: it removes the
344
+ old state folder, with the old log in it.
345
+
315
346
  uv's cache stays too, with the other things uv installed; `uv cache
316
347
  clean sessionview` drops this package's entries. The key bindings go
317
348
  when your tmux server next exits.
318
349
 
319
350
  The rest is your data: delete it by hand only if you want it gone.
320
- `~/.sessionkit/view/` holds your settings, and the kit's other packages
351
+ `~/.sessionkit/view/settings/` holds your settings, and the kit's other packages
321
352
  keep theirs beside it in `~/.sessionkit/`.
322
353
 
323
354
  ## Keys and clicks
@@ -327,17 +358,20 @@ keep theirs beside it in `~/.sessionkit/`.
327
358
  picker (type to filter, ↑↓, enter); `+` at the bottom → start an
328
359
  agent; the store name → that store's session and store view, its `⋯`
329
360
  (or `s`) → Switch store, right-click → Kick; `all · grouped` on the agents
330
- heading are toggles; drag the border to resize; `q` reloads, `Q`
361
+ heading are toggles (the first cycles all / store / project); drag the border to resize; `q` reloads, `Q`
331
362
  quits, esc backs out of any view
332
363
  - tab bar: `◧` at the far left hides/shows the sidebar; click selects,
333
364
  drag reorders, `+` new tab (the picker); a close `×`, on by default;
334
365
  `tmux set -g @sessionview_tab_close 0` turns it off
335
366
  - menu: ↑↓ / j k move, the letter or enter picks, esc or a click
336
367
  anywhere else closes
337
- - agent panes (opt-in): right-click an agent's pane for Speak, Continue,
338
- Read Clipboard, Park or Succeed; each mails that agent. Turn it on
339
- with `sessionview-config set pane_menu true`. It replaces your own tmux right-click menu, which is
340
- why it is off by default. Using tmux without session view? Add
368
+ - agent panes (opt-in): right-click or Option + right-click an agent's
369
+ pane for Speak, Continue, Read Clipboard, Park or Succeed; each mails
370
+ that agent. It opens even on an agent whose program uses the mouse, as
371
+ Claude Code does. An agent's pane is one whose title is
372
+ `<glyph> <address>`. Turn it on with
373
+ `sessionview-config set pane_menu true`. It replaces your own tmux
374
+ right-click menus, which is why it is off by default. Using tmux without session view? Add
341
375
  `source-file -q /path/to/sessionview/tmux/agent-menu.conf` to
342
376
  `~/.tmux.conf` and reload.
343
377
 
@@ -363,8 +397,9 @@ The look follows herdr's; its colours were matched from herdr's own window.
363
397
  - **Environment:** `SESSIONVIEW_CONFIG` (another file for yours),
364
398
  `SESSIONVIEW_FROM` (the name mail is sent as, else `mail_from`, else
365
399
  `$USER`; `sessionview from` prints the name in effect), `SESSIONVIEW_STATE`
366
- (state, default `~/.local/state/sessionview`), `SESSIONVIEW_PLUGINS`
367
- (another plugin directory).
400
+ (state, default `~/.sessionkit/view/state`), `SESSIONVIEW_LOG` (the log
401
+ file, default `~/.sessionkit/view/log/sessionview.log`),
402
+ `SESSIONVIEW_PLUGINS` (another plugin directory).
368
403
  - sessionview does not read the kit's settings cascade today, only these.
369
404
 
370
405
  Every file, variable and tmux option is in
@@ -374,19 +409,25 @@ and every key in
374
409
 
375
410
  ## Logs
376
411
 
377
- - `~/.local/state/sessionview/sessionview.log`: what went wrong, one
412
+ - `~/.sessionkit/view/log/sessionview.log`: what went wrong, one
378
413
  entry each, starting with the time (a failed tmux call, an uncaught
379
414
  error with its traceback). Follow it with
380
- `tail -f ~/.local/state/sessionview/sessionview.log` when a click
415
+ `tail -f ~/.sessionkit/view/log/sessionview.log` when a click
381
416
  seems to do nothing.
417
+ - **After an upgrade:** the log of earlier releases,
418
+ `~/.local/state/sessionview/sessionview.log`, stays where it is and
419
+ is never moved. Its last line names the new log, and the new log's
420
+ first line names the old one.
382
421
  - **Levels:** `log_level` in your settings (or `$SESSIONVIEW_LOG_LEVEL`
383
422
  for one run). `error`, the default, logs only what went wrong.
384
423
  `timing` adds a line per click or key with its milliseconds, and each
385
424
  command that took 50 ms or more, so a slow click names its slow part.
386
425
  `debug` adds every command and every mouse event (what the old
387
426
  `sidebar-mouse.log` held).
388
- - It sits under `$SESSIONVIEW_STATE` when that is set. It is not rotated:
389
- turn `timing` or `debug` on while you look, then back to `error`.
427
+ - `$SESSIONVIEW_LOG` names another log file. Without it, a
428
+ `$SESSIONVIEW_STATE` that names another folder keeps the log there, as
429
+ `sessionview.log`. It is not rotated: turn `timing` or `debug` on
430
+ while you look, then back to `error`.
390
431
  - **Health:** `sessionview install --check`: exit 0 and `ready` when
391
432
  every requirement is present, exit 1 with each MISSING one named
392
433
  otherwise. It checks the machine, not a running sidebar.
@@ -6,7 +6,8 @@
6
6
  # sessionview --help | --version
7
7
  #
8
8
  # sessionview [SESSION] the frame: sidebar on the left, SESSION attached on
9
- # the right (default main; a named SESSION must exist).
9
+ # the right (default: one the server has, else main;
10
+ # a named SESSION must exist).
10
11
  # --sock names its tmux server; --width, columns.
11
12
  # sessionview config settings: no verb opens the screen (get/set/list ...)
12
13
  # sessionview session NAME open a project session with its standard tabs
@@ -22,7 +23,7 @@
22
23
  #
23
24
  # Also: prefix H in any tmux client opens the sidebar as a popup.
24
25
  # Config: ~/.sessionkit/view/settings/view.json over the shipped config/config.json.
25
- # State: ~/.local/state/sessionview (SESSIONVIEW_STATE=).
26
+ # State: ~/.sessionkit/view/state (SESSIONVIEW_STATE=). Log: ~/.sessionkit/view/log (SESSIONVIEW_LOG=).
26
27
  # (end of usage)
27
28
  # Ghostty was stripped out so this works in any terminal with only tmux. It
28
29
  # started as ghostty-herd; the Ghostty launcher, the split hook and cmd+d
@@ -66,7 +67,7 @@ case "${1:-}" in
66
67
  "$SESSIONVIEW_ROOT/bin/tmux-sidebar" --help >&2
67
68
  exit 2
68
69
  fi
69
- SESSIONVIEW_STATE="${SESSIONVIEW_STATE:-$HOME/.local/state/sessionview}"; . "$SESSIONVIEW_ROOT/lib/state.sh"; . "$SESSIONVIEW_ROOT/lib/python.sh"; export SESSIONVIEW_ROOT SESSIONVIEW_STATE
70
+ . "$SESSIONVIEW_ROOT/lib/state.sh"; . "$SESSIONVIEW_ROOT/lib/python.sh"; export SESSIONVIEW_ROOT SESSIONVIEW_STATE
70
71
  # The supervisor: q = exit 0 = reload (picks up an edited tmux-sidebar or
71
72
  # sidebar.toml), Q = exit 3 = quit. A CRASH IS NOT A QUIT: any other exit
72
73
  # holds the error on screen and waits (one undefined name once took the
@@ -86,7 +87,9 @@ esac
86
87
  # (--sock, else this tmux's own server when run inside one, else default):
87
88
  # a typo or a probe word must not mean "open a frame on a new session named
88
89
  # after it". Read-only check; on a miss, usage and exit 2, nothing touched.
89
- # A new named session is `sessionview session NAME`. No word = main, as before.
90
+ # A new named session is `sessionview session NAME`. No word: the frame picks
91
+ # a session the server already has, and makes main only when it has none
92
+ # (lib/landing.sh).
90
93
  word=""; sock=""; prev=""
91
94
  for a in "$@"; do
92
95
  case "$prev" in --sock) sock="$a"; prev=""; continue;; --width) prev=""; continue;; esac
@@ -110,5 +113,5 @@ if [ -n "$word" ]; then
110
113
  # the frame looks where this checked: inside tmux, the caller's own server
111
114
  [ -n "$extra" ] && set -- "$@" --sock "$sock"
112
115
  fi
113
- SESSIONVIEW_STATE="${SESSIONVIEW_STATE:-$HOME/.local/state/sessionview}"; . "$SESSIONVIEW_ROOT/lib/state.sh"; . "$SESSIONVIEW_ROOT/lib/python.sh"; export SESSIONVIEW_ROOT SESSIONVIEW_STATE
116
+ . "$SESSIONVIEW_ROOT/lib/state.sh"; . "$SESSIONVIEW_ROOT/lib/python.sh"; export SESSIONVIEW_ROOT SESSIONVIEW_STATE
114
117
  exec "$SESSIONVIEW_ROOT/bin/sessionview-frame" "$@"
@@ -116,9 +116,14 @@ sys.exit(1)' "$1"
116
116
  # that then fails to start. Empty output, missing command, any error: the
117
117
  # free-text question below still works, so this fails open like everything
118
118
  # else in this file.
119
+ #
120
+ # Each line is NAME, then a TAB and a mark (`claude<TAB>built in`), or the
121
+ # NAME alone. Only the field before the first TAB is a name: the mark can hold
122
+ # a space, and splitting the whole output on whitespace offered "built" and
123
+ # "in" as agents.
119
124
  agent_list() {
120
125
  command -v session >/dev/null 2>&1 || return 0
121
- session start --list-agents 2>/dev/null
126
+ session start --list-agents 2>/dev/null | awk -F '\t' '$1 != "" { print $1 }'
122
127
  }
123
128
 
124
129
  # Discard every byte waiting to be read on the terminal. ask() calls this once
@@ -7,7 +7,9 @@
7
7
  #
8
8
  # sessionview-frame [SESSION] [--sock SOCKET] [--width N]
9
9
  #
10
- # SESSION what the right pane attaches (default: main)
10
+ # SESSION what the right pane attaches (default: a session the server
11
+ # already has: the default seat's, else the last attached, else
12
+ # the newest; main, made, only when the server has none)
11
13
  # --sock the tmux server SESSION lives on (default: default; a store's is
12
14
  # tm-store-NAME)
13
15
  # --width sidebar columns (default: config width via sessionview-config, else 34)
@@ -32,7 +34,7 @@ while [ $# -gt 0 ]; do case "$1" in
32
34
  *) SESSION="$1"; GIVEN=1;;
33
35
  esac; shift; done
34
36
  # The state dir and the Python only after the flags: -h touches nothing.
35
- SESSIONVIEW_STATE="${SESSIONVIEW_STATE:-$HOME/.local/state/sessionview}"; . "$SESSIONVIEW_ROOT/lib/state.sh"; . "$SESSIONVIEW_ROOT/lib/python.sh"; export SESSIONVIEW_ROOT SESSIONVIEW_STATE
37
+ . "$SESSIONVIEW_ROOT/lib/state.sh"; . "$SESSIONVIEW_ROOT/lib/python.sh"; export SESSIONVIEW_ROOT SESSIONVIEW_STATE
36
38
  # The frame takes over the terminal it runs in, so it needs one: a script or
37
39
  # an agent's shell (stdin/stdout not a tty) is refused and nothing is touched.
38
40
  # Every refusal is one line on stderr, exit 2.
@@ -72,11 +74,21 @@ if [ -n "${TMUX:-}" ]; then
72
74
  # (measured: SESSIONVIEW_FRAME_SOCK set here did not reach the frame,
73
75
  # which landed on the default frame server) - carry what matters by hand.
74
76
  cmd="SESSIONVIEW_ROOT=$(q "$SESSIONVIEW_ROOT") SESSIONVIEW_STATE=$(q "$SESSIONVIEW_STATE") SESSIONVIEW_PYTHON=$(q "${SESSIONVIEW_PYTHON:-}")"
77
+ # A log the user gave goes too; a derived one never does (lib/state.sh).
78
+ if [ -n "${SESSIONVIEW_LOG:-}" ]; then cmd="$cmd SESSIONVIEW_LOG=$(q "$SESSIONVIEW_LOG")"; fi
75
79
  cmd="$cmd SESSIONVIEW_FRAME_SOCK=$(q "$FRAME_SOCK") TMUX="
76
80
  cmd="$cmd exec $(q "$0") $(q "$SESSION") --sock $(q "$SOCK")"
77
81
  [ -n "$WIDTH" ] && cmd="$cmd --width $(q "$WIDTH")"
78
82
  exec tmux -S "$here_path" detach-client -t "$ctty" -E "$cmd"
79
83
  fi
84
+ # Outside tmux with no SESSION named: land in a session the server already
85
+ # has (the default seat's, else the last attached, else the newest; the rule
86
+ # and its reasons are in lib/landing.sh). `main` is made below only when the
87
+ # server has no session at all.
88
+ if [ -z "$GIVEN" ]; then
89
+ . "$SESSIONVIEW_ROOT/lib/landing.sh"
90
+ if sv_landing "$SOCK"; then SESSION="$SV_LANDING"; fi
91
+ fi
80
92
  if [ -z "$WIDTH" ]; then
81
93
  WIDTH="$("$SESSIONVIEW_ROOT/bin/sessionview-config" get width 2>/dev/null || true)"
82
94
  WIDTH="${WIDTH:-34}"
@@ -107,12 +119,16 @@ FRESH_WIN=""
107
119
  # global config is untouched (a normal tmux session keeps its bar at the
108
120
  # bottom).
109
121
  sh -c 'S="$1"; L="$3"; tmux() { command tmux -L "$L" "$@"; }; . "$2"' sh "$SESSION" "$SESSIONVIEW_ROOT/tmux/frame.conf" "$SOCK"
110
- # the agent pane menu (right-click an agent's pane: Speak / Continue / ...), IF
111
- # the user turned it on. Opt-in: one bind-key is global to the server and
112
- # replaces their own right-click menu. Sourced into the running server, so it
113
- # works whether or not tmux was already up. Fails open, like the rest of the
114
- # frame's setup. See tmux/agent-menu.conf.
115
- if [ "$("$SESSIONVIEW_ROOT/bin/sessionview-config" get pane_menu 2>/dev/null)" = true ]; then
122
+ # the agent pane menu (right-click or Option + right-click an agent's pane:
123
+ # Speak / Continue / ...), IF the user turned it on. Opt-in: its two bind-keys
124
+ # are global to the server and replace both of the user's own right-click
125
+ # bindings (MouseDown3Pane, M-MouseDown3Pane). Sourced into the running
126
+ # server, so it works whether or not tmux was already up. Fails open, like the
127
+ # rest of the frame's setup. See tmux/agent-menu.conf. Read once: the frame's
128
+ # own server acts on the same answer below (the Option + right-click
129
+ # pass-through).
130
+ PANE_MENU="$("$SESSIONVIEW_ROOT/bin/sessionview-config" get pane_menu 2>/dev/null || true)"
131
+ if [ "$PANE_MENU" = true ]; then
116
132
  "$TMUX_BIN" -L "$SOCK" source-file "$SESSIONVIEW_ROOT/tmux/agent-menu.conf" 2>/dev/null || true
117
133
  fi
118
134
  # the chosen agent, if the user has one - asked once, remembered, started in
@@ -182,6 +198,13 @@ o set -w -t "=$F:0" pane-active-border-style "fg=#3a3f4d,bg=#21252e"
182
198
  o set -w -t "=$F:0" pane-border-lines single
183
199
  o bind-key -T root MouseDown1Border "resize-pane -M"
184
200
  o bind-key -T root MouseDrag1Border "resize-pane -M"
201
+ # with pane_menu on, an Option + right-click on the client pane goes through to
202
+ # your tmux and its agent menu; the frame's other panes keep the binding this
203
+ # server had. Here, after new-session: before it this server may not exist.
204
+ # Off, nothing is touched. Fails open. See lib/pane-menu.sh.
205
+ if [ "$PANE_MENU" = true ]; then
206
+ { . "$SESSIONVIEW_ROOT/lib/pane-menu.sh" && sv_frame_option_forward "$FRAME_SOCK"; } >/dev/null 2>&1 || true
207
+ fi
185
208
  MAIN="$(o split-window -h -P -F '#{pane_id}' -t "$SIDE" -e "TMUX=" \
186
209
  "exec \"$TMUX_BIN\" -L \"$SOCK\" attach -t \"=$SESSION\"")"
187
210
  # the frame publishes its own panes, so the sidebar process (which cannot see
@@ -209,7 +232,9 @@ o resize-pane -t "$SIDE" -x "$WIDTH"
209
232
  TTY="$(tty 2>/dev/null || echo none)"
210
233
  o set-hook -g "client-detached[$$]" "if -F '#{==:#{hook_client},$TTY}' 'kill-session -t =$F'"
211
234
  o select-pane -t "$MAIN"
212
- # pin = the client pane's tty: that is the client the sidebar may move
213
- o display-message -p -t "$MAIN" '#{pane_tty}' >"$PIN"
235
+ # pin = the client pane's tty: that is the client the sidebar may move. Made
236
+ # 0600: the umask is the subshell's alone, never this script's, which starts
237
+ # the user's tmux server (a server keeps its umask for every pane it opens).
238
+ ( umask 077; o display-message -p -t "$MAIN" '#{pane_tty}' >"$PIN" )
214
239
  o attach -t "=$F"
215
240
  cleanup
@@ -157,22 +157,28 @@ fi
157
157
  if command -v session >/dev/null 2>&1; then say PASS "session on PATH"
158
158
  else say WARN "session not on PATH - the sidebar lists tmux sessions without it, but no agent rows, so no agent menu (Message, Settle, ...), and no 'Start an agent' (uv tool install sessiontools)"; fi
159
159
  # neomutt - the messages tab (bin/sessionview-messages runs `neomutt -R`, exit 2 without
160
- # it). MISSING, not WARN: sessionview needs NeoMutt.
160
+ # it). A WARN, not MISSING: that tab is made only when sessionmail, which is optional,
161
+ # is installed, so sessionview runs without neomutt and the check still says ready.
162
+ # (It was MISSING until 2026-10-28, when neomutt was ruled optional for the check.)
161
163
  if command -v neomutt >/dev/null 2>&1; then
162
164
  nv="$(neomutt -v 2>/dev/null | sed -n 's/^NeoMutt \([0-9]*\).*/\1/p' | head -1)"
163
165
  if neomutt -v 2>/dev/null | grep -q '+notmuch'; then say PASS "neomutt ${nv:-?} with notmuch"
164
166
  else say WARN "neomutt ${nv:-?} built without notmuch - the messages tab opens, with no sidebar (brew install neomutt)"; fi
165
167
  else
166
- fail "neomutt - brew install neomutt (the messages tab)"
168
+ say WARN "neomutt not on PATH - the messages tab, which sessionview makes only when sessionmail is installed, cannot open (brew install neomutt)"
167
169
  fi
168
170
  # notmuch - the messages tab's sidebar queries Type: and Project:; unindexed, the Asks
169
- # rows are every mail to NAME+ask, with no error
171
+ # rows are every mail to NAME+ask, with no error. Asked of sessionmail's notmuch profile
172
+ # when `message path --notmuch-config` names one (sessionmail 0.3.3), as the tab does;
173
+ # no message, an older one or no profile: notmuch's own default config.
170
174
  if command -v notmuch >/dev/null 2>&1; then
171
- if [ "$(notmuch config get index.header.Type 2>/dev/null)" = Type ] &&
172
- [ "$(notmuch config get index.header.Project 2>/dev/null)" = Project ]; then
173
- say PASS "notmuch indexes Type and Project"
175
+ NMCFG="$(message path --notmuch-config 2>/dev/null)" || NMCFG=""
176
+ NM="notmuch${NMCFG:+ --config=$NMCFG}"
177
+ if [ "$(notmuch ${NMCFG:+"--config=$NMCFG"} config get index.header.Type 2>/dev/null)" = Type ] &&
178
+ [ "$(notmuch ${NMCFG:+"--config=$NMCFG"} config get index.header.Project 2>/dev/null)" = Project ]; then
179
+ say PASS "notmuch indexes Type and Project${NMCFG:+ ($NMCFG)}"
174
180
  else
175
- say WARN "notmuch does not index Type and Project - the Asks rows show every mail to NAME+ask: notmuch config set index.header.Type Type; notmuch config set index.header.Project Project; notmuch reindex '*'"
181
+ say WARN "notmuch${NMCFG:+ ($NMCFG)} does not index Type and Project - the Asks rows show every mail to NAME+ask: $NM config set index.header.Type Type; $NM config set index.header.Project Project; $NM reindex '*'"
176
182
  fi
177
183
  else say WARN "notmuch not on PATH - the messages tab has no sidebar (brew install notmuch)"; fi
178
184
  # optional