Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,46 @@ permissions:
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

# Attach static builder-tui binaries (Linux, macOS, Windows) to the release release-please just cut;
# skills/tui/setup.sh downloads them and checks builder-tui-checksums.txt.
tui-binaries:
needs: release-please
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: skills/tui/go.mod
cache-dependency-path: skills/tui/go.sum
- name: Build
working-directory: skills/tui
env:
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
mkdir -p dist
for os in linux darwin windows; do
for arch in amd64 arm64; do
ext=""; [ "$os" = windows ] && ext=.exe
CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -trimpath \
-ldflags="-s -w -X main.version=${TAG}" -o "dist/builder-tui-$os-$arch$ext" .
done
done
(cd dist && sha256sum builder-tui-*-* > builder-tui-checksums.txt)
- name: Upload to the release
working-directory: skills/tui
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ needs.release-please.outputs.tag_name }}
run: gh release upload "$TAG" dist/* --clobber
6 changes: 5 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Validate plugin manifests, skills, and scripts
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: skills/tui/go.mod
cache-dependency-path: skills/tui/go.sum
- name: Validate plugin manifests, skills, scripts and Go tools
run: bash scripts/validate.sh
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ __pycache__/
*.pyc
usage.json
ctxwire-last.json
skills/tui/builder-tui
skills/tui/dist/
*.test
*.out
8 changes: 7 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@ Pick the skill whose lever matches the gap you observe:
| Adopt that graph-first workflow in a new repo end to end | `harness` | Both, as a loop |
| Know whether you'll blow your weekly usage limit, and which idle session to compact | `watch-limits` | Effectiveness / economics |
| Reconcile a task tracker against work already in flight and groom the rest | `intake` | Feedback into infrastructure |
| See every agent session, machine load and git cost in one place, or read it as JSON (`builder-tui status`) | `tui` | Environment |

## Working on builder

- Skills live in `skills/<name>/SKILL.md`; they are auto-discovered — no manifest
to edit. Each must open with a `---` frontmatter block carrying `name:` and
`description:` (see any existing skill).
- Run `bash scripts/validate.sh` before opening a PR. It checks manifest JSON,
skill frontmatter, and Python compilation. CI runs it too.
skill frontmatter, Python compilation, and gofmt/vet/tests plus a fixture
smoke run for Go tools (`skills/tui`). CI runs it too.
- Go tools are single static binaries: no cgo. Linux readers are rooted at a
directory so tests run against `testdata/` fixtures, never the live machine;
macOS and Windows read through gopsutil (`internal/host`) behind the same
`procfs.Host` interface, and `validate.sh` cross-builds both.
- Keep skills self-contained and composable: a new application skill should call
an existing capability skill, not re-implement it.
- Conventional Commits; release is automated via release-please.
Expand Down
8 changes: 7 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ bash scripts/validate.sh
```

It checks: manifests parse, skills have valid frontmatter, Python tools compile,
and `burn-proj.py`'s output shape is stable.
`burn-proj.py`'s output shape is stable, and Go tools (`skills/tui`) are
gofmt-clean, vetted, tested, and pass a smoke run against the fixture machine.
It needs Go for that last part (the version in `skills/tui/go.mod`; with
`GOTOOLCHAIN=auto` an older Go fetches it).

## Commit convention

Expand Down Expand Up @@ -63,5 +66,8 @@ Fully automated:
2. release-please opens/updates a **release PR** with the next version + changelog.
3. Merging the release PR tags the version, cuts a GitHub release, and bumps
`.claude-plugin/plugin.json`.
4. The same workflow then builds static `builder-tui` binaries (Linux,
macOS and Windows, amd64/arm64) and attaches them with a checksum file, which
`skills/tui/setup.sh` downloads and verifies.

Maintainers: do not bump versions manually.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ Built for [Claude Code](https://claude.com/claude-code) +
PR, existing issue), and groom the raw leftovers into issues. Annotates by
default; creates issues only when you mark a task for it. Pluggable source
adapter — bring your own MCP for other trackers.
- `/builder:tui` — Install `builder-tui`, a terminal cockpit you leave open in
a tmux window: every agent session by pane with its context use, machine load
and CPU power-limit drift, detached busy loops with the pane that leaked them,
git dirt and removable worktrees, and a ranked list of what to do with the
exact command for each. Read-only by default; destructive actions need a typed
`y`. Agents read the same state with `builder-tui status`. One static binary for
Linux, macOS and Windows; on Linux it stays under 1% of one core at idle,
including the commands it runs.
- `/builder:code-graph` — Wire a local
[codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp) graph
into a repo so agents answer structural questions (where is X, what calls Y,
Expand All @@ -62,6 +70,7 @@ Each skill is one intervention on those levers, not a standalone gadget:
| `harness` | Applies that loop to a repo end to end: baseline → provision → route → verify. |
| `watch-limits` | Optimizes for measured effectiveness — tokens and attention against your real usage limits. |
| `intake` | Turns feedback into infrastructure — captured work reconciled into durable issues. |
| `tui` | Curates the environment the agents share — one view of sessions, load and git, so the costs agents impose on each other (a dirty checkout, a leaked loop) surface with their fix. |

The framing derives from Ryan Lopopolo's
[Harness Engineering](https://github.com/lopopolo/harness-engineering) corpus
Expand Down
25 changes: 24 additions & 1 deletion scripts/validate.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
# Validate the builder plugin: manifests parse, skills have frontmatter,
# Python tools compile. Run locally before opening a PR; CI runs it too.
# Python tools compile, Go tools are formatted, vetted and tested. Run locally
# before opening a PR; CI runs it too.
set -euo pipefail
cd "$(dirname "$0")/.."

Expand Down Expand Up @@ -45,5 +46,27 @@ grep -qE 'elapsed=.* naive=.* profile_aware=.* reset_in=' <<<"$out" \
|| err "burn-proj.py output shape changed: $out"
note "✓ burn-proj smoke: $out"

echo "→ go tools"
for mod in $(find skills -name go.mod -not -path '*/testdata/*'); do
dir="$(dirname "$mod")"
if ! command -v go >/dev/null 2>&1; then err "$dir needs Go to validate (https://go.dev/dl)"; continue; fi
unformatted="$(cd "$dir" && gofmt -l .)"
[ -z "$unformatted" ] && note "✓ gofmt: $dir" || err "$dir: gofmt needed: $unformatted"
(cd "$dir" && go vet ./...) && note "✓ vet: $dir" || err "$dir: go vet failed"
(cd "$dir" && go test ./...) >/dev/null && note "✓ tests: $dir" || err "$dir: go test failed (run: cd $dir && go test ./...)"
done
# builder-tui smoke: the binary reads the fixture machine and the example config
if [ -f skills/tui/go.mod ] && command -v go >/dev/null 2>&1; then
for target in darwin/arm64 darwin/amd64 windows/amd64; do
(cd skills/tui && CGO_ENABLED=0 GOOS="${target%/*}" GOARCH="${target#*/}" go build -o /dev/null .) \
&& note "✓ builder-tui builds for $target" || err "builder-tui does not build for $target"
done
bin="$(mktemp)"
(cd skills/tui && CGO_ENABLED=0 go build -o "$bin" .)
out="$("$bin" status --text --fixtures skills/tui/testdata/machine --config skills/tui/config.example.toml)"
grep -q 'sessions: 3 (busy 1, idle 2)' <<<"$out" && note "✓ builder-tui smoke" || err "builder-tui smoke output changed: $out"
rm -f "$bin"
fi

