tuiboard 0.11.0 → 0.13.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.
@@ -43,13 +43,36 @@ archive_column: Archive
43
43
  # zones:
44
44
  # planner: on # Today/Tomorrow cross-board panel (F1)
45
45
  # agenda: on # 24h agenda + calendar overlay (F2)
46
- # agents: on # live Claude Code session view (F3)
46
+ # agents: on # live agent sessions view (F3)
47
47
 
48
- # Optional: override what Enter does in the Agents zone ("open the selected
49
- # Claude Code session"). An argv array — the tokens {cwd} and {sessionId} are
48
+ # Enter in the Agents zone opens the selected session in a new tab/window of
49
+ # the terminal tuiboard runs in, detected automatically: tmux, herdr, WezTerm,
50
+ # Windows Terminal, Ghostty — else the OS default (Linux: xdg-terminal-exec,
51
+ # Windows: a new PowerShell window, macOS: Terminal.app). If nothing works, the
52
+ # resume command is copied to the clipboard instead. Force one when detection
53
+ # guesses wrong (e.g. a multiplexer env var leaking into another terminal):
54
+ # resume_terminal: auto # tmux | herdr | wezterm | windows-terminal | ghostty
55
+ # # | xdg-terminal-exec | windows-console | macos-terminal
56
+ #
57
+ # The session runs in your shell, which stays open when the agent exits.
58
+ # `auto` = the shell you started tuiboard from: on Windows Git Bash, Nushell or
59
+ # PowerShell (pwsh, else powershell); elsewhere your $SHELL. Force one with:
60
+ # resume_shell: auto # bash | zsh | fish | nu | pwsh | powershell | cmd
61
+ #
62
+ # Agent status symbols, the same as herdr's: × waiting for you, ◐ working,
63
+ # ✓ done, ○ idle, · closed (△ = turn stopped updating). Or colored dots:
64
+ # status_indicators: symbols # symbols (default) | dots
65
+ #
66
+ # Enter not doing what you expect? From the directory you run tuiboard in (in a
67
+ # checkout of the repo): `bun run agents:open <session-id-prefix> --dry-run`
68
+ # prints the detected terminal, shell and exact command; the `o` detail of a
69
+ # session also shows the full result of its last Enter.
70
+
71
+ # Optional: replace that with your own launcher script. An argv array — the tokens {cwd}, {sessionId} and {resume}
72
+ # (the agent's own resume command, e.g. `claude --resume <sessionId>`) are
50
73
  # substituted, then it's run directly (no shell). Point it at your own script
51
- # to spawn a custom terminal layout. When omitted, tuiboard just opens a new
52
- # WezTerm tab and runs `claude --resume <sessionId>`.
74
+ # to spawn a custom terminal layout. Takes precedence over resume_terminal —
75
+ # remove it to get the automatic behavior above.
53
76
  #
54
77
  # The first element must be a DIRECTLY EXECUTABLE program — a real binary on
55
78
  # PATH or an absolute path. It is NOT run through a shell, so shell builtins
@@ -65,11 +88,12 @@ archive_column: Archive
65
88
  # Optional: the command copied to your clipboard by `c` in the Agents zone — one
66
89
  # paste that cd's into the session's directory and resumes it, for when you want
67
90
  # to open the session yourself in a new tab/pane anywhere (no WezTerm needed).
68
- # The tokens {cwd} and {sessionId} are substituted. It's a plain string, so use
69
- # whatever chaining your shell wants. Default (works in bash/zsh/pwsh/cmd):
70
- # copy_resume_command: 'cd "{cwd}" && claude --resume {sessionId}'
91
+ # The tokens {cwd}, {sessionId} and {resume} (the agent's own resume command,
92
+ # e.g. `claude --resume <sessionId>`) are substituted. It's a plain string, so
93
+ # use whatever chaining your shell wants. Default (works in bash/zsh/pwsh/cmd):
94
+ # copy_resume_command: 'cd "{cwd}" && {resume}'
71
95
  # Nushell users typically want `;` instead of `&&`:
72
- # copy_resume_command: 'cd "{cwd}"; claude --resume {sessionId}'
96
+ # copy_resume_command: 'cd "{cwd}"; {resume}'
73
97
 
74
98
  # Optional: overlay read-only calendar events on the Agenda (the 24h timeline).
75
99
  # Connect a provider with `tuiboard calendar-setup google` / `... microsoft`,
