board

package
v1.101.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Package board implements a Kanban board for orchestrating docker agents. Each card owns an agent running in a tmux session on an isolated git worktree; the board observes and drives the agent through the control plane its run exposes with --listen. Columns form a pipeline: moving a card forward sends the destination column's prompt to its agent.

Index

Constants

View Source
const DefaultAgent = "default"

DefaultAgent is the agent ref used for projects that do not set one.

Variables

View Source
var DefaultColumns = []Column{
	{ID: "dev", Name: "Dev", Emoji: "🔨"},
	{ID: "review", Name: "Review", Emoji: "🔍", Prompt: "Review the local changes. Look for bugs, security issues, and code quality problems. Fix any issues you find."},
	{ID: "push", Name: "Push", Emoji: "🚀", Prompt: "Start by committing any remaining uncommitted files. Then rebase on top of the upstream default branch and fix any test failures and linter issues. Finally, squash all commits on this branch into a single commit with a clear and concise commit message. Push the branch to your fork (or the appropriate remote). Then use gh to open a pull request."},
	{ID: "done", Name: "Done", Emoji: "✅"},
}

DefaultColumns is the pipeline used when the user config defines none.

View Source
var ErrAgentStarting = errors.New("the agent is still starting")

ErrAgentStarting means the card's agent has not answered on its control plane yet, so there is no UI worth attaching to.

View Source
var ErrCardBusy = errors.New("cannot move a busy card forward")

ErrCardBusy rejects a forward move of a busy card. It is checked under the store lock so a watcher flipping the status concurrently cannot slip a running card past the caller's check.

View Source
var ErrCardNotFound = errors.New("card not found")

ErrCardNotFound reports a lookup of a card that does not exist (anymore).

Functions

func StatePath

func StatePath() string

StatePath returns the file the board persists its cards to.

func TmuxSocketPath

func TmuxSocketPath() (string, error)

TmuxSocketPath is the dedicated tmux socket the board runs its sessions on. The board shares the host's tmux binary but not its default server: a private socket keeps the board's server-wide options from leaking into the user's interactive tmux. The path is stable across board restarts so the controller can reattach to sessions left running on it.

Like tmux's own /tmp/tmux-<uid> convention, the socket lives in a per-user 0700 directory so other local users cannot pre-create or reach it. The directory is created and validated before tmux binds the socket.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App is the board engine: it owns the cards, the per-card agent sessions, and the configuration (projects and columns) stored in the user's global config file.

func NewApp

func NewApp(ctx context.Context, onChanged func()) (*App, error)

NewApp loads the board state and reattaches to any sessions still running in tmux. onChanged is called (from arbitrary goroutines) whenever a card changes, so the UI can refresh.

func (*App) AddProject

func (a *App) AddProject(p Project) error

AddProject validates and appends a project, persisting it to the user's global config file. The path is normalized to an absolute path (expanding a leading ~) so cards never depend on the board's working directory.

func (*App) AttachCommand

func (a *App) AttachCommand(cardID string) (*exec.Cmd, error)

AttachCommand returns the command that attaches the caller's terminal to the card's agent session. It fails with ErrAgentStarting until the agent's control plane answers, so the user never lands on a bare launch command. Before attaching, the session's header row is refreshed with the card's current title and project.

func (*App) Cards

func (a *App) Cards() []*Card

Cards returns all cards in board order.

func (*App) ColumnIndex

func (a *App) ColumnIndex(colID string) int

ColumnIndex returns the position of a column in the pipeline, or -1.

func (*App) Columns

func (a *App) Columns() []Column

Columns returns the board's pipeline.

func (*App) CreateCard

func (a *App) CreateCard(project Project, prompt string) (card *Card, err error)

CreateCard creates a card in the first column and launches its agent session. docker agent creates the isolated git worktree (named after the card) on first launch and exposes its control plane on a per-card unix socket; the board records where the worktree lives and starts watching the session. The title is a placeholder derived from the prompt, replaced when the agent emits its session_title event, so card creation is instant.

func (*App) DeleteCard

func (a *App) DeleteCard(cardID string) error

DeleteCard removes a card, kills its session, and cleans up its worktree.

func (*App) Diff

func (a *App) Diff(cardID string) (string, error)

Diff returns the card's full worktree diff against the upstream base.

func (*App) MoveCard

func (a *App) MoveCard(cardID, colID string) error

MoveCard moves a card to the given column. A move never changes the card's status: the status tracks the agent's activity, not the move. A busy card cannot move forward; the check is enforced atomically by the store so a watcher flipping the status concurrently cannot slip past it. Moving forward sends the destination column's prompt to the card's agent; the move stays observable even when the prompt cannot be delivered.