echo
if [ "$fail" -eq 0 ]; then echo "✓ all checks passed"; else echo "✗ validation failed"; exit 1; fi
97 changes: 97 additions & 0 deletions skills/tui/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
name: tui
description: Use when you want one place to watch a machine that runs many AI coding agents — every agent session by tmux pane with its context use, machine load, detached busy loops, git worktrees, and a ranked list of what to do — or when an agent needs to read that state. Triggers: "open the cockpit", "what is loading the machine", "which session should I compact or clear", "find leaked processes", "which worktrees can I remove", "builder tui".
---

# builder tui — a cockpit for agent sessions, tmux, git and machine load

`builder-tui` is a single static Go binary (Linux, macOS, Windows) that a builder leaves open in
a tmux window. It answers, in one screen:

- **Sessions** — every agent process mapped pane → process → worktree (from
the pane the agent records in its session file, else `TMUX_PANE` in its
environment, so a re-parented process still finds its pane), busy/idle, context use and 30-minute burn, CPU of its whole process
tree, and a suggestion: `compact`, `clear`, or nothing.
- **Machine** — CPU per core, load, PSI, memory and swap, package temperature,
CPU power limits (alerting on drift), GPUs, containers and the CPU share of
container groups you name (CI runners, say).
- **Procs** — the top processes by CPU attributed to their owner (pane,
container or system), and **detached busy loops**: shells re-parented to init
or a user service manager that keep burning CPU, with the pane that started
them.
- **Git** — per checkout an agent uses: dirty paths, the default branch behind
its remote, worktrees, which ones are merged, clean and unused, and how many
cores the agents spend running git there.
- **Recs** — a ranked list, each with the evidence behind it and the exact
command that resolves it.
- **History** — 24 hours of CPU, load, pressure, temperature, memory and swap
from its own log (plus any CSV you point it at).

It is read-only by default. Every action prints its exact command first; `Enter`
runs a non-destructive one, and anything destructive (kill, remove a worktree,
type into a session) needs `x` and then a typed `y`.

