# Technical Documentation: tw93/Kaku

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/tw93/Kaku)

### Overview (`README.md`)

<div align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/tw93/Kaku/main/assets/readme-logo.svg" />
    <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/tw93/Kaku/main/assets/readme-logo-light.svg" />
    <img src="https://raw.githubusercontent.com/tw93/Kaku/main/assets/readme-logo-light.svg" alt="Kaku" width="120" />
  </picture>
  <h1>Kaku</h1>
  <p><em>A modern Mac terminal that looks good, runs fast, and works with AI out of the box.</em></p>
  <p>English · <a href="README_CN.md">中文</a> · <a href="README_TW.md">繁體</a> · <a href="README_JA.md">日本語</a> · <a href="README_KR.md">한국어</a> · <a href="README_DE.md">Deutsch</a> · <a href="README_FR.md">Français</a></p>
  <p>
    <a href="https://kaku.fun">Website</a> ·
    <a href="https://kaku.fun/docs/">Docs</a> ·
    <a href="https://kaku.fun/compare">Compare</a> ·
    <a href="https://github.com/tw93/Kaku/releases/latest">Download</a>
  </p>
  <a href="https://kaku.fun"><img src="https://img.shields.io/badge/website-kaku.fun-287A3D?style=flat-square" alt="Website"></a>
  <a href="https://github.com/tw93/Kaku/stargazers"><img src="https://img.shields.io/github/stars/tw93/Kaku?style=flat-square" alt="Stars"></a>
  <a href="https://github.com/tw93/Kaku/releases"><img src="https://img.shields.io/github/v/tag/tw93/Kaku?label=version&style=flat-square" alt="Version"></a>
  <a href="LICENSE.md"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License"></a>
  <a href="https://github.com/tw93/Kaku/commits"><img src="https://img.shields.io/github/commit-activity/m/tw93/Kaku?style=flat-square" alt="Commits"></a>
  <a href="https://twitter.com/HiTw93"><img src="https://img.shields.io/badge/follow-Tw93-red?style=flat-square&logo=Twitter" alt="Twitter"></a>
</div>

<p align="center">
  <img src="assets/kaku.jpg" alt="Kaku Screenshot" width="1000" />
</p>

## Why

Kaku (書く, かく) means "to write" in Japanese. It is a macOS terminal based on WezTerm, with fonts, themes, shell integration, and Mac shortcuts already configured, while preserving full Lua customization.

