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.
- package/.tuiboard/config.example.yaml +33 -9
- package/CHANGELOG.md +127 -0
- package/README.md +116 -30
- package/bin/tuiboard.ts +9 -0
- package/package.json +3 -2
- package/src/app-exit.ts +37 -0
- package/src/app.tsx +23 -13
- package/src/config/loader.ts +40 -7
- package/src/input/handleKey.ts +115 -52
- package/src/input/herdr-open.ts +132 -0
- package/src/input/open-session.ts +452 -0
- package/src/store/agent-adapters/claude-code.ts +349 -0
- package/src/store/agent-adapters/codex.ts +406 -0
- package/src/store/agent-adapters/index.ts +17 -0
- package/src/store/agent-adapters/opencode.ts +196 -0
- package/src/store/agent-adapters/pi.ts +254 -0
- package/src/store/agents.ts +182 -342
- package/src/store/herdr.ts +272 -0
- package/src/store/index.ts +48 -8
- package/src/ui/AgentRow.tsx +188 -50
- package/src/ui/AgentsBar.tsx +9 -3
- package/src/ui/Modal.tsx +63 -6
- package/src/ui/agent-line.ts +95 -0
- package/src/views/AgentsOnly.tsx +12 -5
|
@@ -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
|
|
46
|
+
# agents: on # live agent sessions view (F3)
|
|
47
47
|
|
|
48
|
-
#
|
|
49
|
-
#
|
|
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.
|
|
52
|
-
#
|
|
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 {
|
|
69
|
-
#
|
|
70
|
-
#
|
|
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}";
|
|
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
|
|
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
|
-

|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
104
|
-
don't want, add a `zones:` block setting it
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
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}
|
|
167
|
-
# Default: 'cd "{cwd}" &&
|
|
168
|
-
# copy_resume_command: 'cd "{cwd}";
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
197
|
-
|
|
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
|
|
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` |
|
|
429
|
-
| `c` | Copy a one-paste `cd … && claude --resume <id>`
|
|
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.
|
|
4
|
-
"description": "Terminal kanban for markdown task boards, with optional Today/Tomorrow planner, 24h agenda + calendar overlay, and a live Claude Code
|
|
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
|
},
|
package/src/app-exit.ts
ADDED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
process.stdout.
|
|
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
|
-
|
|
237
|
+
const renderer = await createCliRenderer({ useMouse: true });
|
|
238
|
+
registerRenderer(renderer);
|
|
239
|
+
await render(() => <App />, renderer);
|