## Install

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/tui/setup.sh"
```

`setup.sh` downloads the release binary for this machine and checks its
SHA-256, or builds from the plugin checkout when Go is installed. It installs to
`~/.local/bin/builder-tui` (override with `BUILDER_BIN_DIR`). It runs on Linux
and macOS; on Windows, run it inside WSL, or download
`builder-tui-windows-amd64.exe` (or `-arm64.exe`) from the release page.

## Run

```bash
tmux new-window -d -n cockpit builder-tui
```

Keys: `1`–`6` or `tab` switch panels, `↑↓` select, `Enter` the panel's safe
action (jump to a pane, open a shell in a worktree), `x` the destructive one,
`r` rescans git, `q` quits.

It runs at `nice 19`. It reads cheap counters every 2 s, the process table every
10 s, `docker` and `nvidia-smi` every minute, and git every 5 minutes. It
re-checks merged worktrees at most every 2 h, because git refuses a dirty removal
anyway. The renderer is capped at 15 fps. On a workstation with ~1,100 processes,
25 agent sessions and ~190 worktrees it used 0.84% of one core over 10 minutes,
child commands included.

## For agents

```bash
builder-tui status # JSON: sessions, machine, alerts, git, recommendations
builder-tui status --text # the same as a short summary with exact commands
```

`status` reads the snapshot a running cockpit writes to
`~/.local/state/builder/tui.json` each sample; with no cockpit running it samples
on its own. Recommendations carry `primary` (safe) and `secondary` (destructive)
actions as argv steps. Ask the user before running a destructive step, and
check its `guards` right before each step: the same pid with the same
`start_ticks`, a worktree still clean and unused, a session still idle. The
cockpit does this itself and skips a step whose guard no longer holds.

## Configure

Machine goals live in a local file, never in a repository:
`~/.config/builder/tui.toml`. Every key is optional; see
[`config.example.toml`](config.example.toml) for all of them.

## Requirements

tmux for the Sessions panel. Optional: `docker` (containers), `nvidia-smi`
(GPUs), `gh` (merged pull requests, so squash merges count as merged).

| | Linux | macOS | Windows |
|---|---|---|---|
| Sessions, Git, Recs, History | yes | yes | in WSL (no native tmux) |
| CPU, memory, swap, network, disk, load, processes | yes | yes | yes |
| Pressure (PSI), power limits, temperature, container CPU | yes | no | no |

Linux reads `/proc` and `/sys` directly. macOS and Windows read through the
system's own APIs, which cost more per process scan, so the scan stays on its
10-second cadence. Under WSL the cockpit is the Linux build.
50 changes: 50 additions & 0 deletions skills/tui/config.example.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# builder-tui — copy to ~/.config/builder/tui.toml and keep it local.
# Every key is optional; the values below are the defaults unless noted.

interval = "2s" # cheap counters; processes every 3x, git every git.interval
agents = ["claude"] # process names that are agent sessions
latency_target = "" # host:port to time a TCP connect against every 10 s, e.g. "example.com:443"

[sessions]
compact_at = 85 # context % at which a session should compact
clear_idle = "30m" # idle this long while holding clear_min_pct context: clear
clear_min_pct = 20
clear_stale = "24h" # idle this long: clear whatever it holds
# [sessions.windows] # context window by model-name prefix; default 200k, or
# "some-model" = 1000000 # 1M when the session's model setting ends in [1m] or it passed 200k

[alerts] # a zero threshold is off
temp_over_c = 95
load1_over = 0
load1_for = "2m" # the condition must hold this long
psi_cpu_over = 0 # PSI cpu "some avg10", percent
psi_cpu_for = "2m"
mem_available_under_gb = 0
swap_over_gb = 0 # swap held although RAM is free:
swap_when_available_over_gb = 0
power_limits_w = [] # expected CPU package limits, long term then short term;
# empty alerts when a limit changes from its value at startup

[orphans] # detached busy loops
cpu_over = 80 # percent of one core
for = "30s"
min_age = "2m"
commands = ["sh", "bash", "zsh", "dash", "fish"] # empty: any command

# [[groups]] # CPU share of containers whose name matches
# name = "CI runners"
# container = "runner" # regular expression

[git]
interval = "5m"
# main = "main" # default: the branch the remote's HEAD names
remote = "origin"
repos = [] # checkouts to watch besides the ones sessions use

[history]
log = "" # default ~/.local/share/builder/tui-history.csv; "-" disables
source = "" # another CSV with a ts column to chart alongside

[hooks]
alert = [] # argv run on each new alert, title and evidence appended,
# e.g. ["notify-send", "builder-tui"]
38 changes: 38 additions & 0 deletions skills/tui/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module github.com/Lutherwaves/builder/skills/tui

go 1.26.8

require (
charm.land/bubbletea/v2 v2.0.10
charm.land/lipgloss/v2 v2.0.6
github.com/BurntSushi/toml v1.6.0
github.com/NimbleMarkets/ntcharts/v2 v2.7.0
github.com/charmbracelet/x/ansi v0.11.8
github.com/shirou/gopsutil/v4 v4.26.9
)

require (
charm.land/bubbles/v2 v2.2.1 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260928045949-bbf040aedf25 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/charmbracelet/x/termios v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/ebitengine/purego v0.11.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/lrstanley/bubblezone/v2 v2.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mattn/go-runewidth v0.0.30 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/power-devops/perfstat v0.0.0-20260805114148-88456608a4f6 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/tklauser/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect
github.com/xo/terminfo v1.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sync v0.23.0 // indirect
golang.org/x/sys v0.48.0 // indirect
)
Loading
Loading