The Commit cell sliced `&head[..8]` while `--format=json`'s `short_sha` carried git's `%h`, so one commit read `1b9f1d96` in the table and `1b9f1d9` in JSON, and `core.abbrev` reached only the JSON. #3675 gave a detached row's Branch cell the same slice, so the disagreement showed up twice on one row. `ListItem::short_sha` becomes the only abbreviation of `head` anywhere: the Commit cell, a detached row's Branch cell, the statusline, and JSON all render it. `abbreviated_head()` is gone. ## Column widths `COMMIT_HASH_WIDTH = 8` is gone. The Commit column and the Branch column's detached budget both measure the SHAs they will render, so `core.abbrev = 12` no longer truncates mid-hash and the default 7 stops reserving a column nothing fills — the freed character goes to Message. ## Latency `collect()` folds `%h` onto the rows before layout instead of after the skeleton. The batch carrying it already gates the skeleton for `%ct` sort order, so this is a map lookup rather than new I/O, and both cells are identity columns with no placeholder — they still paint in the first frame. Measured on a 40-worktree / 400-branch fixture: git subprocess counts are identical (5 pre-skeleton, 108 for the full run). Pre-skeleton wall time is unchanged; running both binaries in each order, the sign of the difference follows run order rather than the binary (+1.5 ms with this branch second, −0.5 ms with it first), so the residual sits inside drift. ## Behavior change Where the commit-details batch fails, the Commit cell is now empty rather than a slice of a SHA git refused. Age and Message already report that failure the same way, under the same warning, and two snapshots show it. `render_text_cell` also stops styling empty text, so a blank cell no longer emits an escape pair around nothing. ## Reading the diff 160 files, but the hand-written part is +91/−79 in `src/commands/list/` plus a +71 test. The rest is generated. The docs mirrors and help snapshots are symmetric. Of the snapshot lines, content is +799/−799 — every changed line a 1-for-1 hash swap — while +1396 is insta `env:` metadata refreshing on the 128 snapshots this happens to touch. `test_list_abbreviated_sha_follows_git` pins the invariant: the table's hash equals JSON's `short_sha` at git's default and at `core.abbrev = 12`, and a longer prefix is ruled out. It fails against the old fixed slice. > _This was written by Claude Code on behalf of max-sixty_
6.9 KiB
+++ title = "Agent Integration" description = "Worktrunk plugins for Claude Code, Codex, OpenCode, and Gemini CLI: a configuration skill, wt list activity tracking, and Claude-only worktree isolation." weight = 23
[extra] group = "Reference" +++
Worktrunk ships a plugin for each supported agent CLI. What a plugin provides depends on the hooks that CLI exposes:
| Capability | Claude Code | Codex | OpenCode | Gemini CLI |
|---|---|---|---|---|
| Configuration skill | ✓ | ✓ | ✓ | |
Activity tracking (🤖/💬 in wt list) |
✓ | ✓ | ✓ | ✓ |
| Worktree isolation | ✓ | |||
/wt-switch-create command |
✓ |
The configuration skill is documentation the agent reads to help set up LLM commits, hooks, and troubleshooting. Activity tracking shows which worktrees have running sessions. Worktree isolation needs worktree-lifecycle hooks and /wt-switch-create needs session working-directory switching — both Claude Code-only, so Codex, OpenCode, and Gemini users invoke wt switch --create and wt remove directly. Codex tracks activity through its own Stop and SessionEnd hooks.
Installation
Claude Code
{{ terminal(cmd="wt config plugins claude install") }}
Manual equivalent:
{{ terminal(cmd="claude plugin marketplace add max-sixty/worktrunk|||claude plugin install worktrunk@worktrunk") }}
Codex
{{ terminal(cmd="wt config plugins codex install") }}
This configures the Worktrunk marketplace in Codex. Then run /plugins in Codex and install Worktrunk from the marketplace. Manual equivalent:
{{ terminal(cmd="codex plugin marketplace add max-sixty/worktrunk") }}
To remove the marketplace entry, run wt config plugins codex uninstall. Already-installed plugins are left unchanged.
OpenCode
{{ terminal(cmd="wt config plugins opencode install") }}
This writes the activity-tracking plugin to OpenCode's global plugins directory, ~/.config/opencode/plugins/worktrunk.ts (honoring $OPENCODE_CONFIG_DIR and $XDG_CONFIG_HOME). wt config plugins opencode uninstall removes it.
Gemini CLI
{{ terminal(cmd="gemini extensions install https://github.com/max-sixty/worktrunk") }}
Gemini loads the extension natively from the repository, so there is no wt wrapper. gemini extensions uninstall worktrunk removes it.
Configuration skill
With the /worktrunk skill, the agent can help with:
- Setting up LLM-generated commit messages
- Adding project hooks (pre-start, pre-merge, pre-commit)
- Configuring worktree path templates
- Fixing shell integration issues
Claude Code is designed to load the skill automatically when it detects worktrunk-related questions.
Activity tracking
The Claude Code, Codex, OpenCode, and Gemini plugins track agent sessions with status markers in wt list:
{% terminal(cmd="wt list") %} wt list Branch Status HEAD± main↕ main…± Remote⇅ Path Commit Age Message @ main ^⇡ ⇡1 . 33323bc 1d Initial commit
- feature-api ↑ 🤖 ↑1 +1 ../repo.feature-api 70343f0 1d Add REST API endpoints
- review-ui ? ↑ 💬 ↑1 +1 ../repo.review-ui a585d6e 1d Add dashboard component
- wip-docs ? – ../repo.wip-docs 33323bc 1d Initial commit
○ Showing 4 worktrees, 2 with changes, 2 ahead {% end %}
- 🤖 — agent is working
- 💬 — agent is waiting or idle
All four plugins clear the marker when a session ends. A stale marker can remain if the agent process is killed before its session-end hook runs. In every case, wt config state marker clear removes a marker manually.
Manual status markers
Set status markers manually for any workflow:
{% terminal() %} wt config state marker set "🚧" # Current branch wt config state marker set "✅" --branch feature # Specific branch git config worktrunk.state.feature.marker '{"marker":"💬","set_at":0}' # Direct {% end %}
Worktree isolation (Claude Code only)
Claude Code agents can run in isolated worktrees (isolation: "worktree"). By default, Claude Code creates these with git worktree add. The plugin's WorktreeCreate and WorktreeRemove hooks route this through wt switch --create and wt remove instead, so worktrees created by agents get worktrunk's naming conventions, hooks, and lifecycle management.
/wt-switch-create command (Claude Code only)
/wt-switch-create [<branch>] [<repo>] [-- <task>] starts a task in a fresh worktree without leaving the session: it creates the worktree, switches into it, and runs the task (all arguments optional). The worktree shows up in wt list; merge or remove it with wt merge / wt remove.
Statusline (Claude Code only)
wt list statusline --format=claude-code outputs a single-line status for the Claude Code statusline. Claude Code runs it in the background, which is what makes the occasional 1–2 second CI fetch invisible.
~/w/myproject.feature-auth !🤖 @+42 -8 ↑3 ⇡1 #3035 Opus 🌔 65% 1.4×(10am–3pm)
Worktree state comes from the same cells wt list renders; Claude Code's stdin JSON adds the model, the 🌔 65% context gauge, and the rate-limit pace notice. wt list statusline documents every segment, how the links behave, and the JSON fields behind them.
Add to ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "wt list statusline --format=claude-code"
}
}