feat: add docker agent board, a Kanban TUI for orchestrating agents#3451
Draft
dgageot wants to merge 53 commits into
Draft
feat: add docker agent board, a Kanban TUI for orchestrating agents#3451dgageot wants to merge 53 commits into
dgageot wants to merge 53 commits into
Conversation
3119838 to
fdb2835
Compare
Member
|
Didn't look at the code, one note: i'm using the tasks toolset a lot, can you make sure that the board can be reused? I would want to have a board for the tasks at some point |
Assisted-By: claude-opus-4-5
Any other stat failure (permissions, corrupt path) was silently hidden as "no changes"; surface it instead.
An empty path silently validated against the board's working directory and a relative one depended on it. Reject blank paths, expand a leading ~, and store absolute paths.
A card whose column was dropped from the configured pipeline silently disappeared from the TUI while its agent kept running: impossible to attach, move, or delete. Bucket such cards into the first column.
A watcher's background resume could race a prompt-bearing relaunch and kill the session it had just created, dropping the prompt. Relaunches now run under a lock, and a plain resume skips sessions a concurrent relaunch already resurrected.
A predictable socket path directly under /tmp let other local users pre-create or interfere with it. Follow tmux's own /tmp/tmux-<uid> convention.
Removes the concrete-type assertions at dialog open sites: every dialog now implements Init and a single openDialog helper installs it.
First-run users saw six empty columns with no guidance; the overlay explains the model and points at the n/p/? keys.
Makes it easy to find the branch to check out or push without opening the agent session.
The dialog was rebuilt from scratch on every change, resetting the cursor to the first entry.
Session titles arrive from agent-controlled control planes and diffs are repository content: both could embed ANSI/OSC sequences (clipboard writes, title changes, screen manipulation). Strip terminal controls from titles, project names, flash messages, and diff content before rendering.
SendPrompt runs outside the watcher, so a delete racing an in-flight prompt delivery could resurrect the tmux session (and worktree) of a card already gone from the store. relaunch now aborts when the card no longer exists, and DeleteCard kills the session under the same lock.
The per-user socket dir under /tmp was created best-effort: a path pre-created by another local user would have been used silently. The directory is now validated once per process (real directory, owned by the current user, 0700) and tmux is never started when the checks fail.
Drop the Simplify and Fix columns. Also stop freezing the built-in columns into the config file on every save: the columns section is only persisted when it differs from the defaults, so future default changes reach users who never customized their pipeline.
All of a project's cards share an accent color (border and project badge), matching the project chip in the new-card dialog, so each project's work is recognizable at a glance. Cards whose project was removed hash to a stable color.
Enter still creates the card; the newline binding moves from shift+enter to cmd+enter (super+enter in CSI-u terms), with ctrl+j as a fallback for terminals without the Kitty keyboard protocol.
The prompt textarea now spans most of the screen (up to 24 rows, width 100): long task descriptions are the norm, not the exception. When only one project is configured the selector row disappears entirely; the dialog title still names the target project.
Adding a project now starts in a small directory picker (type to filter, enter descends, backspace walks up, git repositories are marked) instead of a bare text field. The picked directory pre-fills the form's name and path; ctrl+o re-opens the browser from the form.
Board dialogs now build their titles and help-key lines through the shared dialog content builder (pkg/tui/dialog), so they render exactly like every other docker-agent dialog: centered title, highlighted keys with muted descriptions, centered help row.
lipgloss Layer.Draw ignores a layer's X/Y/Z — positioning lives in the Compositor — so each Compose painted the layer's content at the origin over the whole canvas: dialogs erased the board behind them. Route the layers through a Compositor and pin the regression with a test.
Enter now inserts a newline (multiline prompts are the norm) and cmd+enter submits the card, with ctrl+s as a fallback for terminals without the Kitty keyboard protocol.
View called App.Projects() — a config lock plus slice rebuild — on every render frame (120ms while any card is busy). The model now keeps a snapshot, refreshed on reload and after project add/delete, which also keeps project colors and the header count fresh without waiting for the next card change.
Same submit binding as the new-card dialog (ctrl+s still works).
Wheel events move the selection through the column under the cursor; the scroll window already follows the selection. The diff viewport keeps its native wheel scrolling.
Columns with starting or running agents get an animated spinner and count next to the card count, so activity is visible even when the busy cards are scrolled out of view.
Runs $BOARD_EDITOR (default: code) on the selected card's worktree, matching the web board's editor button.
Project names, paths, and agent refs come from the config file (or are typed into dialogs); strip terminal controls before rendering them in chips, titles, project rows, and the footer, like every other untrusted string.
Scrolling over the header or footer changed the selection. Column hit-testing now bounds Y to the board area and is shared between cardAt and handleWheel so the math cannot drift apart.
upstreamBase blindly assumed <remote>/main when nothing resolved, so git merge-base failed and the diff view errored in local-only repos. Fall back to the local default branch, then HEAD (+ tests).
The projects dialog now shows each project in the same color its cards carry on the board.
The diff is a snapshot and the agent may still be working; r reloads it in place, preserving the scroll position.
Listing (hidden dirs and files excluded, git detection), descend and walk-up navigation, filtering, picking, and cancellation.
The quit binding now merges the user's remapped global quit (from the config file, resolved through the main TUI's keymap) instead of hard-coding ctrl+c — inside dialogs too. Help additionally answers to the main TUI's f1/ctrl+h.
Matches the main TUI's viewer dialogs: a scrollbar column next to the viewport (reserved even when the content fits, so refreshes don't shift the layout) and the scroll percentage in the help row.
The delete confirmation now runs on dialog.DefaultConfirmKeyMap (Y/N, case-insensitive) like every other docker-agent confirmation dialog; enter still confirms and esc still cancels.
ctrl+z (or the user's remapped suspend binding) suspends the board to the shell; fg resumes it.
Keys in bold secondary, descriptions in the shared dialog help style, instead of ad-hoc badge colors.
Pressing enter repeatedly stacked tea.ExecProcess commands, replaying an attach after every detach. A guard now holds from the readiness probe until the session detaches or the probe fails.
Colorizing and holding an unbounded diff in the viewport could freeze the UI; larger diffs are cut at a line boundary with a notice.
A restored scroll offset (diff refresh) was applied while the viewport height was still zero, so it clamped against the wrong bound; a shrunken diff or a taller terminal could then render blank content past the real bottom. Re-clamp once the real dimensions are known.
The per-card unix socket file lingered in ~/.cagent/run forever.
Two boards over the same state file would each run per-card watchers and race one another relaunching agent sessions. NewApp now takes an exclusive flock on the state file; a second instance fails fast with a clear message. The OS drops the lock on exit, so a crash never leaves a stale lock.
The textarea was a fixed 10 rows; it now adapts between 4 and 16 rows like the new-card dialog.
User-defined columns may omit the emoji; the header and prompt-editor title no longer show a leading double space (and column names/emojis are sanitized like other config strings).
Typing 'exec docker-agent …' into the user's interactive shell was fragile: it depends on the shell supporting exec, pollutes shell history, and a slow shell startup could swallow or garble the input. respawn-pane -k replaces the pane process directly (tmux runs the command via /bin/sh), keeping the same remain-on-exit dead-pane semantics. Verified live: create card, agent starts, relaunch works.
Columns that did not fit were clipped off screen and unreachable by mouse. The board now windows the pipeline around the selected column, with ◀/▶ hidden-column counts in the header; hit-testing follows the window (+ tests). Verified live on a 55-column terminal.
Assisted-By: Claude
fdb2835 to
6938695
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Working on several agent tasks in parallel means juggling terminals, worktrees, and half-remembered session IDs. This adds
docker agent board, a full-screen Kanban TUI that turns that workflow into cards on a pipeline: each card launches an agent in a tmux session on an isolated git worktree, and moving a card forward (Dev → Review → Push → Done) delivers the destination column's prompt to its agent. It is a TUI port of the experimental web-based board project, built natively on docker-agent's own primitives (--worktree,--session,--listen).The engine (
pkg/board) keeps one watcher goroutine per card tailing the agent's control-plane event stream over a per-card unix socket, mirroring title and running/waiting/paused/failed status into a JSON store, and relaunching the tmux session (resuming the same conversation and worktree) if the agent dies. Prompt delivery, busy-move rejection, and delete/relaunch races are all serialized through the store and a relaunch lock. Projects and column prompts live in the user's global config file (~/.config/cagent/config.yaml, newboard:section) and are editable from the TUI, which follows the main TUI's standards: shared dialog chrome, theme, keymap (remapped quit/suspend bindings are honored), scrollbars, and mouse support. Pressing enter attaches the terminal to a card's agent;ctrl+qdetaches back to the board.The board is single-instance (flock on the state file), sanitizes agent-controlled strings before rendering, runs its tmux sessions on a private validated socket, and requires tmux (it fails with a clear message otherwise; Windows builds compile but the command is effectively unix-only). Docs live in
docs/features/board/.