A minimalist terminal multiplexer with first-class AI context.
Katu replaces cryptic multiplexer flags with semantic verbs: start, join, list, stop. Sessions survive disconnects. Each session exposes its own AI context file through KATU_AI_CONTEXT, so your AI assistant switches context when you switch sessions — without Katu ever talking to an AI API itself.
Status: early development. Sessions, attach/detach, pane splits, windows, capture, AI context injection, AI agent management, session persistence (
katu resurrect), and copy mode work. See PRD.md for the product vision and docs/manual.md for the complete command, keybinding, and configuration reference.
Katu is Tupi-Guarani for "good," "proper," or "well made" — the standard a tool has to meet before it earns a place in a terminal you live in all day. Katu started as a personal itch: tmux does everything, but I wanted something smaller, built around the two things I actually do — juggle panes and run AI agents — with nothing else demanding to be configured first. It's built for that use, but nothing about it is private; anyone who wants a minimalist multiplexer with agent-awareness built in from the start is welcome to it.
The very first katu you ever run writes a default katu.toml, prints
three lines confirming it, and drops you straight into a main session —
no setup wizard. Every run after that is just this:
katu # attach to the last session, or create "main"
katu start api # create and attach to session "api"
katu join api # attach to an existing session
katu list # show all sessions
katu split # split the current pane side by side
katu split --horizontal # split the current pane top/bottom
katu window new # open a new window (tab)
katu stop api # kill one session
katu stop --all # kill the server and every session
katu reload # apply katu.toml changes without restarting anything
katu resurrect # recreate sessions saved at the last clean shutdown
katu completions fish > ~/.config/fish/completions/katu.fish # or bash/zsh/elvish/powershellDetach with ctrl+space then d. The shell keeps running in the background.
Defaults are my preference keybinding Alt chords, with a ctrl+space prefix for the rest:
| Keys | Action |
|---|---|
alt \ |
Split pane side by side |
alt - |
Split pane stacked |
alt h/j/k/l |
Focus the pane in that direction |
alt H/J/K/L |
Move the nearest divider left/down/up/right |
alt z |
Zoom: toggle the pane to full window |
alt q |
Close the pane (kills its shell) |
alt Q |
Close the window and all its panes |
alt n |
New window |
alt 1–9 |
Jump to window by tab number |
alt y |
Copy mode: browse history, select, and yank (see below) |
alt < / alt > |
Scroll the pane in front up / down. In a popup, alt k / alt j (vim) scroll it too |
alt p |
Toggle the scratch-shell popup |
alt a |
Toggle the AI popup |
alt u |
Agent picker popup (fzf, jumps on enter) |
alt r |
Toggle the HTTP request tool popup (see Testing APIs) |
alt s |
Session switcher popup: pick a session, or type a new name to create one |
prefix d |
Detach (session keeps running) |
prefix n / p |
Next / previous window |
prefix o |
Focus next pane |
prefix = / - |
Grow / shrink the pane inside its split |
prefix 1–9 |
Jump to window by tab number |
Every action is rebindable (see Configuration): values take the form alt + <key> or prefix + <key>. Case matters for Alt (alt + H is Alt+Shift+h); alt + [, alt + ], and alt + O are reserved by terminal escape sequences.
A pane closes when its shell exits; the sibling pane takes over the space.
alt s opens a session switcher — a bordered popup the client draws itself (no external picker, no dependency) — without ever detaching to your shell: type to filter, arrows or ctrl+p/ctrl+k (up) and ctrl+n/ctrl+j (down) to move, Enter to act on whatever's highlighted, Esc to cancel and stay exactly where you were. The session you're switching from always shows a filled ●, even though it's technically already detached by the time the popup draws. "Create a new session" only ever appears as its own explicit row below any matches — typing an existing session's name never risks silently duplicating it, and creating one always shows exactly what will happen before you press Enter. This is also how katu start <name>/katu join <name> behave when you're already inside a session — attaching directly isn't possible from a nested shell (there's no OS-level channel from a process inside a pane to the client bridging your terminal), so the error message points you at alt s instead.
katu stop --all (or a server restart via SIGTERM) writes every running
session's windows and splits — with each pane's working directory — to
~/.katu/sessions.json before it exits. Run katu resurrect afterward
(it starts the server if needed) to recreate them: same windows, same
splits, same directories, fresh shells — agent windows come back running
the AI command again rather than a plain shell, and resume the actual
conversation if [ai] resume_flag is set. Sessions already running under
a saved name are left alone, so running it again is always safe.
This is v1 and deliberately minimal: no scrollback, no popup state, no
zoom, and no autosave — only a clean shutdown saves anything, so a crash
isn't covered. AI context needs no special handling: context files are
already keyed by session name alone, so a resurrected session reconnects
to its existing KATU_AI_CONTEXT file automatically.
katu http (or alt r inside a session) opens a small request tool in
the brand palette: cycle the method, type a URL, add headers, edit the
body in $EDITOR, and send. Status, timing, size, and a pretty-printed
JSON body (server field order preserved) come back in a scrollable viewer
that treats a 500 as a result to inspect, not an error.
{{base_url}}-style variables resolve at send time against switchable
environments (e), defined globally in ~/.katu/http/environments.toml
or per project in a committable .katu/http.toml. The UI and every file
on disk keep the raw templates — tokens live only in your environment
files. Every send lands in a filterable history (h), and S saves the
request to a hand-editable TOML collection (c to browse).
As a popup, hiding it with alt r keeps the drafted request alive —
the same trick as the AI popup; katu http --window gives it a full
window instead. See docs/manual.md for every key and file format.
Four themes ship with Katu:
katu(default) — Katu's own palette: Jenipapo (#1A1C19) background and Palha (#E5DFD3) text for a calm, high-contrast bar over long sessions; Urucum (#C85A3C) marks the active window and the session block, the same restraint a tiling WM gives an active border; Mata (#4A6B53) and Ocre (#D9A05B) carry inactive tabs, separators, and the clock/battery — support colors that never compete with the accent. Window tabs with a per-process Nerd Font icon and the pane's directory on the left; date, clock, battery, and session name on the right, refreshed every second. Needs a Nerd Font and a truecolor terminal.shugyo— an IBM Carbon dark bar, otherwise laid out the same askatu.craftzdog— a pixel-faithful clone of the classic Solarized-dark tmux statusline:session:window.paneand username in flat color blocks on the left, window tabs in the middle (the active tab gets a two-tone cream/yellow block), hostname on the right. No icons, no separators — plain truecolor only, no font requirements.minimal— a plain reverse-video bar that works on any terminal.
By default the bar appears only when you have more than one window (status_bar = "auto").
alt y opens copy mode: a cursor over the pane's history (1000 lines) that you can move, select from, and yank to your system clipboard — no mouse required. The [copy n] badge shows the scrollback offset; it becomes [copy n selecting] once you've marked a selection.
| Keys | Action |
|---|---|
↑ / k, ↓ / j |
Move the cursor up/down a line, paging the view as needed |
← / h, → / l |
Move the cursor left/right a column |
PgUp / PgDn |
Page up / down |
g / G |
Jump to the oldest / newest line |
v |
Mark the selection anchor here (press again to cancel it) |
y |
Yank the selection to your system clipboard (via OSC 52) and exit |
q, Esc |
Exit copy mode |
Copying uses OSC 52, the same terminal-native clipboard escape tmux and others use — it needs a terminal that supports it (ghostty, wezterm, kitty, alacritty, and foot all do), but needs nothing else: no xclip/wl-copy, no dependency, and it works exactly the same over SSH.
A selection can span more than one screen — extend it by paging with k/j/PgUp/PgDn — but only as far as the far end (the anchor) stays representable on the current screen; once extending further would push it off-screen, that direction just stops. Selecting across many screens in one continuous move isn't supported in this first pass.
Every session gets a private context file:
$ katu join api
$ echo "$KATU_AI_CONTEXT"
/home/you/.katu/ai-contexts/api.jsonExternal AI CLIs read and write that file; switching sessions switches the file. To hand your assistant the current terminal output:
katu capture | claude "explain this database lock error"Katu manages parallel AI agents natively (inspired by tmux-claude-session-manager):
katu ai # toggle a floating AI popup inside a session, or start
# a dedicated ai-<dir> session for the current directory
katu agents # list agents inside katu: status, name, age, location
katu agents --pick # fzf picker; enter jumps to the agent's panealt a toggles the AI popup: a floating pane over your work, tmux display-popup style. Hiding it keeps the agent running. alt u opens the agent picker in a popup, and alt p gives you a scratch-shell popup. The status line marks any window hosting an agent with a robot icon colored by state — yellow when the agent is waiting for your input, red while busy, green when idle — so a glance at the bar tells you which agent needs attention. Agent discovery uses claude agents --json (Claude Code ≥ 2.1.139); the command is configurable via [ai] command.
Claude Code renders its transcript inline and doesn't track the mouse, so its history lands in the pane's terminal scrollback. In the AI popup, alt k / alt j (or alt < / alt >) page back through that output — vim-style, no mouse needed. A mouse-tracking full-screen app (less, htop) gets wheel events from the same keys instead, so it scrolls its own viewport.
When an agent transitions to waiting for you, katu also sends an OSC 9 terminal notification ([ai] notify, on by default) — so you don't have to keep glancing at the bar to know an agent anywhere in the session needs attention. Needs a terminal that supports OSC 9 (iTerm2, wezterm, kitty, and foot do); unsupported terminals just ignore it.
katu resurrect (see Persistence) restores an agent window as an agent again, not a plain shell. Set [ai] resume_flag (e.g. "--continue" for Claude Code) to also resume the actual conversation for that pane's restored directory — off by default, since an unrecognized flag could stop an arbitrary configured [ai] command from starting at all.
Optional TOML file at ~/.config/katu/katu.toml:
[ui]
theme = "katu" # katu (default) | shugyo | craftzdog (tmux-style) | minimal
status_bar = "auto" # auto | visible | hidden
[ai]
context_directory = "~/.katu/ai-contexts/"
command = "claude" # launched by `katu ai` and prefix + a
notify = true # OSC 9 notification when an agent starts waiting on you
resume_flag = "" # e.g. "--continue" to resume agent panes on `katu resurrect`
[bindings]
prefix = "ctrl+space"
detach = "prefix + d"
split_vertical = "alt + \\" # side by side
split_horizontal = "alt + -" # stacked
window_new = "alt + n"
window_next = "prefix + n"
window_prev = "prefix + p"
window_close = "alt + Q"
pane_focus_left = "alt + h"
pane_focus_down = "alt + j"
pane_focus_up = "alt + k"
pane_focus_right = "alt + l"
pane_focus_next = "prefix + o"
pane_resize_left = "alt + H"
pane_resize_down = "alt + J"
pane_resize_up = "alt + K"
pane_resize_right = "alt + L"
pane_zoom = "alt + z"
pane_close = "alt + q"
pane_grow = "prefix + ="
pane_shrink = "prefix + -"
scrollback = "alt + y"
popup = "alt + p"
agent_popup = "alt + a"
agent_picker = "alt + u"
session_switcher = "alt + s"
scroll_up = "alt + <"
scroll_down = "alt + >"$KATU_CONFIG overrides the config file path. Run katu reload to apply
edits to a running server without restarting any session — status theme,
visibility, and the AI command update immediately; a malformed file is
rejected with an error instead of touching anything.
Katu is one binary with two roles:
- Server — a background daemon that owns the PTYs, keeps sessions alive across disconnects, and exits when the last session ends. It starts automatically on first use. Each session runs as an actor task holding a vt100 grid per pane, so reattaching repaints the exact screen and panes compose into one view.
- Client — a stateless process that connects over a Unix socket (mode 600) and bridges your terminal to a session.
Everything Katu writes (~/.katu, context files, the socket) is restricted to your user.
The repo ships a Nix flake with the full toolchain:
direnv allow # or: nix develop
cargo build
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warningsThe workspace has two crates:
crates/katu-core— config, IPC protocol, shared typescrates/katu— the CLI, client, and server
- v0.4 — mouse support
- Later — remote session sharing, cloud sync (PRD §6)
MIT




![Copy mode with a multi-line selection highlighted and the [copy 0 selecting] badge in the top-right corner](/simaojunior/katu/raw/main/assets/screenshot-copy-mode.png)


