Skip to content

feat: add vibe list command with pluggable summary generation #408

@7tsuno

Description

@7tsuno

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/)

  1. 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'
    """
  2. Claude CLI with batch prompt: ./examples/summary/claude.sh

  3. 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

  1. 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.
  2. 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.
  3. AGE column: last commit age, or worktree creation age? Suggested: last commit age, more meaningful for active work.

Acceptance Criteria

  • vibe list runs without any config and shows BRANCH / BASE / AGE / STATUS
  • When [summary] is configured, SUMMARY column appears with command-generated values
  • Full cache hit does not invoke the command
  • Partial cache hit invokes the command with only missing worktrees
  • Command timeout is respected and falls back to cache
  • .vibe.toml hash change (including [summary]) triggers trust re-approval
  • --dirty / --clean filters work correctly
  • --base <branch> filter matches upstream / merge-base
  • --recent <duration> and --stale <duration> accept formats like 7d, 2w, 3h
  • --recent and --stale cannot be combined (error)
  • --limit <n> truncates after sort
  • --sort supports age, name, status; --reverse flips order
  • Filters compose with AND semantics
  • --json produces a valid JSON array honoring all filters/sort/limit
  • --json output includes summary field only when [summary] is configured
  • Docs (en + ja) updated under packages/docs/src/content/docs/commands/
  • examples/summary/ contains at least 3 working examples

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions