feat(tui): add /worktree command for git-worktree sessions#753
Open
sam-saffron-jarvis wants to merge 4 commits into
Open
feat(tui): add /worktree command for git-worktree sessions#753sam-saffron-jarvis wants to merge 4 commits into
sam-saffron-jarvis wants to merge 4 commits into
Conversation
Let a TUI chat session run in a git worktree (an isolated second
checkout sharing one .git) created and torn down through term-llm
instead of by hand. Implements ~/designs/term-llm/tui-worktree.md.
Core (internal/worktree): Create/List/Get/Promote/Remove/Diff over a
managed root ($XDG_DATA_HOME/term-llm/worktrees/<repo-hash>/<slug>),
detached HEAD by default, optional setup script run in the new tree,
cleanup-on-failure, and an adjective-noun slug generator.
Session binding: new worktree_dir column on the session row (schema +
migration 26 + scan/insert/update, mirroring the reasoning_effort
optional-column pattern) so a session's bound tree survives resume.
TUI surface: /worktree (alias /wt) with new/list/switch/pwd/diff/
promote/rm/shell subcommands, a cached footer indicator
("⌥ name ⎇ detached@sha ±N"), and worktree re-entry on resume. The
single-session TUI binds by chdir so every existing tool operates in
the tree with no per-tool threading; worktree_dir stays the persisted
source of truth for a future multi-session surface.
Tests: worktree package lifecycle (real git), session column
round-trip, and chat command-surface guards + a full
new→bind→diff→remove integration flow.
Replace the synchronous /worktree-new path with an async create driven by a worktree.Progress channel: a footer spinner shows live progress (git worktree add, setup script) and on completion the session binds to the new worktree via the existing chdir model (bindWorktree). - worktree.CreateOptions gains a Progress chan<- Progress; Create emits progress events (additive alongside the existing ProgressFn callback). - Model gains worktreeBusy/worktreeProgress/worktreeOpSeq; the spinner keeps ticking while a worktree op runs; renderStatusLine surfaces the live progress ahead of any footer message. - worktreeProgressMsg/worktreeCreateDoneMsg drive the update loop.
/worktree shell now suspends the TUI and runs $SHELL with cwd set to the bound worktree via tea.ExecProcess (proper terminal release/restore), returning to chat on exit. --tmux opens a tmux split (new-window fallback) and warns when run outside tmux. Command construction is factored into worktreeShellCommand for testability; worktreeShellDoneMsg reports the outcome and refreshes the footer segment.
Replace the markdown /worktree list with a reusable modal picker (DialogWorktreePicker): a synthetic root row, one row per worktree (status dot, dirty count, elided path), and a '+ new worktree…' row. /worktree and /worktree list open it. Keys route through the target's chdir binding model: - enter switches (root -> bindRoot, a worktree -> bindWorktree), - n / '+ new worktree…' create (async), - d deletes with an in-place two-press confirm (worktreeDeleteTarget), - s drops into a shell for the highlighted row. Delete uses an async removeWorktreeDir/worktreeRemoveDoneMsg that rebinds the session to root when the removed worktree was the bound one. The superseded markdown list printer is removed.
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.
What
Adds a
/worktreecommand (alias/wt) that lets a TUI chat session run in a git worktree — an isolated second checkout sharing one.git— created and torn down through term-llm instead of by hand. Implements the design in~/designs/term-llm/tui-worktree.md(TUI scope).Core:
internal/worktreeA new package owning all git logic behind a small API:
Create / List / Get / Promote / Remove / Diff.$XDG_DATA_HOME/term-llm/worktrees/<repo-hash>/<name>/.refs/heads/*clean; promote-to-branch is explicit), matching the Codex precedent.npm install, copying gitignored.env) — the difference between a usable tree and a broken one.neon-canyon) for zero-friction/worktree new.Session binding:
internal/sessionworktree_dircolumn on the session row (schema + migration 26 + scan/insert/update), mirroring the existingreasoning_effortoptional-column pattern, so a session's bound tree survives resume.TUI surface:
internal/tui/chat/worktreedispatch withnew/list/switch/pwd(+clipboard) /diff(scrollable pager) /promote/rm(dirty-guarded, two-step force) /shell(--tmuxsplit, locator fallback).⌥ neon-canyon ⎇ detached@a1b2c3 ±3— hidden entirely on the root checkout (zero git calls in the common case; TTL-debounced otherwise).Init.The single-session TUI binds by
chdir, so every existing tool operates in the worktree with no per-tool threading;worktree_dirremains the persisted source of truth for a future multi-session surface.Why
Two long-standing gaps: (1) no cheap isolation — an experimental/risky agent run dirties your real checkout, and
contain(Docker) is far heavier than needed; (2) no cheap parallelism of working trees. Git worktrees solve exactly this, and per prior art (OpenCode, Codex) the hard part — creation — belongs in core behind a stable API while the TUI stays a thin list/switch/create surface.Testing
internal/worktree: full lifecycle against real git (create/list/get/remove, setup script + dirty guard, cleanup-on-failure, promote + force-remove deletes branch, diff, duplicate-name, non-repo inert, porcelain parsing).internal/session:worktree_dirCreate/Get/Update round-trip incl. clearing.internal/tui/chat: command-surface guards + a fullnew → bind → diff → (refused) rm → force rm → rootintegration flow and switch-to-root.go build ./...,gofmt, andgo vetclean; fullgo test ./...green (49 packages, 0 failures).Notes / follow-ups (out of scope, per design)
TERM_LLM_WORKTREE_SETUPfor v1; config-file precedence (repo-local → user) is a follow-up./worktree switch <name|root>.queue_agentisolation remain out of scope.