Summary
Add a new vibe list command that shows all worktrees with useful per-worktree context, including an optional user-defined "summary" column. The summary is produced by an arbitrary external command configured in .vibe.toml, so users can use AI (claude/gemini/ollama), simple git commands, or any custom logic.
Problem
There is no easy way to see what each worktree is for at a glance. git worktree list shows path + HEAD but not the branch's intent or recent activity. When multiple worktrees accumulate, users can't tell "which one was I doing the button fix in?" without cd-ing into each.
Proposed Solution
New command: vibe list
Displays all worktrees with the following columns:
| Column |
Source |
| BRANCH |
Current branch name |
| BASE |
Upstream / merge-base branch |
| AGE |
Time since last commit on branch |
| STATUS |
clean / M <n> for dirty worktrees |
| SUMMARY |
Output of user-configured [summary] command (only if configured) |
Example:
$ vibe list
BRANCH BASE AGE STATUS SUMMARY
my-feature develop 2h clean Button hover a11y fix
scratch/20260423-143052 develop 4h M 3 Refactoring config loader
fix-bug-123 develop 1d clean Invalidate stale cache
If [summary] is not configured, the SUMMARY column is omitted entirely (opt-in).
Filters & sort
Flags for narrowing and ordering the output:
| Flag |
Behavior |
--dirty |
Only worktrees with uncommitted changes |
--clean |
Only worktrees with no changes |
--base <branch> |
Only worktrees based on the given branch (e.g. --base develop) |
--recent <duration> |
Only worktrees whose last commit is within the duration (e.g. --recent 1d, --recent 1w) |
--stale <duration> |
Only worktrees whose last commit is older than the duration (e.g. --stale 7d, --stale 2w) |
--limit <n> |
Show only the top N after sort |
--sort <key> |
Sort order: age (default, newest first), name, status |
--reverse |
Reverse sort order |
--json |
Output as JSON instead of a table (see Output format) |
Default ordering: most-recently-committed first (answers "what was I just doing?" naturally).
Filters combine with AND semantics (e.g., vibe list --dirty --base develop). --recent and --stale cannot be combined (error).
Example use cases:
vibe list --recent 1d # touched today
vibe list --limit 5 # 5 most recent
vibe list --recent 1w --dirty # touched this week and still dirty
vibe list --stale 30d # abandoned for a month — clean candidates
vibe list --json | jq '...' # pipe into scripts
Output format
Default output is a human-readable table (column layout shown above). With --json, the output is a JSON array suitable for scripting:
[
{
"name": "my-feature",
"path": "/Users/.../worktrees/my-feature",
"branch": "my-feature",
"base": "develop",
"head": "abc123def456...",
"last_commit_at": "2026-04-23T14:30:52+09:00",
"status": "clean",
"dirty_files": 0,
"summary": "Button hover a11y fix"
},
{
"name": "scratch/20260423-143052",
"path": "/Users/.../worktrees/scratch-20260423-143052",
"branch": "scratch/20260423-143052",
"base": "develop",
"head": "def456abc789...",
"last_commit_at": "2026-04-23T12:15:10+09:00",
"status": "dirty",
"dirty_files": 3,
"summary": "Refactoring config loader"
}
]
- Filters, sort, and
--limit apply to --json output identically
summary is omitted when [summary] is not configured
- Timestamps use ISO 8601 with timezone offset (machine-parseable)
status is "clean" or "dirty"; dirty_files is the count (0 when clean)
- Schema is stable; additive changes only in future versions
Configuration
.vibe.toml in the main worktree:
[summary]
command = "..." # shell command; runs once per list invocation (batch)
timeout_seconds = 30 # optional, default 30
Only the main worktree's .vibe.toml is consulted. No per-worktree config.
Command contract (batch)
vibe invokes the command once per vibe list (not per-worktree), passing all cache-miss worktrees together.
stdin (vibe → command):
{
"worktrees": [
{
"name": "my-feature",
"path": "/Users/.../worktrees/my-feature",
"base": "develop",
"head": "abc123..."
}
]
}
stdout (command → vibe):
{
"my-feature": "Button hover a11y fix"
}
- Keys are worktree
name; values are single-line summaries
- Missing keys → displayed as empty (not cached)
- Non-zero exit → previous cache used if present, else empty, with warning indicator
- stdout is parsed as JSON; anything else is an error
Cache
- Location:
~/.cache/vibe/summaries/<repo-hash>.json
- Per-worktree cache key:
HEAD sha + hash of uncommitted changes
- On cache hit: value reused, worktree omitted from stdin payload
- On full hit (all worktrees cached): command not invoked at all
- On cache miss: worktree included in stdin, stdout value written back to cache
This keeps steady-state vibe list near-instant even with AI-backed summary generators.
Trust integration
[summary].command is an arbitrary shell command, so it goes through the existing SHA-256 .vibe.toml trust mechanism. First run (or after command change) requires vibe trust.
Examples (to ship under examples/summary/)
-
Last commit subject (no AI):
command = """
jq -r '.worktrees[] | "\\(.name)\\t\\(.path)"' \\
| while IFS=$'\\t' read name path; do \\
msg=$(git -C "$path" log -1 --pretty=%s); \\
jq -n --arg n "$name" --arg m "$msg" '{($n): $m}'; \\
done | jq -s 'add'
"""
-
Claude CLI with batch prompt: ./examples/summary/claude.sh
-
Static note file: cat .vibe/note.txt style
Out of Scope
- Prompt presets / built-in AI providers — vibe stays provider-agnostic; users supply their own command
Open Questions
- Should the
command be a shell string or an argv array? Shell string allows pipes (| jq | ...) but requires quoting care. Suggested: shell string, since the examples benefit from pipes.
- How to compute the dirty hash in the cache key —
git diff hash, file mtimes, or status porcelain output? Suggested: git status --porcelain=v1 hash for determinism.
- AGE column: last commit age, or worktree creation age? Suggested: last commit age, more meaningful for active work.
Acceptance Criteria
Summary
Add a new
vibe listcommand that shows all worktrees with useful per-worktree context, including an optional user-defined "summary" column. The summary is produced by an arbitrary external command configured in.vibe.toml, so users can use AI (claude/gemini/ollama), simple git commands, or any custom logic.Problem
There is no easy way to see what each worktree is for at a glance.
git worktree listshows path + HEAD but not the branch's intent or recent activity. When multiple worktrees accumulate, users can't tell "which one was I doing the button fix in?" without cd-ing into each.Proposed Solution
New command:
vibe listDisplays all worktrees with the following columns:
clean/M <n>for dirty worktrees[summary] command(only if configured)Example:
If
[summary]is not configured, the SUMMARY column is omitted entirely (opt-in).Filters & sort
Flags for narrowing and ordering the output:
--dirty--clean--base <branch>--base develop)--recent <duration>--recent 1d,--recent 1w)--stale <duration>--stale 7d,--stale 2w)--limit <n>--sort <key>age(default, newest first),name,status--reverse--jsonDefault ordering: most-recently-committed first (answers "what was I just doing?" naturally).
Filters combine with AND semantics (e.g.,
vibe list --dirty --base develop).--recentand--stalecannot be combined (error).Example use cases:
Output format
Default output is a human-readable table (column layout shown above). With
--json, the output is a JSON array suitable for scripting:[ { "name": "my-feature", "path": "/Users/.../worktrees/my-feature", "branch": "my-feature", "base": "develop", "head": "abc123def456...", "last_commit_at": "2026-04-23T14:30:52+09:00", "status": "clean", "dirty_files": 0, "summary": "Button hover a11y fix" }, { "name": "scratch/20260423-143052", "path": "/Users/.../worktrees/scratch-20260423-143052", "branch": "scratch/20260423-143052", "base": "develop", "head": "def456abc789...", "last_commit_at": "2026-04-23T12:15:10+09:00", "status": "dirty", "dirty_files": 3, "summary": "Refactoring config loader" } ]--limitapply to--jsonoutput identicallysummaryis omitted when[summary]is not configuredstatusis"clean"or"dirty";dirty_filesis the count (0 when clean)Configuration
.vibe.tomlin the main worktree:Only the main worktree's
.vibe.tomlis consulted. No per-worktree config.Command contract (batch)
vibe invokes the command once per
vibe list(not per-worktree), passing all cache-miss worktrees together.stdin (vibe → command):
{ "worktrees": [ { "name": "my-feature", "path": "/Users/.../worktrees/my-feature", "base": "develop", "head": "abc123..." } ] }stdout (command → vibe):
{ "my-feature": "Button hover a11y fix" }name; values are single-line summariesCache
~/.cache/vibe/summaries/<repo-hash>.jsonHEAD sha + hash of uncommitted changesThis keeps steady-state
vibe listnear-instant even with AI-backed summary generators.Trust integration
[summary].commandis an arbitrary shell command, so it goes through the existing SHA-256.vibe.tomltrust mechanism. First run (or after command change) requiresvibe trust.Examples (to ship under
examples/summary/)Last commit subject (no AI):
Claude CLI with batch prompt:
./examples/summary/claude.shStatic note file:
cat .vibe/note.txtstyleOut of Scope
Open Questions
commandbe a shell string or an argv array? Shell string allows pipes (| jq | ...) but requires quoting care. Suggested: shell string, since the examples benefit from pipes.git diffhash, file mtimes, or status porcelain output? Suggested:git status --porcelain=v1hash for determinism.Acceptance Criteria
vibe listruns without any config and shows BRANCH / BASE / AGE / STATUS[summary]is configured, SUMMARY column appears with command-generated values.vibe.tomlhash change (including[summary]) triggers trust re-approval--dirty/--cleanfilters work correctly--base <branch>filter matches upstream / merge-base--recent <duration>and--stale <duration>accept formats like7d,2w,3h--recentand--stalecannot be combined (error)--limit <n>truncates after sort--sortsupportsage,name,status;--reverseflips order--jsonproduces a valid JSON array honoring all filters/sort/limit--jsonoutput includessummaryfield only when[summary]is configuredpackages/docs/src/content/docs/commands/examples/summary/contains at least 3 working examples