Kaku is for writing code, alongside [Waza](https://github.com/tw93/Waza) (技) for engineering habits and [Kami](https://github.com/tw93/Kami) (紙) for documents.

## Quick Start

Download the [Kaku DMG](https://github.com/tw93/Kaku/releases/latest), open it, and drag Kaku into Applications. Or install with Homebrew:

```bash
brew install --cask kaku
```

Open Kaku to set up shell integration, install missing optional tools with `kaku init`, and check your installed version with `kaku --version`.

## Features

- **Ready to use**: JetBrains Mono, automatic dark and light themes, copy on select, and familiar Mac shortcuts
- **Tabs and panes**: Split your workspace, jump between tabs with Tab Navigator, and restore windows, panes, and working directories when you reopen Kaku
- **Right-click menu**: Paste, search, open AI chat, split or close the clicked pane, and open or close tabs without remembering shortcuts
- **Clickable links**: `Cmd + Click` opens URLs and file paths, and URLs automatically wrapped by the terminal keep their full address
- **AI-friendly**: See which agent is working and on what at the top right, get notified when Claude Code needs you, and use an optional assistant for command suggestions and chat
- **Remote access**: Run `kaku remote` to get a link, open it in any browser on another computer, and keep using the Kaku on this Mac
- **Shell tools**: Built-in zsh completion, syntax highlighting, and directory jumping, with shortcuts for optional Lazygit and Yazi installations
- **Lua configuration**: Customize fonts, themes, shortcuts, and terminal behavior using WezTerm's Lua configuration system

## Usage Guide

| Action | Shortcut |
| :--- | :--- |
| New Tab | `Cmd + T` |
| New Window | `Cmd + N` |
| Close Tab/Pane | `Cmd + W` |
| Navigate Tabs | `Cmd + Shift + [` / `]` or `Cmd + 1-9` |
| Navigate Panes | `Cmd + Opt + Arrows` |
| Split Pane Vertical | `Cmd + D` |
| Split Pane Horizontal | `Cmd + Shift + D` |
| Open Settings Panel | `Cmd + ,` |
| AI Settings | `Cmd + Shift + A` |
| AI Chat | `Cmd + L` |
| Apply AI Suggestion | `Cmd + Shift + E` |
| Open Lazygit | `Cmd + Shift + G` |
| Yazi File Manager | `Cmd + Shift + Y` or `y` |
| Clear Screen | `Cmd + K` |

## Kaku AI

Configure your own AI service with `kaku ai` to use the built-in assistant. Kaku does not provide or relay the AI service.

- **Command suggestions**: When a command fails, the configured assistant can suggest a fix, and `Cmd + Shift + E` pastes it at the prompt for review
- **Natural language to command**: Type `# <description>` at the prompt and press Enter, and the assistant places the generated command at the prompt for you to review and run
- **Chat**: Press `Cmd + L` to discuss terminal output or work with project files and tools, or run `kaku chat` from another shell to access the same conversation store
- **AI Tools Config**: Manage settings for Claude Code, Codex, Gemini CLI, Copilot CLI, Kimi Code, and more

For authentication, models, API Mode, and tool settings, see the [AI assistant docs](docs/features.md).

## FAQ

- **Is there a Windows or Linux version?** Not currently, Kaku is macOS-only
- **How is Kaku different from iTerm2, Warp, Ghostty, or WezTerm?** See [kaku.fun/compare](https://kaku.fun/compare)
- **Can I use transparent windows?** Yes, set `config.window_background_opacity` in `~/.config/kaku/kaku.lua`
- **The `kaku` command is missing.** Run `/Applications/Kaku.app/Contents/MacOS/kaku init --update-only && exec zsh -l`, then `kaku doctor`

## Docs

- [Keybindings](docs/keybindings.md) - full shortcut reference
- [Features](docs/features.md) - AI assistant, remote access, lazygit, yazi, remote files, shell suite
- [Configuration](docs/configuration.md) - themes, fonts, custom keybindings, Lua API
- [CLI Reference](docs/cli.md) - `kaku ai`, `kaku config`, `kaku doctor`, and more
- [FAQ](docs/faq.md) - common questions and troubleshooting

## Background

I heavily rely on the CLI for both work and personal projects. Tools I've built, like [Mole](https://github.com/tw93/mole) and [Pake](https://github.com/tw93/pake), reflect this.

I used Alacritty for years and learned to value speed and simplicity. As my workflow shifted toward AI-assisted coding, I wanted stronger tab and pane ergonomics. I also explored Kitty, Ghostty, Warp, and iTerm2. Each is strong in different areas, but I still wanted a setup that matched my own balance of performance, defaults, and control.

WezTerm is robust and highly hackable, and I am grateful for its engine and ecosystem. So I built Kaku to be that environment: fast, polished, and ready to work.

## Contributors

Big thanks to all contributors who helped build Kaku. Go follow them! ❤️

<a href="https://github.com/tw93/Kaku/graphs/contributors">
  <img src="./CONTRIBUTORS.svg?v=2" width="1000" />
</a>

## Support

- The most direct way to support me is getting [Mole for Mac](https://mole.fit), my paid Mac cleanup app
- If Kaku helped you, give it a star, [share it](https://twitter.com/intent/tweet?url=https://github.com/tw93/Kaku&text=Kaku%20-%20A%20modern%2C%20AI-friendly%20Mac%20terminal.), or open an issue or PR
- I have two cats, TangYuan and Coke, and if Kaku has been helpful to you, feel free to treat them to <a href="https://cats.tw93.fun?name=Kaku" target="_blank">canned food 🥩</a>

These lovely people already did 🐱

<a href="https://cats.tw93.fun?name=Kaku"><img src="https://raw.githubusercontent.com/tw93/sponsors/main/assets/sponsors.svg" width="1000" loading="lazy" /></a>

## License

MIT License. Please feel free to use and contribute to the development. Attribution for WezTerm and the bundled fonts is in [NOTICE.md](NOTICE.md).

---

### Overview (`lua-api-crates/README.md`)

The crates in this directory provide modules and functions
to wezterm's lua config file and interface.

They are registered into the lua config via env-bootstrap.

It is advantageous to build times to have multiple, smaller, crates.

---

### Overview (`term/README.md`)

# wezterm-term

This crate provides the core of the virtual terminal emulator implementation
used by [wezterm](https://wezterm.org/).  The home for this
crate is in the wezterm repo and development is tracked at
<https://github.com/wezterm/wezterm/>.

It is full featured, providing terminal escape sequence parsing, keyboard
and mouse input encoding, a model for the screen cells including scrollback,
sixel and iTerm2 image support, OSC 8 Hyperlinks and a wide range of
terminal cell attributes.

This crate does not provide any kind of gui, nor does it directly
manage a PTY; you provide a `std::io::Write` implementation that
could connect to a PTY, and supply bytes to the model via the
`advance_bytes` method.

The entrypoint to the crate is the [Terminal](terminal/struct.Terminal.html)
struct.

License: MIT

---

### Overview (`termwiz/README.md`)

# Terminal Wizardry

This is a rust crate that provides a number of support functions for
applications interested in either displaying data to a terminal or in building
a terminal emulator.

It is currently in active development and subject to fairly wild sweeping
changes.

Included functionality:

* `Surface` models a terminal display and its component `Cell`s
* Terminal attributes are aware of modern features such as
  True Color, [Hyperlinks](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda)
  and will also support sixel and iterm style terminal graphics display.
* `Surface`s include a log of `Change`s and an API for consuming
  and applying deltas.  This is a powerful building block for
  synchronizing screen instances.
* Escape sequence parser decodes inscrutable escape sequences
  and gives them semantic meaning, making the code that uses
  them clearer.  The decoded escapes can be re-encoded, allowing
  applications to start with the semantic meaning and emit
  the appropriate escape sequence without embedding obscure
  binary bytes.
* `Capabilities` allows probing for terminal capabilities
  that may not be included in the system terminfo database,
  and overriding them in an embedding application.
* `Terminal` trait provides an abstraction over unix style ttys
  and Windows style console APIs.  `Change`s from `Surface`
  can be rendered to `Terminal`s.  `Terminal`s allow decoding
  mouse and keyboard inputs in both blocking or non-blocking
  mode.
* `Widget` trait allows composition of UI elements at a higher level.
* `LineEditor` implements shell-like line editing functionality.

# Windows Support

Termwiz understands how to work with both the legacy console APIs and
the new PTY and virtual terminal features available in Windows 10,
allowing for true color terminal applications on Windows 10.

# Documentation

https://docs.rs/termwiz

---

### Overview (`crates/bidi/README.md`)

# wezterm-bidi - a pure Rust bidi implementation

This crate provides an implementation of the *The Unicode Bidirectional
Algorithm (UBA)* in Rust.

This crate was developed for use in wezterm but does not depend on
other code in wezterm.

The focus for this crate is conformance.

This crate is `no_std`, but does require `alloc`.

## Status

This crate resolves embedding levels and can reorder line ranges.

The implementation conformant with 100% of the BidiTest.txt and
BidiCharacterTest.txt test cases (approx 780,000 test cases).

## License

MIT compatible License
Copyright © 2022-Present Wez Furlong.

Portions of the code in this crate were derived from the bidiref reference
implementation of the UBA which is:

Copyright © 1991-2022 Unicode, Inc. All rights reserved.

See [LICENSE.md](LICENSE.md) for the full text of the license.

---

### Overview (`crates/filedescriptor/README.md`)

The purpose of this crate is to make it a bit more ergonomic for portable
applications that need to work with the platform level `RawFd` and
`RawHandle` types.

Rather than conditionally using `RawFd` and `RawHandle`, the `FileDescriptor`
type can be used to manage ownership, duplicate, read and write.

## FileDescriptor

This is a bit of a contrived example, but demonstrates how to avoid
the conditional code that would otherwise be required to deal with
calling `as_raw_fd` and `as_raw_handle`:

```
use filedescriptor::{FileDescriptor, FromRawFileDescriptor, Result};
use std::io::Write;

fn get_stdout() -> Result<FileDescriptor> {
  let stdout = std::io::stdout();
  let handle = stdout.lock();
  FileDescriptor::dup(&handle)
}

fn print_something() -> Result<()> {
   get_stdout()?.write(b"hello")?;
   Ok(())
}
```

## Pipe
The `Pipe` type makes it more convenient to create a pipe and manage
the lifetime of both the read and write ends of that pipe.

```
use filedescriptor::Pipe;
use std::io::{Read, Write};

let mut pipe = Pipe::new()?;
pipe.write.write(b"hello")?;
drop(pipe.write);

let mut s = String::new();
pipe.read.read_to_string(&mut s)?;
assert_eq!(s, "hello");
```

## Socketpair
The `socketpair` function returns a pair of connected `SOCK_STREAM`
sockets and functions both on posix and windows systems.

```
use std::io::{Read, Write};

let (mut a, mut b) = filedescriptor::socketpair()?;
a.write(b"hello")?;
drop(a);

let mut s = String::new();
b.read_to_string(&mut s)?;
assert_eq!(s, "hello");
```

## Polling
The `mio` crate offers powerful and scalable IO multiplexing, but there
are some situations where `mio` doesn't fit.  The `filedescriptor` crate
offers a `poll(2)` compatible interface suitable for testing the readiness
of a set of file descriptors.  On unix systems this is a very thin wrapper
around `poll(2)`, except on macOS where it is actually a wrapper around
the `select(2)` interface.  On Windows systems the winsock `WSAPoll`
function is used instead.

```
use filedescriptor::*;
use std::time::Duration;
use std::io::{Read, Write};

let (mut a, mut b) = filedescriptor::socketpair()?;
let mut poll_array = [pollfd {
   fd: a.as_socket_descriptor(),
   events: POLLIN,
   revents: 0
}];
// sleeps for 20 milliseconds because `a` is not yet ready
assert_eq!(poll(&mut poll_array, Some(Duration::from_millis(20)))?, 0);

b.write(b"hello")?;

// Now a is ready for read
assert_eq!(poll(&mut poll_array, Some(Duration::from_millis(20)))?, 1);

```

---

### Overview (`crates/vtparse/README.md`)

# vtparse

This is an implementation of a parser for escape and control sequences.
It is based on the [DEC ANSI Parser](https://vt100.net/emu/dec_ansi_parser).

It has been modified slightly to support UTF-8 sequences.

`vtparse` is the lowest level parser; it categorizes the basic
types of sequences but does not ascribe any semantic meaning
to them.

You may wish to look at `termwiz::escape::parser::Parser` in the
[termwiz](https://docs.rs/termwiz) crate if you're looking for semantic
parsing.

## Comparison with the `vte` crate

`vtparse` has support for dynamically sized OSC buffers, which makes
it suitable for processing large escape sequences, such as those
used by the `iTerm2` image protocol.

---

### Overview (`deps/cairo/cairo/README.md`)

# Cairo: Multi-platform 2D graphics library

<https://cairographics.org>

What is cairo
-------------

Cairo is a 2D graphics library with support for multiple output
devices. Currently supported output targets include the X Window
System (via both Xlib and XCB), quartz, win32, and image buffers,
as well as PDF, PostScript, and SVG file output. Experimental backends
include OpenGL.

Cairo is designed to produce consistent output on all output media
while taking advantage of display hardware acceleration when available
(for example, through the X Render Extension).

The cairo API provides operations similar to the drawing operators of
PostScript and PDF. Operations in cairo include stroking and filling
cubic Bézier splines, transforming and compositing translucent images,
and antialiased text rendering. All drawing operations can be
transformed by any affine transformation (scale, rotation, shear,
etc.).

Cairo has been designed to let you draw anything you want in a modern
2D graphical user interface.  At the same time, the cairo API has been
designed to be as fun and easy to learn as possible. If you're not
having fun while programming with cairo, then we have failed
somewhere---let us know and we'll try to fix it next time around.

Cairo is free software and is available to be redistributed and/or
modified under the terms of either the GNU Lesser General Public
License (LGPL) version 2.1 or the Mozilla Public License (MPL) version
1.1.

Where to get more information about cairo
-----------------------------------------

The primary source of information about cairo is its website:

- <https://cairographics.org>

The latest versions of cairo can always be found at:

- <https://cairographics.org/download>

Documentation on using cairo and frequently-asked questions:

- <https://cairographics.org/documentation>
- <https://cairographics.org/FAQ>

Mailing lists for contacting cairo users and developers:

- <https://cairographics.org/lists>

Roadmap and unscheduled things to do, (please feel free to help out):

- https://cairographics.org/roadmap
- https://cairographics.org/todo

Dependencies
------------

The set of libraries needed to compile cairo depends on which backends are
enabled when cairo is configured. So look at the list below to determine
which dependencies are needed for the backends of interest.

For the surface backends, we have both "supported" and "experimental"
backends. Further, the supported backends can be divided into the "standard"
backends which can be easily built on any platform, and the "platform"
backends which depend on some underlying platform-specific system, (such as
the X Window System or some other window system).

As an example, for a standard Linux build similar to what's shipped by your
distro, (with image, png, pdf, PostScript, svg, and xlib surface backends,
and the freetype font backend), the following sample commands will install
necessary dependencies:

- Debian (and similar):
  - `apt-get build-dep cairo`

- Fedora (and similar):
  - `dnf builddep cairo`

Technically you probably don't need pixman from the distribution since if
you're manually compiling Cairo you probably want an updated pixman as well.
However, if you follow the default settings and install pixman to
/usr/local, your Cairo build should properly use it in preference to the
system pixman.


### Supported, "standard" surface backends

#### image backend (required)

- [pixman](https://cairographics.org/releases) >= 0.30.0 

#### PNG support (preferred)

- [libpng](http://www.libpng.org/pub/png/libpng.html)

#### PDF backend

- [zlib](http://www.gzip.org/zlib)

#### PostScript backend

- [zlib](http://www.gzip.org/zlib)

#### SVG backend

- none

### Supported, "platform" surface backends

#### Xlib backend

- [X11](https://freedesktop.org/Software/xlibs)

#### xlib-xrender backend

- [Xrender](https://freedesktop.org/Software/xlibs) >= 0.6

#### Quartz backend

- macOS >= 10.4 with Xcode >= 2.5

#### Windows backend

- Microsoft Windows 2000 or newer.

#### XCB backend

- [XCB](https://xcb.freedesktop.org)

### Font backends (required)

#### freetype font backend

- [freetype](https://freetype.org) >= 2.1.9
- [fontconfig](https://www.freedesktop.org/wiki/Software/fontconfig/)

#### Quartz-font backend

- MacOS X >= 10.4 with Xcode >= 2.5

#### Windows GDI font backend

- Microsoft Windows 2000 or newer

#### Windows DirectWrite font backend

- Microsoft Windows 7 or newer

Compiling
---------

See the [`INSTALL`](./INSTALL) document for build instructions.

Licensing
---------

Cairo is released under the terms of either the GNU Lesser General Public
License version 2.1, or the terms of the Mozilla Public License version 1.1.

See the [`COPYING`](./COPYING) document for more information.

History
-------

Cairo was originally developed by Carl Worth <cworth@cworth.org> and Keith
Packard <keithp@keithp.com>. Many thanks are due to Lyle Ramshaw without
whose patient help our ignorance would be much more apparent.

Since the original development, many more people have contributed to cairo.
See the [`AUTHORS`](./AUTHORS) document for as complete a list as we've been
able to compile so far.

---

### Cli (`docs/cli.md`)

# CLI Reference

Run `kaku` in your terminal to see all available commands.

## kaku ai

Open the AI settings panel inside Kaku. Configure external coding tools (Claude Code, Codex, Gemini CLI, Copilot CLI, Kimi Code, etc.) and Kaku Assistant.

```bash
kaku ai
```

## kaku chat

Start Kaku's standalone AI chat from any shell. This is a discoverable alias for the bundled `k` helper, so it works even when `k` is not on your PATH.

```bash
kaku chat                 # open interactive chat
kaku chat "explain this"  # one-shot prompt
```

The chat uses `~/.config/kaku/assistant.toml`, shares the same conversation and memory files as the `Cmd + L` overlay, and supports `/new`, `/resume`, `/clear`, `/status`, `/memory`, and `/exit` in interactive mode.

## kaku config

Open the Kaku configuration TUI for common settings and Lua overrides. It creates `~/.config/kaku/kaku.lua` if it is missing. Inside Kaku, `Cmd + ,` opens the same screen.

```bash
kaku config
```

## kaku doctor

Check the Kaku app bundle, PATH entries, and zsh or fish shell integration. Use this first if something feels broken.

Every run also writes a diagnostic bundle to `~/.local/share/kaku/diagnostics/Kaku-Diagnose.zip`: the report, logs, recent crash reports, a short sample of a running Kaku, and any GUI stalls Kaku recorded on its own. Home paths, the host name, MAC addresses, serial numbers and credential-looking values are replaced first, `assistant.toml` is never read, and nothing leaves your Mac. Attach the zip when you report an issue. If Kaku itself is frozen, run `kaku doctor` from Terminal.app.

```bash
kaku doctor
kaku doctor --shell fish       # check fish even when $SHELL points to zsh
kaku doctor --shell fish --fix # repair the selected integration
```

## kaku update

Check for and install the latest Kaku release.

```bash
kaku update
```

## kaku reset

Remove Kaku-managed shell and tmux integration, Kaku-managed git delta defaults, selected Kaku state, and managed theme blocks in `~/.config/kaku/kaku.lua`. It also deletes Kaku AI memory (`soul/` and `ai_chat_memory.md`), the AI chat onboarding flag, and the `backups/` folder in `~/.config/kaku`. User-authored Lua outside managed blocks is preserved. Use with caution and run `kaku init` again if you want shell integration back.

```bash
kaku reset
kaku reset --shell fish # use fish for restart and restore guidance
```

## kaku remote

Use this Mac's terminals from a browser on another device, through Kaku's relay. Remote access is off until you turn it on.

```bash
kaku remote               # turn remote access on (asks first) and print a pairing link
kaku remote devices       # list paired browsers
kaku remote revoke <id>   # remove a browser; its open sessions end within seconds
kaku remote off           # turn remote access off
kaku remote --reset       # new identity, every paired browser removed
```

The page is made for desktop browsers for now. Open the link in the browser you want to pair. A link works once, for 10 minutes, so run `kaku remote` again for each device. After pairing, bookmark the page: the browser keeps its own key and comes back without a new link. What you type and see is encrypted between the browser and Kaku, and the relay only forwards it. Kaku posts a notification when a new browser pairs, and when a paired one returns after a while. `kaku doctor` shows whether Kaku is connected to the relay.

## kaku init

Set up Kaku's shell integration for zsh or fish. When both shells are installed, an interactive run asks which one to configure. Use `--shell` to make the choice explicit in scripts or when `$SHELL` does not match your daily shell. Also installs optional CLI tools (Starship, Delta, Lazygit, Yazi) via Homebrew.

```bash
kaku init
kaku init --shell fish
kaku init --shell zsh --update-only
```

If the `kaku` command goes missing from your shell, restore it with:

```bash
/Applications/Kaku.app/Contents/MacOS/kaku init --update-only
exec zsh -l
```

## kaku cli

Interact with the Kaku multiplexer from scripts and external tools.

```bash
kaku cli split-pane                          # split current pane
kaku cli split-pane -- bash -c "echo hello"  # split and run a command
kaku cli --help                              # list all subcommands
kaku cli split-pane --help                   # help for a specific subcommand
```

Useful for integrating Kaku with AI tools or shell scripts that need to open panes or tabs programmatically.

---

### Config Versions (`docs/config-versions.md`)

# Config Version History

`config_version` is a monotonically increasing integer. The single source of truth is
`assets/shell-integration/config_version.txt`; the release gate
(`scripts/check_release_config.sh`) requires it to increment on every release, even when
nothing in the schema changed. Do not hardcode the current number in agent guides or
instruction files; read the txt file instead.

## Rules for bumping

- A schema change must update the bundled defaults (`assets/macos/Kaku.app/Contents/Resources/kaku.lua`),
  user docs, release checks, and migration behavior together, in one change.
- Only keys that existed in the previous released version need migration code. A feature
  introduced in the current cycle ships its default directly: no migration, no version-gated
  branches, no release-note migration mention. Check with
  `git show V<previous release>:assets/macos/Kaku.app/Contents/Resources/kaku.lua | grep <key>`.
- A bump with no schema change is normal and expected; record why in this file.

## History

| Version | Release | Change |
|---|---|---|
| v21 | - | Adds `smart_tab_mode`. Introduces the optional `SmartPrompt` value for `window_close_confirmation` (the bundled default later flipped to `SmartPrompt` so a stateful pane is no longer dropped silently on Cmd+Q). Accepts the removed `language` option as a deprecated field for backward compat. |
| v22 | - | Adds a precmd guard so the dark-theme comment color override still applies when the user pre-loads fast-syntax-highlighting or zsh-syntax-highlighting in their own `.zshrc`. |
| v23 | - | Flips the bundled `smart_tab_mode` default to `suggestion_first` so Tab accepts a visible autosuggestion, falling back to completion. No schema change, so no migration; users who set `completion_first` keep it. |
| v24 | - | Migrates `$schema` keys in user yazi configs to the `#:schema` comment form that yazi 26.5.6+ requires. No schema change; the repair runs in three places that must stay in sync: setup scripts, the yazi wrapper, and `kaku.lua`. |
| v25 | - | No schema change. Bumps so an updated install regenerates the bundled zsh integration, picking up the fast-syntax-highlighting `path-to-dir` style that no longer underlines existing directories. |
| v26 | V0.12.4 | No schema or bundled-integration change; increments only because the release gate requires a bump every release. The release highlight reuses the Cmd+Click-opens-links improvement. |
| v27 | V0.13.0 | Adds `tab_title_show_foreground_process`, an opt-in setting for showing foreground process names in auto-generated tab titles. No migration is needed because this key did not exist in V0.12.4. |
| v28 | - | No schema change. Bumps so existing installs regenerate the bundled zsh integration with the self-contained SSH wrapper required by shell snapshot tools. |
| v29 | V0.15.0 | No schema change. Bumps so existing installs regenerate the bundled shell integrations with the aligned ssh wrappers: fish keeps user-defined ssh functions and gains the 1Password fix, env-prefix ssh aliases work, and mosh gets the same TERM fallback. |
| v30 | V0.16.0 | No schema change. Bumps so existing installs regenerate shell integrations that authenticate inline AI control messages with a local capability before terminal output can trigger assistant requests, and that scope the Starship prompt setup to Kaku instead of every zsh. |
| v31 | V0.17.0 | Adds the optional `file_link_editor` setting for choosing which editor opens local terminal file links, bare-domain Cmd+Click links (e.g. `github.com` without a scheme), and Option+Click cursor movement within the current input line. Also regenerates shell integrations with the #511 fixes: the inline AI capability file is read tolerantly (zsh/fish `read` treat a missing trailing newline as EOF failure), user-var guards report failure with `return 1` instead of masking it, and the fish widget only enters the waiting state after the request was actually emitted. The Rust side also writes the capability file with a trailing newline for new installs. |
| v32 | V0.18.0 | No schema change, and no bundled-integration change either; increments only because the release gate requires a bump every release. The release highlights reuse the new Move Tab to New Window command and the multi-display menu bar clamp fix. |
| v33 | V0.19.0 | No schema change. Bumps so existing installs pick up two bundled fixes: light-theme selected rows keep a visibly blue background instead of washing out to near-white, and the lazygit launcher resolves its working directory from the shell that actually receives the input, so it also works inside nested shells. |
| v34 | V0.20.0 | No schema change. Regenerates shell integrations to reset mouse reporting at the prompt after abnormal TUI or SSH exits, preserve the Kaku prompt and Smart Tab in tmux, and correct fish grep completion expansion. |
| v35 | V0.21.0 | No schema change. The config generated on first launch now sets `color_scheme = 'Kaku Dark'`; existing configs keep the bundled default that follows macOS appearance, so no migration is needed. Also increments because the release gate requires a bump every release. |
| v36 | V0.22.0 | Adds the opt-in `tab_title_use_pane_title` key and the opt-in `BuiltinInsert` value for `ime_preedit_rendering`; both defaults are unchanged. The bundled `show_new_tab_button_in_tab_bar` now defaults to true, and configs that set it keep their value, so no migration is needed. Bumps so existing installs regenerate `kaku.zsh` and stop writing before the first prompt (#561). |
| v37 | V0.23.0 | Adds the `macos_global_hotkey_*` keys (`mode`, `height_percent`, `animation_duration_ms`, `hide_on_focus_loss`, `extend_behind_notch`, `follow_mouse`, `appearance`, `padding`); `mode` defaults to the new `Drawer`, and `Ordinary` keeps the previous show and hide. All are new in this cycle, so no migration. Bumps so existing installs rerun setup, which replaces a `~/.config/starship.toml` still identical to the V0.10.0 to V0.22.0 copy whose icons were blank (#573). |
| v38 | V0.23.1 | No schema change. Bumps so existing installs regenerate the shell integrations: the cursor comes back at the prompt after a program hides it and exits, and dotfiles kept as symlinks stay linked when Kaku updates them. |
| v39 | V0.24.0 | No schema change: the `remote` keys already existed in V0.23.1, and remote access stays off by default. Increments because the release gate requires a bump every release; the highlight introduces `kaku remote`. |
| v40 | V0.25.0 | Adds `show_agent_title` (default true), which is new in this release, so no migration is needed. Bumps so existing installs regenerate `kaku.zsh`, which loads the completion dump from cache again and refreshes it after a successful full check, and pick up the Yazi `init.lua` that leaves a row under the path when none exists. |
| v41 | V0.26.0 | No schema change. Increments because the release gate requires a bump every release; the highlights cover the remote page's fonts and agent title, and the character picker's return to the command palette. |
| v42 | V0.26.1 | No schema change. Increments because the release gate requires a bump every release; the highlight covers Restart to Update installing the new version. |

When you bump the version, add a row here in the same change.

---

### Configuration (`docs/configuration.md`)

# Configuration

## Config File

Kaku creates `~/.config/kaku/kaku.lua` with a commented template on first launch, and `kaku init` or `kaku config` recreate it if it is missing. Open it with `kaku config` or `Cmd + ,`.

The file loads the bundled Kaku defaults first, then applies your overrides on top:

```lua
local wezterm = require 'wezterm'

local function resolve_bundled_config()
  local resource_dir = wezterm.executable_dir:gsub('MacOS/?$', 'Resources')
  local bundled = resource_dir .. '/kaku.lua'
  local f = io.open(bundled, 'r')
  if f then f:close(); return bundled end
  return '/Applications/Kaku.app/Contents/Resources/kaku.lua'
end

local config = {}
local bundled = resolve_bundled_config()
if bundled then
  local ok, loaded = pcall(dofile, bundled)
  if ok and type(loaded) == 'table' then config = loaded end
end

-- User overrides:
config.font_size = 16
config.window_background_opacity = 0.95

return config
```

The generated file is a longer version of this, with commented examples below `-- User overrides:`; most users only need to uncomment the lines they want to change.

## Common Overrides

Add only the settings you want to change:

```lua
config.font_size = 16
config.window_background_opacity = 0.95

-- New installs start with config.color_scheme = "Kaku Dark". Change it to:
-- config.color_scheme = "Kaku Light"
-- config.color_scheme = "Auto"  -- follow macOS appearance
```

Keep terminal behavior, appearance, key bindings, launch behavior, and other WezTerm-compatible settings in `kaku.lua`.

Manage Kaku Assistant settings with `kaku ai`. That command writes `~/.config/kaku/assistant.toml` for model, base URL, auth, API keys, and tool settings. The Lua/TOML split is intentional: `kaku.lua` stays compatible with WezTerm-style terminal configuration, while `assistant.toml` is managed by the AI setup flow.

## Appearance

**Theme**

New installs start in Kaku Dark, because the generated `kaku.lua` sets `config.color_scheme = "Kaku Dark"`. Installs whose config has no `color_scheme` line keep following macOS appearance, switching between Kaku Dark and Kaku Light automatically. To follow macOS appearance, choose **Auto** in `kaku config` or remove the `config.color_scheme` line. To force one theme:

```lua
config.color_scheme = "Kaku Dark"   -- always dark
config.color_scheme = "Kaku Light"  -- always light
```

**Color overrides**

Remap specific hex colors to keep theme consistency with apps that output their own colors. `color_overrides` applies to rendered backgrounds, including palette-backed ANSI backgrounds and truecolor backgrounds. `foreground_color_overrides` applies only to truecolor text:

```lua
config.color_overrides = {
  ['#6E6E6E'] = '#3A3942',
}

config.foreground_color_overrides = {
  ['#FFFFDB'] = '#575653',
}
```

**Font**

Kaku defaults to JetBrains Mono with PingFang SC as CJK fallback. Change font:

```lua
config.font = wezterm.font("Fira Code")
```

Kaku disables ligatures by default. Re-enable:

```lua
config.harfbuzz_features = {}
```

**Font size**

Kaku auto-selects 15px (low-res) or 17px (high-res) based on your display. Override:

```lua
config.font_size = 16
```

**Line height**

```lua
config.line_height = 1.28  -- default
```

The default favors readable text spacing. Character-cell graphics (QR codes, `neofetch` logos, TUI charts) stretch with the row height; set `1.0`–`1.1` if you want them near-square. See the [FAQ](faq.md#qr-codes-and-terminal-graphics-look-vertically-stretched) for details.

**Window transparency**

```lua
config.window_background_opacity = 0.92
config.macos_window_background_blur = 20  -- optional blur (0–100)
```

**Traffic lights (macOS)**

By default, Kaku embeds the macOS traffic light buttons into the tab bar area using `INTEGRATED_BUTTONS|RESIZE`. To hide the traffic lights while keeping resize edges and tab-bar dragging:

```lua
config.window_decorations = "RESIZE"
```

`RESIZE` preserves the ability to resize the window from its edges and drag it by the tab bar; it only removes the close/minimize/zoom buttons.

**Agent title**

While Claude Code, Codex or Kimi runs in the active pane, its mark and current topic show at the top right of the window, and at the right end of the remote access page's tab bar. To turn it off, choose Off for Agent Title in `kaku config`, or:

```lua
config.show_agent_title = false
```

**Padding**

```lua
config.window_padding = { left = '24px', right = '24px', top = '40px', bottom = '20px' }
```

Sizes accept `px`, `pt`, `cell`, and `%`. `px` is a physical pixel value and does not scale with display DPI, so the same value can look smaller on a high-density display. Use `pt` for DPI-scaled spacing or `cell` for spacing relative to the terminal cell size, for example `top = '15pt'`.

## Terminal Behavior

**Cursor**

```lua
config.default_cursor_style = "BlinkingBar"
config.cursor_thickness = "2px"
config.cursor_blink_rate = 500
```

**Scrollback**

```lua
config.scrollback_lines = 10000  -- default
```

**File link editor**

Set an editor command for local file links printed in the terminal. Kaku appends the resolved `path`, or `path:line:column` when the link includes a location. Shell-style quoting is supported for command arguments:

```lua
config.file_link_editor = "zed"
-- config.file_link_editor = "cursor --goto"
```

This setting takes priority over Kaku's automatic VS Code detection. When it is unset, Kaku keeps the existing VS Code and `$VISUAL` / `$EDITOR` fallback behavior.

**Copy on select**

Enabled by default. Disable:

```lua
config.copy_on_select = false
```

**Strip leading whitespace on copy**

When copying indented multi-line text (e.g. from a code block), remove the shared leading whitespace so the pasted result starts at column 0:

```lua
config.copy_strip_leading_whitespace = true  -- default: false
```

**Restore previous session**

Re-open the tabs and panes from your last session on launch. Enabled by default; set it to `false` to disable saving and restoring the session:

```lua
config.restore_previous_session = false  -- default: true
```

**Working directory inheritance**

```lua
config.window_inherit_working_directory = true   -- new windows
config.tab_inherit_working_directory = true       -- new tabs
config.split_pane_inherit_working_directory = true -- new splits
```

**Focused pane emphasis**

To make the focused pane easier to spot, adjust the colors of inactive panes:

```lua
config.inactive_pane_hsb = {
  hue = 0.95,
  saturation = 1.0,
  brightness = 0.9,
}
```

Each value multiplies the pane's existing HSV component, so `1.0` leaves that component unchanged. `hue` is not a color value or a degree-based rotation; small adjustments work best. The focused pane itself is not transformed.

**Tab bar**

Hidden when only one tab is open. Auto-generated tab titles show the current directory by default. You can change the position, shorten path titles, or opt into showing the foreground command alongside the path:

```lua
config.tab_bar_at_bottom = false                   -- move to top
config.tab_title_show_basename_only = true         -- show "dirname" instead of "parent/dirname"
config.tab_title_show_foreground_process = true    -- show "dirname·codex" while commands run
config.tab_title_use_pane_title = true             -- show application titles set via OSC 0/2
config.show_tab_index_in_tab_bar = true            -- number tabs: "1:dirname", "2:dirname"
```

`show_tab_index_in_tab_bar` (Settings: Tab Numbers) prefixes each tab with its position, so `Cmd + 1` through `Cmd + 8` select the tabs numbered 1 to 8. `Cmd + 9` always selects the last tab, whatever its number.

With `tab_title_use_pane_title`, the active pane's title also supplies the window title and the title of a split tab. Explicit tab renames still take precedence. Shell prompt hooks may overwrite application titles after a command exits; this is independent of OSC support.

The trailing cell stays blank until a pane wants your attention, then shows an amber dot. Background tabs that emit BEL use it, so does any program that reports an error or paused state through the OSC 9;4 progress sequence, and so does Claude Code when it waits on you, finishes or fails (OSC 7501). Disable only the BEL part if you do not want tab-level bell notifications:

```lua
config.bell_tab_indicator = false
```

**Scrollbar**

Disabled by default. Enable via `kaku config` (toggle the scrollbar style option) or in Lua:

```lua
config.enable_scroll_bar = true
```

If you want the mouse wheel to scroll inside alternate-screen apps such as nano and vim, instead of peeking into Kaku's primary scrollback, enable:

```lua
config.alternate_screen_wheel_scrolls_terminal = true
```

**Selection drag + mouse wheel**

Controls what the mouse wheel does while you are dragging out a selection with the left mouse button held down. Defaults to `"Extend"`, which matches macOS `NSTextView` apps such as Safari, TextEdit, VS Code, iTerm2 and `Terminal.app`: the wheel scrolls the scrollback and the selection grows to follow the cursor across screens.

```lua
-- Default (recommended): scroll AND extend the selection so you can grab
-- text that spans more than one screen of output.
config.selection_wheel_scroll_behavior = "Extend"

-- Scroll the scrollback but leave the selection range untouched.
config.selection_wheel_scroll_behavior = "ScrollOnly"

-- Drop the wheel event entirely (the old behavior);
-- selecting text that does not fit on one screen requires releasing the
-- mouse, scrolling, and re-selecting.
config.selection_wheel_scroll_behavior = "Ignore"
```

**macOS Option key**

Left Option sends Meta (useful for Vim/Neovim word navigation). Right Option sends compose characters.

```lua
config.send_composed_key_when_left_alt_is_pressed = false  -- default: left = Meta
config.send_composed_key_when_right_alt_is_pressed = true  -- default: right = Compose
```

## Updates

Kaku checks GitHub for new releases in the background (enabled by default) and quietly downloads a newer version when it finds one. It never installs on its own: it shows a notification, and clicking it asks you to confirm first, since applying an update closes every window and stops running tasks.

Turn off background checks entirely:

```lua
config.check_for_updates = false
```

Change how often it checks (default `10800`, i.e. every 3 hours):

```lua
config.check_for_updates_interval_seconds = 86400  -- once a day
```

You can always update manually with `kaku update` or from the app menu, regardless of these settings.

## Custom Keybindings

Always **insert** into `config.keys`, never replace it. Replacing erases all Kaku defaults.

```lua
-- Navigate pane right
table.insert(config.keys, {
  key = 'RightArrow',
  mods = 'CMD|SHIFT',
  action = wezterm.action.ActivatePaneDirection('Right'),
})

-- Split pane horizontally
table.insert(config.keys, {
  key = 'Enter',
  mods = 'CMD|OPT',
  action = wezterm.action.SplitHorizontal({ domain = 'CurrentPaneDomain' }),
})
```

Full list of available actions: [WezTerm KeyAssignment reference](https://wezfurlong.org/wezterm/config/lua/keyassignment/).

## Advanced

**Enterprise proxy headers**

Add custom HTTP headers to Kaku Assistant API requests (for corporate proxies or API gateways):

```toml
# ~/.config/kaku/assistant.toml
custom_headers = ["X-Customer-ID: your-id", "X-Org: your-org"]
```

Note: `Authorization` and `Content-Type` are reserved and cannot be overridden.

**Extend Command Palette**

Add a custom command to Command Palette (`Cmd + Shift + P`) via `kaku.lua`:

```lua
wezterm.on('augment-command-palette', function(window, pane)
  if not pane then return {} end

  local cwd_obj = pane:get_current_working_dir()
  if not cwd_obj then return {} end

  -- Finder can only reveal local paths. file_path is already URL-decoded,
  -- so directories containing spaces or non-ASCII characters work too.
  local host = cwd_obj.host
  if cwd_obj.scheme ~= 'file'
      or (host and host ~= '' and host ~= 'localhost' and host ~= wezterm.hostname():lower()) then
    return {}
  end
  local cwd = cwd_obj.file_path
  if not cwd then return {} end

  return {
    {
      brief = 'Reveal in Finder',
      doc = 'Reveal current directory in Finder',
      action = wezterm.action_callback(function()
        wezterm.run_child_process({ 'open', '-R', cwd })
      end),
    },
  }
end)
```

**Full WezTerm Lua API**

Kaku uses WezTerm's configuration system. Any WezTerm config option works in `kaku.lua`. For the complete reference, see:

- [WezTerm config options](https://wezfurlong.org/wezterm/config/)
- [WezTerm Lua API](https://wezfurlong.org/wezterm/config/lua/)

### IME composition

To keep the character after the cursor visible when composing Korean, Japanese or Chinese text in the middle of a line:

```lua
config.ime_preedit_rendering = 'BuiltinInsert'
```

This shifts the remaining cells of the current row for display only and clips at the pane edge. The terminal buffer and bytes sent to the application stay unchanged. The default `Builtin` mode overlays the preedit; `System` does not select native preedit rendering on macOS.

---

### Faq (`docs/faq.md`)

# FAQ

## Is there a Windows or Linux version?

Not currently, Kaku is macOS-only.

## How is Kaku different from iTerm2, Warp, Ghostty, or WezTerm?

Kaku is a WezTerm-based Mac terminal with fonts, themes, tabs, panes, and shell tools already set, plus an optional assistant that uses the AI service you configure. See the comparison on the website: https://kaku.fun/compare

## Can I use a transparent window?

Yes. Add these lines below `-- User overrides:` in `~/.config/kaku/kaku.lua`, which already loads the bundled defaults into `config` and returns it:

```lua
config.window_background_opacity = 0.92
config.macos_window_background_blur = 20  -- optional blur, 0–100
```

## How do I turn off copy on select?

```lua
config.copy_on_select = false
```

## How do I customize keybindings?

Append to `config.keys`, do not replace it, as shown in [keybindings.md](keybindings.md#custom-keybindings). [configuration.md](configuration.md#custom-keybindings) has more examples.

## Can I control working directory inheritance?

Yes, individually for windows, tabs, and splits:

```lua
config.window_inherit_working_directory = true
config.tab_inherit_working_directory = true
config.split_pane_inherit_working_directory = true
```

All are enabled by default.

## How do I disable Kaku Assistant?

Run `kaku ai`, open Kaku Assistant settings, and set Enabled to Off. Or edit `~/.config/kaku/assistant.toml` directly:

```toml
enabled = false
```

## How do I use a custom LLM provider?

Run `kaku ai`, keep Auth Type set to `api_key`, and enter your Base URL, API Key, Simple Model, and Deep Model manually. Choose **API Mode** `chat_completions` for `/v1/chat/completions`, or `responses` for `/v1/responses`. If the Responses provider supports hosted search, set **Native Web Search** to On; no separate search provider or search API key is required.

## How do I restore default config?

Run `kaku reset`. It removes Kaku-managed integration and state, including Kaku AI memory and the `backups/` folder, while user-authored Lua outside managed blocks is preserved; see [cli.md](cli.md#kaku-reset) for the full list. Run `kaku init` again if you want shell integration back.

## The `kaku` command is missing. How do I recover it?

```bash
/Applications/Kaku.app/Contents/MacOS/kaku init --update-only
exec zsh -l
```

Then run `kaku doctor` to verify everything is healthy.

## How do I use Kaku's CLI from scripts?

```bash
kaku cli split-pane
kaku cli split-pane -- bash -c "echo hello"
kaku cli --help
```

See [cli.md](cli.md) for full reference.

## How do I enable the scrollbar?

Open `kaku config` and toggle the scrollbar option, or add to `~/.config/kaku/kaku.lua`:

```lua
config.enable_scroll_bar = true
```

## How do I scroll inside nano, vim, or another full-screen terminal app?

Enable alternate-screen wheel forwarding:

```lua
config.alternate_screen_wheel_scrolls_terminal = true
```

## How do I change the font? My font change isn't taking effect.

Font changes require explicitly setting `config.font` in your config:

```lua
config.font = wezterm.font('Your Font Name')
```

Note: Kaku's theme-aware font weight system only applies to the default JetBrains Mono stack. Once you set a custom font, Kaku will no longer override its weight automatically.

## My `window_padding` change isn't working.

`window_padding` takes a number or a string with a unit. Plain numbers are pixels, so `24` and `'24px'` mean the same thing, and the other units are `'pt'`, `'%'`, and `'cell'`:

```lua
config.window_padding = { left = '24px', right = '24px', top = '40px', bottom = '20px' }
```

## The screen jumps to the top while Claude Code is generating output.

Since V0.12.1 the viewport no longer jumps to the top during streaming output. If you scroll up mid-stream, pressing the down arrow or scrolling back down returns you to the current output.

## Cmd+Shift+Y sends a local path when inside an SSH session.

The yazi remote-files feature (`Cmd+Shift+R`) is designed for SSH sessions and mounts the remote filesystem via sshfs. `Cmd+Shift+Y` is for local yazi. Use `Cmd+Shift+R` when you are inside an SSH pane.

## Kaku's prompt shows up in other terminals, or is missing from them.

Kaku's shell integration is loaded by every zsh and fish, but the Starship prompt and Smart Tab only start inside Kaku, including tmux sessions started from Kaku. Other terminals keep whatever prompt you already had.

If you use Kaku's Starship as your one prompt everywhere, add this before the Kaku line in your `.zshrc` (`set -gx KAKU_PROMPT_EVERYWHERE 1` in `config.fish`):

```sh
export KAKU_PROMPT_EVERYWHERE=1
```

Smart Tab stays Kaku-only; `KAKU_SMART_TAB_DISABLE=1` turns it off.

## The `y` shell wrapper doesn't sync my directory on exit.

Make sure the Kaku fish/zsh shell integration is sourced. Check with `kaku doctor`. The `y` wrapper requires the shell init to be loaded. A bare `yazi` call will not sync the directory.

## How do I install or update Kaku with Homebrew?

Install the official cask:

```bash
brew install --cask kaku
```

Homebrew-managed installs update with `brew upgrade --cask kaku`, or with `kaku update` (it detects the cask).

If you still have the older tap `tw93/tap/kakuku`, keep upgrading that tap, or move to the official cask:

```bash
brew uninstall --cask tw93/tap/kakuku
brew install --cask kaku
```

## Claude Code or Codex notifications don't appear.

First check that macOS allows them: choose Kaku > Notification Settings... and turn on Allow Notifications. macOS never asks again after a denial, so when a notification gets dropped for this reason Kaku tells you the next time you switch back to it.

Claude Code 2.1.295 and later tell Kaku whether they are working, waiting on you or done through the program status protocol (OSC 7501), and Kaku turns waiting, done and errors into a notification and the tab's attention dot with no setting needed. Check with `claude --version`, and leave `preferredNotifChannel` at its default `auto`, since `ghostty` there makes Claude Code post a second notification of its own. Older versions only notify terminals they recognize and send nothing to Kaku, so on those set this in `~/.claude/settings.json`:

```json
{ "preferredNotifChannel": "ghostty" }
```

Codex works without changes: it rings the terminal bell and Kaku turns that into a notification. To see Codex's own message instead of a generic one, add this to `~/.codex/config.toml`:

```toml
[tui]
notification_method = "osc9"
```

Notifications appear while Kaku is in the background, not for the pane you are looking at.

## How do I change the global hotkey?

`Cmd + Opt + Ctrl + K` shows or hides Kaku from any app. It follows your current keyboard layout, so on Colemak or Dvorak it is whichever key types K. To pick another combination, change Global Hotkey in `kaku config`, or set it in Lua:

```lua
config.macos_global_hotkey = { key = 'j', mods = 'CMD|OPT|CTRL' }
```

By default (`macos_global_hotkey_mode = 'Drawer'`), the hotkey creates a dedicated terminal session on first use, then shows or hides that same session. Ordinary windows and their running tasks are not moved or hidden. The top-down drawer starts at the screen's top edge, covers the menu bar, and spans the display width and one third of its height. It has no native title bar, traffic-light buttons, or window-rounded corners. Opening Kaku from the Dock opens ordinary windows, not the drawer. The drawer is not restored as an ordinary window after restarting Kaku, and its geometry does not overwrite ordinary window geometry. Use `macos_global_hotkey_mode = 'Ordinary'` to toggle the selected ordinary window instead of invoking the dedicated drawer: Kaku selects the key ordinary window, then the main ordinary window, then the first available ordinary window, or creates one if none exists. This mode does not hide the entire application or toggle every window.

To reveal the hotkey window on the display containing the mouse pointer:

```lua
config.macos_global_hotkey_follow_mouse = true
```

This defaults to `false`, preserving AppKit's main-display behavior. The display is chosen each time the hotkey reveals the window; moving the pointer does not move a visible window, and hiding it does not move it to another display. If no display contains the pointer, Kaku falls back to the main display.

### Inherited appearance and drawer overrides

The drawer inherits the ordinary base configuration, including font, theme, opacity, blur, tab bar, scrollbar, and `window_padding`. This is configuration inheritance, not a copy of a particular ordinary window's live session, position, fullscreen state, or font zoom. Ordinary title-bar spacing does not apply to the drawer.

Set only the appearance fields you want to differ:

```lua
config.macos_global_hotkey_appearance = {
  font_size = 16,
  window_background_opacity = 0.85,
  macos_window_background_blur = 28,
  hide_tab_bar_if_only_one_tab = true,
}
```

Supported appearance fields are `font`, `font_rules`, `font_size`, `line_height`, `color_scheme`, `colors`, `window_background_opacity`, `text_background_opacity`, `macos_window_background_blur`, `window_frame`, `enable_tab_bar`, `hide_tab_bar_if_only_one_tab`, `tab_bar_at_bottom`, `use_fancy_tab_bar`, and `enable_scroll_bar`. Omitted fields, or fields set to `nil`, inherit the base configuration. Explicit `false` and `0` are overrides, not inheritance markers. Color overrides preserve unspecified base color fields; font rules and window frames use their usual replacement semantics.

Drawer content padding can be overridden one side at a time. For example, to use compact padding without changing ordinary windows:

```lua
config.macos_global_hotkey_padding = {
  left = '16px', right = '16px', top = '8px', bottom = '8px',
}
```

These values are an example, not separate product defaults. Omitted sides inherit the corresponding `window_padding` side, so `{ top = 0 }` changes only the top. Set `macos_global_hotkey_padding = nil` to restore inheritance. The same units as `window_padding` are supported (`px`, `pt`, `%`, `cell`); percentages are evaluated against the current drawer's full dimensions. Zero disables that side's content padding (an enabled scrollbar still reserves one cell when right padding is zero).

Base configuration reloads update inherited fields, and removing an override restores inheritance. Explicit per-window overrides from `window:set_config_overrides()` take precedence over the drawer appearance layer. Drawer font zoom is stored separately from ordinary-window zoom; an explicit drawer `font_size` takes precedence over saved drawer zoom when the window is created. Changing the configured drawer font size resets its live zoom without changing ordinary-window zoom.

### Drawer behavior

These behaviors have drawer-specific defaults rather than inheriting ordinary-window geometry:

```lua
config.macos_global_hotkey_height_percent = 100 / 3
config.macos_global_hotkey_animation_duration_ms = 200
config.macos_global_hotkey_hide_on_focus_loss = true
-- Inherit macos_fullscreen_extend_behind_notch unless explicitly set:
-- config.macos_global_hotkey_extend_behind_notch = false
```

Height is a percentage of the physical display frame in the range `(0, 100]`. The drawer remains full-width and top-aligned. Animation duration is in milliseconds, from 0 (no animation) to 10000; the macOS Reduce Motion setting always disables animation. Turning off focus-loss hiding pins the drawer until you hide it with the hotkey.

On a notched display, Kaku adds the current display's safe-area height above the content; external displays without a notch get no extra menu-bar clearance. This safe area is separate from configured top padding, including when top is zero. `macos_global_hotkey_extend_behind_notch = false` enables notch clearance independently of ordinary fullscreen windows; `true` disables it. When omitted, it inherits `macos_fullscreen_extend_behind_notch`. Moving the drawer to another display or unplugging it recalculates the safe area. None of these settings changes the system menu bar or display configuration.

## QR codes and terminal graphics look vertically stretched.

Kaku's default `line_height = 1.28` favors comfortable text spacing. Terminal graphics built from characters, such as QR codes, `neofetch` logos, and TUI bar charts, scale with the row height, so they render about 28% taller than in terminals with no extra line spacing. This is a typography trade-off, not a rendering bug: block characters must fill the whole cell so TUI borders and progress bars stay seamless.

If you want near-square graphics, lower the line height in `~/.config/kaku/kaku.lua`:

```lua
config.line_height = 1.1  -- or 1.0 to match terminals without extra spacing
```

Note that no terminal renders half-block QR codes perfectly square: with common monospace fonts the cell is naturally a bit taller than 2:1 even at `line_height = 1.0`.

## Can I use Kaku with tiling window managers (yabai, AeroSpace)?

Kaku is compatible with yabai and AeroSpace. If you see continuous flickering, it is usually caused by the tiling WM fighting with Kaku's fullscreen/resize logic. Disabling Kaku's native fullscreen (`config.native_macos_fullscreen_mode = false`) or excluding Kaku from the tiling WM's managed window list typically resolves it.

## How do I use Kaku through a phone or tablet remote desktop?

Hold anywhere for about half a second, then lift, to open the menu a right click would: on a tab you get New Tab, Tab Navigator and Close Tab, and on the terminal you get Copy, Paste, Split and the rest. This works in every window, as long as the remote client forwards a held press as a held left button, which UU does. In panes where a program such as vim or tmux takes mouse input, the hold stays with that program.

**Window > Touch Mode** adds a close button to every tab, since a finger has no hover. The tab bar keeps its height and look, and the terminal font and line height do not change.

## Is remote access free?

Yes. I pay for the relay server myself, as a thank-you to everyone who has been building Kaku in the open with me. Your privacy does not rest on trusting that server: what you type and see is encrypted between the browser and Kaku, so the relay only forwards data it cannot read. It is a server that does this one job, so the connection stays quick.

## The remote web page says the Mac is offline, or asks me to pair again.

The page reaches your Mac through Kaku, so the Mac has to be awake with Kaku running and remote access on. On a laptop, keep it plugged in and turn on Prevent automatic sleeping on power adapter when the display is off in System Settings > Battery > Options; on a desktop Mac it is Prevent automatic sleeping when the display is off in System Settings > Energy. Pairing lives in the browser: another browser, a private window or cleared site data needs a new link, which only `kaku remote` on the Mac can make, so pair each device you plan to use before you leave.

---

### Features (`docs/features.md`)

# Features

## Kaku Assistant

Kaku Assistant has two modes: automatic error recovery and on-demand command generation from natural language.

### Setup

Run `kaku ai` to open the AI settings panel. Enable Kaku Assistant and edit the model, auth, base URL, and API key fields directly.

| Field | Description |
| :--- | :--- |
| Auth Type | `api_key` (configure Kaku directly) or `codex` (follow your Codex connection) |
| Simple Model | Used for `#` command generation, command fixes, and lightweight chat |
| Deep Model | Used for primary `Cmd + L` / `k` chat and tool use |
| Base URL | OpenAI-compatible API root, such as `https://api.openai.com/v1` |
| API Key | Provider API key when Auth Type is `api_key` |

For custom providers configured directly in Kaku, keep Auth Type set to `api_key`, enter the provider's OpenAI-compatible Base URL, and set the model names manually. With Auth Type set to `codex`, Kaku follows the user-level Codex connection under `CODEX_HOME` (falling back to `~/.codex`), including API-key or ChatGPT login, the selected model provider, Base URL, query parameters, and provider headers. Project configs, named profiles, and CLI overrides are intentionally excluded. Unsupported authentication modes fail before Kaku sends a request.

### Error recovery

When a command exits with a non-zero status, Kaku Assistant automatically sends the failed command, exit code, working directory, and git branch to the LLM and displays a suggested fix inline. Press `Cmd + Shift + E` to paste the suggestion into the terminal. Dangerous commands (e.g. `rm -rf`, `git reset --hard`) are pasted but never auto-executed.

The assistant does not trigger on: `Ctrl+C` exits, help flags, bare package manager calls, git pull conflicts, or non-shell foreground processes.

### Natural language to command

Type `# <description>` at the prompt and press Enter to generate a shell command from plain English. Kaku intercepts the line before the shell sees it, sends your query along with the current directory and git branch to the LLM, and injects the resulting command back into the prompt ready to review and run.

```
# list all files modified in the last 7 days
# find and kill the process on port 3000
# compress the src folder excluding node_modules
```

The `#` prefix works in both zsh and fish. The original query stays visible while the request is in flight. If the model cannot produce a safe command, it injects a short explanation instead. Dangerous commands are loaded but flagged for review, never auto-executed.

### assistant.toml fields

The config lives at `~/.config/kaku/assistant.toml`:

| Field | Description |
| :--- | :--- |
| `enabled` | `true` to enable, `false` to disable |
| `api_key` | Your provider API key |
| `model` | Simple Model for `#` command generation, command fixes, and lightweight chat; `Follow Codex` resolves the Codex model |
| `chat_model` | Deep Model for primary `Cmd + L` / `k` chat and tool use; `Follow Codex` resolves the Codex model |
| `chat_model_choices` | Optional curated list of chat models for the overlay picker |
| `auto_fix_ignored_exit_codes` | Optional exit codes that should not trigger automatic command-fix suggestions, e.g. `[2]` |
| `base_url` | OpenAI-compatible API root URL |
| `api_mode` | `chat_completions` (default) or `responses` |
| `native_web_search` | Add the provider-hosted `web_search` tool in Responses mode, with no separate search API key |
| `custom_headers` | Extra HTTP headers for enterprise proxies, e.g. `["X-Customer-ID: your-id"]` |
| `web_search_provider` | Optional search backend: `brave`, `pipellm`, or `tavily` |
| `web_search_api_key` | API key for the selected search backend |
| `web_fetch_script` | Optional custom URL-to-Markdown fetch script |
| `chat_tools_enabled` | Set to `false` to disable tool calling for chat providers without tool support |
| `auth_type` | Advanced auth mode, e.g. `api_key` or `codex` |
| `memory_curator_model` | Optional cheaper model for background memory curation |

Older configs may still contain `fast_model`; Kaku treats it as the Simple Model and folds it back into `model` the next time the assistant settings are saved.

For a Responses-compatible endpoint, select `responses` under **API Mode** in `kaku ai`, or configure it directly:

```toml
base_url = "https://api.openai.com/v1"
api_mode = "responses"
native_web_search = true
```

Kaku sends these requests to `{base_url}/responses`. Native web search runs at the model provider, so `web_search_provider` and `web_search_api_key` are not needed. Keep `chat_completions` for providers that only implement `/chat/completions`.

When `auth_type = "codex"`, `base_url`, `api_key`, and `api_mode` do not define the connection. Kaku reads Codex's user configuration instead. New Codex-mode setups default both model fields to `Follow Codex`; either field can still be set to an explicit model as a Kaku-only override. The chat model switcher discovers available models from `{Codex provider base_url}/models` with that provider's resolved authentication, headers, and query parameters. Explicit Kaku model overrides are preserved when changing authentication modes. If the Codex config has no `model`, Kaku uses the first model returned by that endpoint.

## AI Chat Panel

Press `Cmd + L` to open the built-in AI chat panel. It streams Markdown answers, highlights code blocks, can include terminal context, and can use approved tools for project files, shell commands, web search, and memory. Press `Shift + Tab` inside the panel to toggle between the Simple Model and Deep Model when they are different.

Use History, Context, and Model above the input to browse saved conversations, attach terminal context, and switch models. With an empty input, Tab focuses these controls and code actions, and Enter activates the focused action. Shift+Enter inserts a newline. During a reply, Esc or Stop cancels generation and keeps the partial answer; Enter with a draft interrupts and sends it. Scrolling up keeps your reading position while more text arrives; Latest returns to the response. Drafts survive switching conversations within the open panel.

Code blocks offer Copy without adding visual line wraps. Completed, single-line shell blocks also offer Insert, which pastes into the original shell without executing it. Tool permission requests open a scrollable review of the complete target and parameters. Enter allows, Esc denies, and the wheel or Page Up/Page Down scrolls the details.

From a shell, use `k` or `kaku chat` for the same conversation store:

```bash
k "summarize the current project"
kaku chat
```

The standalone CLI is intentionally simpler than the overlay: it streams plain terminal text and supports `/new`, `/resume`, `/clear`, `/status`, `/memory`, and `/exit`.

## Terminal Interactions

Cmd+Click opens URLs and file paths, and also bare domains such as `github.com` that have no scheme prefix. The matcher is tuned to leave code identifiers alone: method calls like `df.info()` and namespaces like `System.Net` never turn into links.

Option+Click moves the shell cursor within the current input line, including across soft-wrapped continuation rows. It never crosses a hard newline, so clicks into scrollback are ignored rather than mangling history.

## Window Snapshots

Kaku saves multi-tab and multi-pane window layouts automatically when you close or hide a window. Use **Shell > Restore Previous Window** or `Cmd + Option + Shift + T` to reopen the last saved layout. Kaku tolerates missing or corrupted snapshot files and simply reports that no snapshot is available.

## AppleScript

Kaku ships a minimal AppleScript dictionary so it shows up in Script Editor and other automation tools. The exposed surface is intentionally small and read-only apart from `quit`.

```applescript
tell application "Kaku"
  get name        -- "Kaku"
  get version     -- e.g. "0.10.0"
  get frontmost   -- true / false
  quit            -- optional `saving ask|yes|no`
end tell
```

To browse the full dictionary, choose File > Open Dictionary in Script Editor and pick `/Applications/Kaku.app`. There is no `do script` verb, so AppleScript cannot run shell commands through Kaku.

## Lazygit Integration

Press `Cmd + Shift + G` to launch lazygit in the current pane. Kaku auto-detects the lazygit binary from PATH or common Homebrew locations.

When a git repo has uncommitted changes and lazygit has not been used in that directory yet, Kaku shows a one-time hint to remind you it is available.

Install lazygit with `brew install lazygit` or via `kaku init`.

## Yazi File Manager

Press `Cmd + Shift + Y` to launch yazi in the current pane. The shell wrapper `y` also launches yazi and syncs the shell working directory on exit.

**Theme sync**: Kaku automatically updates `~/.config/yazi/theme.toml` to match the active color scheme (Kaku Dark or Kaku Light). No manual yazi theme setup needed.

Install yazi with `brew install yazi` or via `kaku init`.

## Remote Files

Press `Cmd + Shift + R` to mount the current SSH session's remote filesystem locally via `sshfs` and open it in yazi.

Kaku auto-detects the SSH target from the active pane. The mount lives at `~/Library/Caches/dev.kaku/sshfs/<host>`.

Requirements: `sshfs` installed (`brew install --cask sshfs-mac`) and passwordless SSH auth (key-based) for the remote host.

## Remote Access

Run `kaku remote` to use this Mac's terminals from a desktop browser on another computer. It asks before turning remote access on, then prints a link that pairs one browser; bookmark the page after pairing to come back. The page has your tabs and splits, copy and paste, dropping files to upload them to the Mac, and Cmd+Click on a path to download it, and what you type and see is encrypted between the browser and Kaku. Remote access is off by default, and free: the relay is a server I pay for myself ([FAQ](faq.md#is-remote-access-free)). Commands are in the [CLI reference](cli.md#kaku-remote).

## Shell Suite

Kaku ships a curated set of shell plugins that load automatically inside Kaku sessions.

**Zsh plugins (built-in)**

- **z**: Smarter `cd` that learns your most-used directories. Use `z <dir>`, `z -l <dir>` to list matches, `z -t` for recent directories.
- **zsh-completions**: Extended completions for common CLI tools.
- **fast-syntax-highlighting**: Live command coloring and error highlighting, with richer colors and faster startup than zsh-syntax-highlighting.
- **zsh-autosuggestions**: Fish-style history-based completions as you type.

**Fish support**

Run `kaku init` to provision `~/.config/kaku/fish/kaku.fish` for fish users. `kaku doctor` verifies both zsh and fish integration paths.

**Optional tools (installed via `kaku init`)**

- **Starship**: Fast, customizable prompt with git and environment info.
- **Delta**: Syntax-highlighting pager for git diff and grep.
- **Lazygit**: Terminal git UI.
- **Yazi**: Terminal file manager.

**Smart Tab**

Kaku's Smart Tab overrides the Tab key in zsh to provide smarter completion behavior. It supports three modes:

| Mode | Behavior | Environment Variable |
| :--- | :--- | :--- |
| Completion First | Tab shows the completion list; press Right Arrow to accept autosuggestions | - |
| Suggestion First (default) | Tab accepts autosuggestions when available, falls back to completion | `KAKU_TAB_ACCEPT_SUGGEST_FIRST=1` |
| Off | Disables Smart Tab entirely, restoring native zsh Tab behavior | `KAKU_SMART_TAB_DISABLE=1` |

You can also set the mode via `kaku config` (the **Smart Tab** option under Behavior) or in `kaku.lua`:

```lua
config.smart_tab_mode = "suggestion_first"   -- default; Tab accepts autosuggestions first
config.smart_tab_mode = "completion_first"   -- Tab shows the completion list instead
config.smart_tab_mode = "off"                -- disable Smart Tab
```

If you prefer environment variables (for example, because you share your zshrc across terminals), add one of these before sourcing the Kaku shell integration:

```zsh
export KAKU_TAB_ACCEPT_SUGGEST_FIRST=0  # completion-first (suggestion-first is the default)
# or
export KAKU_SMART_TAB_DISABLE=1         # disable Smart Tab
```

Environment variables set in your shell rc take precedence over `kaku.lua` settings. Smart Tab is zsh only and runs inside Kaku sessions (`TERM_PROGRAM=Kaku`, or tmux started from a Kaku shell).

---

### Keybindings (`docs/keybindings.md`)

# Keybindings

`Opt` = Option/Alt, `Ctrl` = Control.

## Window

| Action | Shortcut |
| :--- | :--- |
| New window | `Cmd + N` |
| Close pane / tab / hide | `Cmd + W` |
| Hide application | `Cmd + H` |
| Minimize window | `Cmd + M` |
| Toggle fullscreen | `Cmd + Ctrl + F` |
| Toggle always on top | `Cmd + Shift + Up` |
| Quit | `Cmd + Q` |
| Toggle global window | `Cmd + Opt + Ctrl + K` |

> `Cmd + W` is smart: closes the active pane if there are multiple panes, closes the tab if there are multiple tabs or windows, otherwise hides the app.

## Tabs

| Action | Shortcut |
| :--- | :--- |
| New tab | `Cmd + T` |
| Switch to tab 1–8 / last tab | `Cmd + 1` – `Cmd + 8` / `Cmd + 9` |
| Previous tab | `Cmd + Shift + [` |
| Next tab | `Cmd + Shift + ]` |
| Open Tab Navigator | `Cmd + Shift + O` |
| Close tab | `Cmd + Shift + W` |
| Reopen closed tab | `Cmd + Shift + T` |
| Rename tab | `Cmd + Shift + I` or double-click tab title |

> Turn on **Tab Numbers** in Settings (`Cmd + ,`) to show each tab's number, as in `1:dirname`.

## Panes

| Action | Shortcut |
| :--- | :--- |
| Split vertical | `Cmd + D` |
| Split horizontal | `Cmd + Shift + D` |
| Toggle split direction | `Cmd + Shift + S` |
| Zoom / unzoom pane | `Cmd + Shift + Enter` |
| Navigate panes | `Cmd + Opt + Arrows` |
| Resize pane | `Cmd + Ctrl + Arrows` |

## Shell Editing

| Action | Shortcut |
| :--- | :--- |
| Jump word left / right | `Opt + Left` / `Opt + Right` |
| Jump to line start / end | `Cmd + Left` / `Cmd + Right` |
| Delete to line start | `Cmd + Backspace` |
| Delete word | `Opt + Backspace` |
| Newline without execute | `Cmd + Enter` or `Shift + Enter` |

## Font Size

| Action | Shortcut |
| :--- | :--- |
| Increase | `Cmd + =` |
| Decrease | `Cmd + -` |
| Reset | `Cmd + 0` |

## Kaku Features

| Action | Shortcut |
| :--- | :--- |
| Clear screen + scrollback | `Cmd + K` |
| Open Settings panel | `Cmd + ,` (type to filter model lists inside the panel) |
| Open Command Palette | `Cmd + Shift + P` |
| Open AI settings | `Cmd + Shift + A` |
| Open AI Chat | `Cmd + L` |
| Apply Kaku Assistant suggestion | `Cmd + Shift + E` |
| Restore previous window snapshot | `Cmd + Opt + Shift + T` |
| Open lazygit | `Cmd + Shift + G` |
| Open yazi file manager | `Cmd + Shift + Y` |
| Browse remote files (SSH) | `Cmd + Shift + R` |
| Open Doctor panel | `Ctrl + Shift + L` |

## Mouse

| Action | Trigger |
| :--- | :--- |
| Copy selection to clipboard | Release left mouse button after selecting |
| Open link | `Cmd + Click` |
| Move cursor to clicked column | `Opt + Click` (same row, shell prompt only) |

> The emoji and Nerd Font picker has no default shortcut. Open it from the Command Palette by searching `emoji`, or bind `CharSelect` as in the example below.

## Custom Keybindings

Add bindings to `~/.config/kaku/kaku.lua` by **appending** to `config.keys`. Do not assign a new table, this would erase Kaku's defaults.

```lua
-- ~/.config/kaku/kaku.lua (after loading bundled config)
table.insert(config.keys, {
  key = 'RightArrow',
  mods = 'CMD|SHIFT',
  action = wezterm.action.ActivatePaneDirection('Right'),
})

-- Example: rebind AI Chat to Cmd+Shift+Space (original default):
table.insert(config.keys, {
  key = 'Space',
  mods = 'CMD|SHIFT',
  action = wezterm.action.EmitEvent('kaku-ai-chat'),
})

-- Example: open the emoji and Nerd Font picker with Cmd+Shift+U:
table.insert(config.keys, {
  key = 'u',
  mods = 'CMD|SHIFT',
  action = wezterm.action.CharSelect {},
})

-- Example: copy and paste with Ctrl+Shift+C / Ctrl+Shift+V as on Linux:
table.insert(config.keys, {
  key = 'c',
  mods = 'CTRL|SHIFT',
  action = wezterm.action.CopyTo('Clipboard'),
})
table.insert(config.keys, {
  key = 'v',
  mods = 'CTRL|SHIFT',
  action = wezterm.action.PasteFrom('Clipboard'),
})
```

For the full list of available actions, see [WezTerm KeyAssignment reference](https://wezfurlong.org/wezterm/config/lua/keyassignment/).

---

### Release Checklist (`docs/release-checklist.md`)

# Release Checklist

## macOS Tab Bar Matrix

Before shipping a release that touches windowing, titlebar coloring, tab bar
layout, or transparency, build the app with `make app` and verify these macOS
config combinations manually:

| Tab position | Tab style | Opacity | Window state | Expected result |
| --- | --- | --- | --- | --- |
| Top | Fancy | Opaque | Windowed | Tab text/icons stay visible below integrated traffic lights. |
| Top | Fancy | Transparent | Windowed | Tab text/icons stay visible; transparent titlebar has no gap. |
| Top | Retro | Opaque | Windowed | Tab text/icons stay visible below integrated traffic lights. |
| Top | Retro | Transparent | Windowed | Tab text/icons stay visible; transparent titlebar has no gap. |
| Bottom | Fancy | Opaque | Windowed | Bottom tab bar is visible and top content clears traffic lights. |
| Bottom | Fancy | Transparent | Windowed | Bottom tab bar is visible; top titlebar area has no gap. |
| Bottom | Retro | Opaque | Windowed | Bottom tab bar is visible and top content clears traffic lights. |
| Bottom | Retro | Transparent | Windowed | Bottom tab bar is visible; top titlebar area has no gap. |
| Top | Fancy | Opaque | Fullscreen | Native titlebar does not cover the rendered tab bar. |
| Bottom | Fancy | Opaque | Fullscreen | Bottom tab bar remains visible after entering and leaving fullscreen. |

The key regression guard is `update_titlebar_background()` in
`window/src/os/macos/window.rs`: native titlebar coloring must remain opt-in for
opaque windows, otherwise `NSTitlebarContainerView` can cover the Metal-rendered
top tab bar.

## Window Drag Regression Checks

After the backend drag-decision tests pass, check the built app on macOS:
single click must not restore a maximized window; dragging must restore and
move it; double-click must zoom according to the system preference. Repeat
across displays, with a manually screen-filling window, and in fullscreen.
The automated matrix covers disagreements between cached maximized state,
native zoom state, and screen-filling geometry; it does not prove AppKit's
interactive tracking behavior.

## Repeatable Performance Checks

Use the same release build, display scale, window size, power mode, and machine
for before/after samples. Record the source revision and build profile. Debug
builds prove the harness works but are not release latency baselines.

| Scenario | Fixed workload | Evidence |
| --- | --- | --- |
| Startup | `RUNS=10 WARMUP=2 scripts/measure_startup_kaku.sh > startup.json` | Window and shell-ready mean, median and P95; cold resets only the fixture app cache. |
| Sustained output | One 80x24 pane, 100,000 lines of 79 ASCII characters plus newline, repeated three times | PTY byte rate, mux action latency, paint P95, peak RSS. |
| Multiple panes | Four panes at the same total window size, each printing the same output workload | Per-process CPU/RSS and paint P95; confirm input remains responsive in a fifth idle pane. |
| AI streaming | A local OpenAI-compatible fixture emitting 20 fixed-size deltas/second for 30 seconds, repeated three times | Paint P95 and CPU/RSS during streaming; cancel halfway once and confirm work stops. Network/provider latency is excluded. |

Enable `periodic_stat_logging = 5` in an isolated test config to collect existing
`gui.paint.impl`, `read_from_pane_pty.bytes.rate`, and
`send_actions_to_mux.perform_actions.latency` metrics. Use a fresh process per
scenario so percentile history does not mix workloads. The output workload is:

```sh
python3 -c 'import sys; sys.stdout.write(("x" * 79 + "\n") * 100000); sys.stdout.flush()'
```

Run `bash scripts/test_startup.sh` after changing measurement or budget logic.
The schema 2 startup budget checks launch-to-shell-ready means; zeros disable
individual budgets until a release-build baseline exists. Preparation and
process cleanup run outside the timed interval. The cross-terminal comparison
script refuses to run while a target terminal is already open, and only
terminates processes it started.

---

--- METRICS ---
- Files Extracted: 15
- Estimated Token Budget: ~20267 tokens
- Canonical Reference: https://codewiki.google/github.com/tw93/Kaku