package/CHANGELOG.md CHANGED
@@ -5,6 +5,130 @@ All notable changes to **tuiboard** are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.13.0] - 2026-09-17
11
+
12
+ tuiboard and [herdr](https://herdr.dev) now work as one: the Agents zone shows
13
+ herdr's live state for every session open there, speaks herdr's status
14
+ symbols, and `H` jumps to a session in herdr or resumes it in the right
15
+ workspace. Plus fixes found using it all day on Windows.
16
+
17
+ ### Added
18
+ - **Live agent state from herdr** (#37). When herdr is running, tuiboard polls
19
+ `herdr api snapshot` and links every pane running Claude Code, Codex,
20
+ OpenCode or Pi to its session — by the session id/file herdr reports (with
21
+ herdr's agent integrations installed), else by agent and directory. Those
22
+ sessions take herdr's state, including two new ones: **waiting for you** and
23
+ **done**; idle Codex/OpenCode/Pi sessions become visible, and long Claude
24
+ turns no longer show as stale (#22). Zoomed cards and the `o` detail show the
25
+ herdr workspace and tab.
26
+ - **herdr's status symbols** — `×` waiting, `◐` working, `✓` done, `○` idle,
27
+ `·` closed (tuiboard's own `△` for stale) — are the default for everyone, so
28
+ both tools read the same; `status_indicators: dots` keeps colored dots.
29
+ - **`H` opens sessions in herdr** (#38): focuses the pane a session is open
30
+ in, or resumes it in herdr — a new tab named after the session, in the
31
+ workspace that already holds that directory (else one named like the
32
+ folder, else the focused one). **Enter** on a session already open in herdr
33
+ focuses it instead of starting a second copy.
34
+ - **Omarchy bar widget in the repo** (#27): `omarchy-plugin/` — an
35
+ overdue/today badge and a Today/Tomorrow panel (done, undone, defer, open
36
+ tuiboard) built on `tuiboard summary` / `tuiboard task`, installed with
37
+ `omarchy-plugin/install.sh`. Repo only, not part of the npm package.
38
+
39
+ ### Changed
40
+ - **Agent sessions are sorted by most recent activity** (#43), newest first,
41
+ instead of by status first — a session used 40 seconds ago no longer sits
42
+ below ones idle in herdr for hours. Status only breaks ties; archived
43
+ sessions stay at the bottom.
44
+ - Launch steps for Enter and `H` run asynchronously: a slow start (cold
45
+ PowerShell, an agent booting in herdr) no longer freezes the UI.
46
+
47
+ ### Fixed
48
+ - **Quitting gives the terminal back** (#47). `q`, Ctrl+C and termination
49
+ signals exited without tearing down the renderer, leaving mouse tracking
50
+ and the alternate screen on — moving the mouse at the shell then printed
51
+ `51;7;45M…`. tuiboard now restores the terminal before exiting, and the
52
+ `tuiboard` launcher resets mouse/paste/focus reporting and the cursor after
53
+ the app ends, whatever way it ended.
54
+ - **`H` on Windows with Codex, OpenCode and Pi** (#41). npm-installed CLIs on
55
+ Windows are an extensionless sh shim next to `.cmd`/`.ps1`, which herdr
56
+ can't launch directly ("not a valid Win32 application"); on Windows the
57
+ resume command is typed into the new pane's shell instead.
58
+ - **Responsive layout follows the renderer's terminal size** (#45), the size
59
+ the frame is drawn at, instead of `process.stdout.columns`.
60
+
61
+ ### Known issues
62
+ - On Windows, herdr may not detect Pi sessions as agents (#49): they open
63
+ fine, but without herdr's live state.
64
+
65
+ ## [0.12.0] - 2026-09-17
66
+
67
+ The Agents zone stops being Claude-Code-only: it now lists **Codex, OpenCode
68
+ and Pi** sessions next to Claude Code, tells them apart at a glance, and
69
+ reopens any of them in whatever terminal and shell you use (#12).
70
+
71
+ ### Added
72
+ - **Codex sessions** (#20). Read-only from Codex's rollout files under
73
+ `$CODEX_HOME` (default `~/.codex`), including archived and zstd-compressed
74
+ ones, with names from `session_index.jsonl` / the state DB; resume with
75
+ `codex resume <id>`. Subagent threads are hidden.
76
+ - **OpenCode sessions** (#17). Read-only from OpenCode's SQLite store
77
+ (`$XDG_DATA_HOME/opencode/opencode.db`); resume with
78
+ `opencode --session <id>`.
79
+ - **Pi sessions** (#33). Read-only from Pi's JSONL sessions
80
+ (`~/.pi/agent/sessions/`, honoring `PI_CODING_AGENT_DIR`,
81
+ `PI_CODING_AGENT_SESSION_DIR` and `sessionDir`); names from `/name`; resume
82
+ with `pi --session <id>`.
83
+ - Codex, OpenCode and Pi keep no record of running processes, so their
84
+ sessions are busy while a turn is open and turn stale once it stops updating
85
+ for 30 minutes; an open-but-idle TUI of theirs isn't detected.
86
+ - **Harness badge, model and harness filter** (#23). Each session shows a
87
+ colored `cc` / `cx` / `oc` / `pi` badge and its model (`opus-5`,
88
+ `gpt-5.5-codex`, …), also in the `o` detail. `f` in the Agents zone cycles
89
+ all → cc → cx → oc → pi; the active filter shows in the panel title.
90
+ - **Two-line session cards** in the zoomed / fullscreen Agents view: title and
91
+ age on top, model · branch · directory underneath. The dashboard strip stays
92
+ one line per session and fits its fields to the real row width, dropping
93
+ model, then branch, then directory before shortening the title.
94
+ - **Enter opens sessions in any common terminal, not only WezTerm** (#25).
95
+ tuiboard detects where it runs — tmux, herdr, WezTerm, Windows Terminal (new
96
+ tab), Ghostty (new window) — and otherwise uses the OS default:
97
+ `xdg-terminal-exec` on Linux, a new console window on Windows, Terminal.app
98
+ on macOS. If launching fails, the resume command is copied to the clipboard
99
+ and the banner says so. New `resume_terminal` option forces one;
100
+ `resume_command` still wins.
101
+ - **Resumed sessions run in your shell** (#31). `auto` follows the shell you
102
+ started tuiboard from — on Windows Git Bash (Git's `bin\bash.exe`, never
103
+ WSL's), Nushell or PowerShell; `$SHELL` elsewhere — and the shell stays open
104
+ after the agent exits. New `resume_shell` option (`bash | zsh | fish | nu |
105
+ pwsh | powershell | cmd`) forces one.
106
+ - **Enter diagnostics.** The `o` detail shows the full result of a session's
107
+ last Enter, and `bun run agents:open <session-id-prefix> [--dry-run]` (in a
108
+ checkout) prints the detected terminal, shell and exact launch command.
109
+
110
+ ### Changed
111
+ - **Agent CLIs plug in through a common adapter interface** (#16). Claude Code
112
+ sessions behave as before. The `stale-pid` status is now `stale` (same glyph
113
+ and color), since not every agent writes PID records.
114
+ - **New `{resume}` token** for `resume_command` and `copy_resume_command`: the
115
+ selected agent's own resume command. The `copy_resume_command` default is now
116
+ `cd "{cwd}" && {resume}`; custom templates using `claude --resume
117
+ {sessionId}` keep working unchanged.
118
+ - **The Agents zone refreshes per agent.** A change under one agent's session
119
+ store re-scans only that agent, and a session writing non-stop still
120
+ refreshes at least once a second. Session stores that don't exist yet when
121
+ tuiboard starts (agent installed later, first session) are picked up within
122
+ a few seconds instead of needing a restart.
123
+
124
+ ### Fixed
125
+ - **Enter in Windows Terminal** (#29). `wt.exe` (and a Store-installed `pwsh`)
126
+ are App Execution Aliases that Bun's spawn can't find; Windows launches now
127
+ go through PowerShell's `Start-Process`, which resolves them. Session
128
+ directories stored with `/` (OpenCode) are passed as `\`.
129
+ - **Agent directories keep `/` on macOS/Linux** — the shortened path was always
130
+ joined with `\`.
131
+
8
132
  ## [0.11.0] - 2026-09-16
9
133
 
10
134
  ### Fixed
@@ -350,6 +474,9 @@ First public release on npm. This entry captures the full feature set at launch.
350
474
 
351
475
  Built with [OpenTUI](https://opentui.com) + SolidJS on Bun.
352
476
 
477
+ [0.13.0]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.13.0
478
+ [0.12.0]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.12.0
479
+ [0.11.0]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.11.0
353
480
  [0.10.0]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.10.0
354
481
  [0.9.2]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.9.2
355
482
  [0.9.1]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.9.1
package/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  A terminal **kanban** board on plain markdown files, with three optional panels
4
4
  you switch on or off: a **Today/Tomorrow planner** across all your boards, a
5
5
  **24-hour agenda** with a read-only Google / Microsoft 365 calendar overlay, and
6
- a **live view of your Claude Code sessions**. Run it as a pure kanban, or any mix
6
+ a **live view of your coding-agent sessions** (Claude Code, Codex, OpenCode, Pi).
7
+ Run it as a pure kanban, or any mix
7
8
  of the four. The board is always on; the rest is opt-in (see [Zones](#zones)).
8
9
 
9
10
  Built with [OpenTUI](https://opentui.com) + SolidJS on Bun. Cross-platform
@@ -11,7 +12,7 @@ Built with [OpenTUI](https://opentui.com) + SolidJS on Bun. Cross-platform
11
12
  the Obsidian Tasks-plugin emoji vocabulary, so they open and edit fine in
12
13
  any markdown editor.
13
14
 
14
- ![tuiboard — kanban board, Today/Tomorrow panel, 24h agenda with calendar overlay, and live Claude Code agents in one terminal dashboard](docs/screenshot.png)
15
+ ![tuiboard — kanban board, Today/Tomorrow panel, 24h agenda with calendar overlay, and live coding-agent sessions in one terminal dashboard](docs/screenshot.png)
15
16
 
16
17
  ## Install
17
18
 
@@ -72,10 +73,38 @@ Starting fresh — no Obsidian, no special folders required:
72
73
  already contains `.md` files with `- [ ]` tasks — it auto-discovers them.
73
74
  4. **Run it:** `tuiboard` (or the short alias `tb`).
74
75
 
75
- **The Agent view needs zero setup.** tuiboard reads your local Claude Code
76
- sessions from `~/.claude/` automatically, so the live agent strip fills in as
77
- soon as you've used Claude Code — nothing to connect or configure. (Tools that
78
- don't write to `~/.claude`, like Codex, won't show up there.)
76
+ **The Agent view needs zero setup.** tuiboard reads your local agent sessions
77
+ automatically, read-only — Claude Code from `~/.claude/`, Codex from
78
+ `~/.codex/` (respects `$CODEX_HOME`), OpenCode from
79
+ `~/.local/share/opencode/opencode.db` (respects `$XDG_DATA_HOME`), Pi from
80
+ `~/.pi/agent/sessions/` (respects `$PI_CODING_AGENT_DIR`,
81
+ `$PI_CODING_AGENT_SESSION_DIR` and `sessionDir`) — so the live agent strip
82
+ fills in as soon as you've used any of them, even if you start the agent after
83
+ tuiboard. Nothing to connect or configure. Codex, OpenCode and Pi keep no
84
+ record of running processes, so their sessions show as busy while a turn is in
85
+ progress (stale if a turn stops updating for 30 minutes), never as
86
+ idle-but-open.
87
+
88
+ Each session's state uses the same symbols as [herdr](https://herdr.dev):
89
+ `×` waiting for you, `◐` working, `✓` done, `○` idle, `·` closed, plus
90
+ tuiboard's own `△` for a turn that stopped updating (`status_indicators: dots`
91
+ switches to colored dots). **With herdr running**, tuiboard reads its live
92
+ state for every session open in a herdr pane — so "waiting for you", "done"
93
+ and "idle" show up for every agent, and a long Claude turn no longer turns
94
+ stale — and the zoomed cards say where each one lives (`herdr blits · tab 3`).
95
+ herdr reports the exact session when its agent integration is installed
96
+ (`herdr integration install claude|codex|opencode|pi`); otherwise tuiboard
97
+ matches by agent and directory. `H` jumps to a session in herdr — or resumes
98
+ it there, in a new tab of the workspace that holds its project — and Enter on
99
+ a session that's already open in herdr takes you to it instead of starting a
100
+ second copy.
101
+
102
+ Every session carries a colored two-letter harness badge — `cc` Claude Code,
103
+ `cx` Codex, `oc` OpenCode, `pi` Pi — plus the model it ran on. The dashboard strip keeps
104
+ one line per session (on a narrow row the model gives way first, then the
105
+ branch, then the directory); zoom the Agents zone (`z`) or run
106
+ `tuiboard --view=agents` for two-line cards with the model, branch and full
107
+ directory under each title. `f` in the Agents zone filters by harness.
79
108
 
80
109
  See [Configure](#configure) for assignees, the done/archive column names, and
81
110
  the optional custom "open session in your terminal" command.
@@ -100,11 +129,11 @@ https://github.com/NazzarenoGiannelli/tuiboard). Set it up for me from scratch:
100
129
  home path) with a `boards:` list pointing at those files by ABSOLUTE path,
101
130
  plus `assignees: [...]`, `done_column: Done`, `archive_column: Archive`.
102
131
  4. Ask me which zones I want besides the kanban board: the Today/Tomorrow
103
- planner, the 24h agenda, and the live Claude Code agents view. For any I
104
- don't want, add a `zones:` block setting it to `off` (e.g. someone who
105
- doesn't use Claude Code would set `agents: off`). If I want them all, omit
106
- the block. Do NOT configure the agents view beyond on/off — it reads
107
- `~/.claude` automatically when enabled.
132
+ planner, the 24h agenda, and the live agent-sessions view (Claude Code,
133
+ Codex, OpenCode, Pi). For any I don't want, add a `zones:` block setting it
134
+ to `off` (e.g. someone who uses none of those agents would set
135
+ `agents: off`). If I want them all, omit the block. Do NOT configure the
136
+ agents view beyond on/off — it finds each agent's sessions automatically.
108
137
  5. Ask me whether I want to overlay my Google Calendar or Microsoft 365 events
109
138
  on the Agenda (skip this if I turned the agenda off). If yes, tell me to run
110
139
  `tuiboard calendar-setup google` (or `microsoft`) — it interviews me, opens
@@ -155,31 +184,40 @@ assignees: [Alice, Bob]
155
184
  done_column: Done
156
185
  archive_column: Archive
157
186
 
158
- # Optional: override Enter in the Agents zone. argv array, {cwd}/{sessionId}
159
- # substituted, run directly (no shell — element 0 must be a real binary/abs
160
- # path, NOT a shell builtin or Windows App Execution Alias). Defaults to
161
- # opening a WezTerm tab with `claude --resume <id>`. For a custom layout:
187
+ # Enter in the Agents zone opens the session in the terminal tuiboard runs in
188
+ # (tmux, herdr, WezTerm, Windows Terminal, Ghostty, else the OS default
189
+ # terminal; clipboard as last resort). Force one if detection guesses wrong:
190
+ # resume_terminal: windows-terminal # auto (default) | tmux | herdr | wezterm |
191
+ # ghostty | xdg-terminal-exec | windows-console | macos-terminal
192
+ # The session runs in your shell (auto: the one you started tuiboard from — Git
193
+ # Bash / Nushell / PowerShell on Windows, $SHELL elsewhere). Force one with:
194
+ # resume_shell: bash # auto | bash | zsh | fish | nu | pwsh | powershell | cmd
195
+
196
+ # Optional: replace Enter with your own launcher. argv array, {cwd}/{sessionId}/
197
+ # {resume} substituted, run directly (no shell — element 0 must be a real binary/abs
198
+ # path, NOT a shell builtin or Windows App Execution Alias). Takes precedence
199
+ # over resume_terminal. For a custom layout:
162
200
  # resume_command: ["nu", "C:/Users/you/.config/tuiboard/code-resume.nu", "{cwd}", "{sessionId}"]
163
201
 
164
202
  # Optional: the command `c` copies to the clipboard in the Agents zone — one
165
203
  # paste that cd's into the session dir and resumes it, so you can open it
166
- # yourself in any tab/pane (no WezTerm needed). {cwd}/{sessionId} substituted.
167
- # Default: 'cd "{cwd}" && claude --resume {sessionId}'. Nushell users:
168
- # copy_resume_command: 'cd "{cwd}"; claude --resume {sessionId}'
204
+ # yourself in any tab/pane (no WezTerm needed). {cwd}/{sessionId}/{resume}
205
+ # substituted. Default: 'cd "{cwd}" && {resume}'. Nushell users:
206
+ # copy_resume_command: 'cd "{cwd}"; {resume}'
169
207
  ```
170
208
 
171
209
  ## Zones
172
210
 
173
211
  tuiboard is four zones — **board** (kanban), **planner** (Today/Tomorrow across
174
212
  all boards), **agenda** (24h timeline + calendar overlay), and **agents** (live
175
- Claude Code sessions). Only want some of them? The board is always on; the other
213
+ coding-agent sessions). Only want some of them? The board is always on; the other
176
214
  three are yours to configure:
177
215
 
178
216
  ```yaml
179
217
  zones:
180
218
  planner: on # Today/Tomorrow panel (toggle at runtime with F1)
181
219
  agenda: on # 24h agenda + calendars (F2)
182
- agents: off # live Claude Code view (F3)
220
+ agents: off # live agent sessions view (F3)
183
221
  ```
184
222
 
185
223
  Each zone takes one of:
@@ -187,14 +225,14 @@ Each zone takes one of:
187
225
  | Value | Behavior |
188
226
  |---|---|
189
227
  | `on` | Enabled and shown at launch (the default). |
190
- | `off` | **Disabled entirely** — never rendered, skipped by `Shift-Tab`, its F-key is inert, and its background work never starts (no calendar fetch, no `~/.claude` reads). |
228
+ | `off` | **Disabled entirely** — never rendered, skipped by `Shift-Tab`, its F-key is inert, and its background work never starts (no calendar fetch, no agent session reads). |
191
229
  | `hidden` | Enabled but **collapsed at launch** — reveal it any time with its F-key. |
192
230
 
193
231
  `true`/`false` work as aliases for `on`/`off`. So a pure kanban is just
194
232
  `agenda: off` and `agents: off`; kanban + calendar is `agents: off`. The
195
233
  difference between `off` and the F-key hide: `off` means the feature never runs
196
- at all — handy if you don't use Claude Code and don't want tuiboard reading
197
- `~/.claude`.
234
+ at all — handy if you use none of the supported agents and don't want
235
+ tuiboard reading their session stores.
198
236
 
199
237
  ## Calendars (Agenda overlay)
200
238
 
@@ -376,7 +414,7 @@ Launch `tuiboard` with no flag for the default dashboard (every enabled zone).
376
414
  |---|---|---|
377
415
  | (none) | **Dashboard** — every enabled zone | Default; your configured layout |
378
416
  | `--view=planner` | Today/Tomorrow alone, full width | A narrow vertical strip beside other work |
379
- | `--view=board` | Kanban + planner panel only | Focus mode, or a single WezTerm pane |
417
+ | `--view=board` | Kanban + planner panel only | Focus mode, or a single terminal pane |
380
418
  | `--view=timeline` | Timeline fullscreen | Wall-mounted "what's now" |
381
419
  | `--view=agents` | Agent view fullscreen | Cross-machine session monitor |
382
420
 
@@ -404,10 +442,9 @@ session (until the next terminal resize).
404
442
  | `v` | Toggle Today/Tomorrow planner panel focus |
405
443
  | `Shift-Tab` | Cycle active zone (planner → board → timeline → agents) |
406
444
  | `+` | New board — create one, or adopt markdown files you already have (also the `+` chip in the top bar) |
407
- | `z` | Focus one pane (single-pane mode) — automatic below 100 columns |
408
445
  | `h` / `l` | In single-pane, walk the ring: planner → each column → agenda → agents, wrapping |
409
446
  | `F1` / `F2` / `F3` | Toggle visibility of Planner / Timeline / Agents zones |
410
- | `z` | Zoom active zone to full screen |
447
+ | `z` | Zoom active zone to full screen (single-pane below 100 columns is automatic, not triggered by `z`) |
411
448
  | `r` | Refresh everything — reload boards from disk, rescan agents, force-refetch the agenda calendar (bypasses the 30-min cache) |
412
449
 
413
450
  ### Agenda (timeline zone)
@@ -425,9 +462,11 @@ session (until the next terminal resize).
425
462
  | Key | Action |
426
463
  |---|---|
427
464
  | `j` / `k` | Move the cursor down / up the session list |
428
- | `Enter` | Open (resume) the selected session in a new WezTerm tab |
429
- | `c` | Copy a one-paste `cd … && claude --resume <id>` command for the selected session — drop it into any tab/pane to land in the right dir and resume (no WezTerm needed; format is `copy_resume_command`) |
430
- | `o` | Session detail (cwd, branch, last prompts, resume command) |
465
+ | `Enter` | Go to the session if it's open in herdr; otherwise open (resume) it in a new tab/window of your terminal — tmux, herdr, WezTerm, Windows Terminal, Ghostty, or the OS default; falls back to copying the command (`resume_terminal` to force one) |
466
+ | `c` | Copy a one-paste `cd … && <resume>` command (e.g. `claude --resume <id>`) for the selected session — drop it into any tab/pane to land in the right dir and resume (no WezTerm needed; format is `copy_resume_command`) |
467
+ | `o` | Session detail (harness, model, cwd, branch, last prompts, resume command, result of the last `Enter`) |
468
+ | `H` | herdr: go to the session's pane, or resume it in a new tab of its project's workspace (herdr must be running) |
469
+ | `f` | Filter by harness: all → `cc` Claude Code → `cx` Codex → `oc` OpenCode → `pi` Pi (shown in the panel title; outside the Agents zone `f` is the board filter) |
431
470
 
432
471
  ### Task actions (work in board, planner, AND timeline zones)
433
472
 
@@ -530,10 +569,57 @@ An ambiguous match is refused rather than guessed at.
530
569
  Exit 3 is the mtime watermark: a write is refused rather than allowed to
531
570
  clobber an edit made in the TUI or another editor in the meantime.
532
571
 
572
+ ## Omarchy bar widget
573
+
574
+ On [Omarchy](https://omarchy.org), `omarchy-plugin/` ships a bar widget built
575
+ on `tuiboard summary` and `tuiboard task`: an overdue/today badge in the bar,
576
+ and a panel with the Today/Tomorrow planner where a task can be marked
577
+ done/undone or deferred to tomorrow without leaving the bar. Right-click the
578
+ badge to force a refresh, middle-click to open tuiboard itself.
579
+
580
+ **Requires** `tuiboard` on `PATH` (see [Install](#install) above) and Omarchy
581
+ with its shell plugin support.
582
+
583
+ **Install** (from a checkout of this repo):
584
+
585
+ ```bash
586
+ git clone https://github.com/NazzarenoGiannelli/tuiboard.git
587
+ cd tuiboard
588
+ ./omarchy-plugin/install.sh
589
+ ```
590
+
591
+ This symlinks `omarchy-plugin/` into `~/.config/omarchy/plugins/nazz.tuiboard`
592
+ and enables it in the bar's right section. `omarchy plugin add <git-url>`
593
+ isn't used here — it clones a git repo and expects `manifest.json` at its
594
+ root, which doesn't fit a widget living inside this monorepo, and there's no
595
+ separate Omarchy plugin marketplace to publish to at the time of writing. The
596
+ symlink means `omarchy plugin update` doesn't apply; update by pulling this
597
+ repo instead (`git pull`, then `omarchy-shell shell rescanPlugins` if the bar
598
+ doesn't pick it up on its own).
599
+
600
+ Refresh interval, the `tuiboard summary`/`tuiboard task` commands, the open
601
+ command, and the completion sound are all configurable from Omarchy's own
602
+ plugin settings (`manifest.json`'s schema) — no config file to hand-edit.
603
+
604
+ Uninstall: `omarchy plugin remove nazz.tuiboard`.
605
+
533
606
  ## Status
534
607
 
535
608
  See [CHANGELOG.md](CHANGELOG.md) for the full release history.
536
609
 
610
+ - **v0.13** — tuiboard + herdr: live agent state from herdr (waiting for you,
611
+ working, done, idle) with herdr's status symbols, `H` to jump to a session
612
+ in herdr or resume it in its project's workspace, sessions sorted by most
613
+ recent activity, and the terminal properly restored on quit.
614
+ - **v0.12** — the Agents zone goes multi-agent: Codex, OpenCode and Pi sessions
615
+ next to Claude Code, with a colored harness badge, the model, a harness
616
+ filter (`f`) and two-line cards when zoomed. Enter reopens a session in the
617
+ terminal and shell you're using — Windows Terminal, Ghostty, tmux, herdr,
618
+ WezTerm, Git Bash, Nushell… — instead of WezTerm only.
619
+ - **v0.11** — zoomed columns no longer clip task titles short of the available
620
+ width, a board's custom name survives external edits instead of reverting to
621
+ the filename, and the keyboard reference (`?`) got a scroll + visual restyle
622
+ grouped by section.
537
623
  - **v0.10** — a task's note, read inside tuiboard: when a task's title is a
538
624
  link, `o` shows that note's text instead of pointing at Obsidian. Works with
539
625
  plain markdown links too, so the convention needs no vault.
@@ -553,7 +639,7 @@ See [CHANGELOG.md](CHANGELOG.md) for the full release history.
553
639
  - **v0.5** — daily-driver ready. Kanban + planner + timeline + agents
554
640
  all functional, multi-select, undo, atomic file roundtrip, mouse click,
555
641
  responsive layout. Tested on Windows with WezTerm; Linux/macOS should
556
- work via the same OpenTUI binaries (untested).
642
+ work via the same OpenTUI binaries (untested at the time).
557
643
 
558
644
  ## Contributing
559
645
 
package/bin/tuiboard.ts CHANGED
@@ -85,8 +85,17 @@ const child = spawn(
85
85
  env: { ...process.env, TUIBOARD_SPLASH_DONE: "1", TUIBOARD_READY_FLAG: readyFlag },
86
86
  },
87
87
  );
88
+ // Safety net: whatever way the app ended (crash, kill), give the shell a sane
89
+ // terminal back — mouse reporting off (else moving the mouse prints
90
+ // `51;7;45M…`), focus/paste reporting off, cursor visible. Harmless when the
91
+ // app already restored everything.
92
+ const RESET_TERMINAL =
93
+ "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?1015l" + // mouse
94
+ "\x1b[?1004l\x1b[?2004l" + // focus events, bracketed paste
95
+ "\x1b[?25h"; // cursor
88
96
  child.on("exit", (code, signal) => {
89
97
  stopSplash();
98
+ try { process.stdout.write(RESET_TERMINAL); } catch { /* ignore */ }
90
99
  process.exit(code ?? (signal ? 1 : 0));
91
100
  });
92
101
  child.on("error", (err) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tuiboard",
3
- "version": "0.11.0",
4
- "description": "Terminal kanban for markdown task boards, with optional Today/Tomorrow planner, 24h agenda + calendar overlay, and a live Claude Code agent view. Use only the panels you want.",
3
+ "version": "0.13.0",
4
+ "description": "Terminal kanban for markdown task boards, with optional Today/Tomorrow planner, 24h agenda + calendar overlay, and a live view of your coding-agent sessions (Claude Code, Codex, OpenCode, Pi). Use only the panels you want.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Nazzareno Giannelli <nazzareno.giannelli@gmail.com>",
@@ -50,6 +50,7 @@
50
50
  "typecheck": "tsc --noEmit",
51
51
  "test": "bun test",
52
52
  "agents:check": "bun run src/scripts/agents-check.ts",
53
+ "agents:open": "bun run src/scripts/agents-open.ts",
53
54
  "summary": "bun run src/cli/summary.ts",
54
55
  "prepublishOnly": "bun run typecheck && bun test"
55
56
  },
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The one way out of the TUI. Destroying the OpenTUI renderer is what gives
3
+ * the terminal back — mouse tracking off, main screen, cooked input, cursor —
4
+ * and a bare `process.exit` skips it (OpenTUI only cleans up on `beforeExit`
5
+ * and its own signal/Ctrl+C handling), which left shells printing mouse
6
+ * reports like `51;7;45M` after quitting.
7
+ */
8
+
9
+ import type { CliRenderer } from "@opentui/core";
10
+
11
+ let renderer: CliRenderer | undefined;
12
+ let quitting = false;
13
+
14
+ export function registerRenderer(r: CliRenderer): void {
15
+ renderer = r;
16
+ }
17
+
18
+ /** Restore the terminal, run `cleanup`, exit. Safe to call more than once. */
19
+ export async function quitApp(cleanup: () => Promise<void>, code = 0): Promise<never> {
20
+ if (!quitting) {
21
+ quitting = true;
22
+ try {
23
+ renderer?.destroy();
24
+ // A destroy requested mid-frame completes when that frame ends.
25
+ await new Promise((r) => setTimeout(r, 50));
26
+ } catch {
27
+ // Never let a teardown error keep the process alive.
28
+ }
29
+ try {
30
+ await cleanup();
31
+ } catch {
32
+ // Same.
33
+ }
34
+ process.exit(code);
35
+ }
36
+ return new Promise<never>(() => {});
37
+ }
package/src/app.tsx CHANGED
@@ -21,8 +21,11 @@ import { appendFileSync, mkdirSync } from "node:fs";
21
21
  import { homedir } from "node:os";
22
22
  import { join } from "node:path";
23
23
 
24
- import { createMemo } from "solid-js";
25
- import { render, useKeyboard } from "@opentui/solid";
24
+ import { createEffect, createMemo } from "solid-js";
25
+ import { createCliRenderer } from "@opentui/core";
26
+ import { render, useKeyboard, useTerminalDimensions } from "@opentui/solid";
27
+
28
+ import { quitApp, registerRenderer } from "~/app-exit";
26
29
 
27
30
  import { parseArgs, type ViewKind } from "~/cli/args";
28
31
  import { loadConfig } from "~/config/loader";
@@ -84,12 +87,8 @@ if (needsOnboarding) {
84
87
  store.openBoardNew(true);
85
88
  }
86
89
 
87
- process.on("SIGINT", () => {
88
- store.dispose().finally(() => process.exit(0));
89
- });
90
- process.on("SIGTERM", () => {
91
- store.dispose().finally(() => process.exit(0));
92
- });
90
+ process.on("SIGINT", () => void quitApp(() => store.dispose()));
91
+ process.on("SIGTERM", () => void quitApp(() => store.dispose()));
93
92
 
94
93
  // ─── Responsive layout ──────────────────────────────────────────────────────
95
94
  // Auto-hide optional zones when the terminal isn't wide enough to host them
@@ -102,8 +101,7 @@ process.on("SIGTERM", () => {
102
101
  // This only reports what FITS. The store combines it with each zone's enabled
103
102
  // flag and the user's desired visibility, so F1/F2/F3 toggles persist across
104
103
  // resizes and a disabled/hidden zone is never force-shown.
105
- function applyResponsiveLayout(): void {
106
- const width = process.stdout.columns ?? 200;
104
+ function applyResponsiveLayout(width: number): void {
107
105
  // Report which zones FIT at this width. The store ANDs this with each zone's
108
106
  // enabled flag and the user's desired visibility, so a disabled or
109
107
  // intentionally-hidden zone is never force-shown just because there's room.
@@ -122,8 +120,11 @@ function applyResponsiveLayout(): void {
122
120
  { narrow: width < 100 },
123
121
  );
124
122
  }
125
- applyResponsiveLayout();
126
- process.stdout.on("resize", applyResponsiveLayout);
123
+ // Initial guess before the renderer exists; from then on the layout follows
124
+ // OpenTUI's own dimensions (see App). `process.stdout.columns` can lag behind
125
+ // the real size — e.g. a Windows Terminal tab that starts at a provisional
126
+ // size — which left zones overlapping until a manual resize.
127
+ applyResponsiveLayout(process.stdout.columns ?? 200);
127
128
 
128
129
  // Land on the Today/Tomorrow panel by default — for a daily-planning tool the
129
130
  // first question is "what's on my plate today", and that panel answers it.
@@ -170,6 +171,13 @@ function App() {
170
171
 
171
172
  useKeyboard((key) => handleKey(store, key, plannerItems().length));
172
173
 
174
+ // Same width the frame is drawn at, updated on the renderer's resize events.
175
+ const dims = useTerminalDimensions();
176
+ createEffect(() => {
177
+ const { width } = dims();
178
+ if (width > 0) applyResponsiveLayout(width);
179
+ });
180
+
173
181
  return (
174
182
  <box
175
183
  style={{
@@ -226,4 +234,6 @@ if (process.env.TUIBOARD_READY_FLAG) {
226
234
  }
227
235
  }
228
236
 
229
- await render(() => <App />, { useMouse: true });
237
+ const renderer = await createCliRenderer({ useMouse: true });
238
+ registerRenderer(renderer);
239
+ await render(() => <App />, renderer);