func (*App) OpenEditor

func (a *App) OpenEditor(cardID string) error

OpenEditor opens the card's worktree in the user's GUI editor: the command named by $BOARD_EDITOR, defaulting to VS Code's `code`. The editor is started detached and reaped in the background.

func (*App) Projects

func (a *App) Projects() []Project

Projects returns the configured projects.

func (*App) RemoveProject

func (a *App) RemoveProject(name string) error

RemoveProject deletes a project by name and persists the change. Existing cards keep the repo path and agent they were created with.

func (*App) SetColumnPrompt

func (a *App) SetColumnPrompt(colID, prompt string) error

SetColumnPrompt updates a column's prompt and persists it to the user's global config file.

type Card

type Card struct {
	ID       string     `json:"id"`
	Title    string     `json:"title"`
	Column   string     `json:"column"`
	Status   CardStatus `json:"status"`
	Project  string     `json:"project"`
	Agent    string     `json:"agent"`
	RepoPath string     `json:"repoPath"`
	Branch   string     `json:"branch"`
	Worktree string     `json:"worktree"`
	Session  string     `json:"session"`
	// AgentSession is the docker-agent conversation ID the card owns. It is
	// passed to `docker agent run --session` on every launch, so a session
	// recreated after the agent (or tmux) dies resumes the same conversation
	// instead of starting over.
	AgentSession string `json:"agentSession"`
}

Card is one task on the board.

type CardStatus

type CardStatus string

CardStatus tracks what a card's agent is doing.

const (
	// StatusStarting marks a card whose agent is launching but has not yet
	// answered on its control plane. The watcher replaces it with a real
	// status as soon as the agent emits events.
	StatusStarting CardStatus = "starting"
	StatusRunning  CardStatus = "running"
	StatusWaiting  CardStatus = "waiting"
	// StatusPaused marks a card whose turn is blocked on /pause. It lasts
	// until the runtime emits events again (resume) or the turn ends.
	StatusPaused CardStatus = "paused"
	// StatusError marks a card whose last turn failed. It is sticky: the
	// watcher keeps it until the next turn starts.
	StatusError CardStatus = "error"
)

func (CardStatus) Busy

func (s CardStatus) Busy() bool

Busy reports whether the card's agent cannot accept a prompt right now: it is either still starting or in the middle of a turn.

type Column

type Column struct {
	ID     string
	Name   string
	Emoji  string
	Prompt string
}

Column is one kanban column with the prompt sent to a card's agent when the card moves forward into it.

func ColumnsFromConfig

func ColumnsFromConfig(cols []userconfig.BoardColumn) []Column

ColumnsFromConfig maps user-configured columns to board columns, falling back to DefaultColumns when none are configured.

type Project

type Project struct {
	Name  string
	Path  string
	Agent string
}

Project is a repository cards can be created against, configured in the user's global config file (or through the TUI, which persists there).

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store persists the board's cards as a JSON file under the data directory. All methods are safe for concurrent use and return copies, so callers and background watchers can never alias the same Card.

func OpenStore

func OpenStore(path string) (*Store, error)

OpenStore loads the card store from path, starting empty when the file does not exist yet.

func (*Store) DeleteCard

func (s *Store) DeleteCard(id string) error

DeleteCard removes a card. Deleting a missing card is a no-op.

func (*Store) GetCard

func (s *Store) GetCard(id string) (*Card, error)

GetCard returns the card with the given id.

func (*Store) InsertCard

func (s *Store) InsertCard(c *Card) error

InsertCard appends a card to the board.

func (*Store) ListCards

func (s *Store) ListCards() []*Card

ListCards returns all cards in board order.

func (*Store) MoveCard

func (s *Store) MoveCard(id, column string, requireIdle bool) (*Card, error)

MoveCard atomically moves a card to the given column and re-inserts it at the end of the board order. The card is re-read under the lock so the move preserves the current status; when requireIdle is set, a card whose watcher concurrently flipped it to busy is rejected with ErrCardBusy. The updated card is returned.

func (*Store) UpdateCardStatus

func (s *Store) UpdateCardStatus(id string, status CardStatus) (bool, error)

UpdateCardStatus persists only the status field of a card, so background watchers holding a stale snapshot of the card cannot revert concurrent edits. It reports whether the status actually changed.

func (*Store) UpdateCardTitle

func (s *Store) UpdateCardTitle(id, title string) (bool, error)

UpdateCardTitle persists only the title field of a card. Same rationale as Store.UpdateCardStatus. It reports whether the title actually changed.

Directories

Path Synopsis
Package tui implements the full-screen Kanban TUI for `docker agent board`.
Package tui implements the full-screen Kanban TUI for `docker agent board`.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL