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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 74 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ without leaving your editor.
- ✨ **Context-Aware Prompts**: Automatically include file content, cursor position, and diagnostics in your prompts.
- 📝 **Prompt Library**: A library of pre-defined prompts for common tasks like explaining code, fixing issues, or writing tests.
- 🔄 **Session Persistence**: Keep your CLI sessions alive with `tmux` and `zellij` integration.
- 🪟 **External Sessions**: Run CLI tools in separate `kitty` windows/tabs/splits (no persistence).
- 📂 **Automatic File Watching**: Automatically reloads files in Neovim when they are modified by AI tools.

- **🔌 Extensible and Customizable**
Expand Down Expand Up @@ -318,14 +319,16 @@ local defaults = {
nav = nil,
},
---@class sidekick.cli.Mux
---@field backend? "tmux"|"zellij" Multiplexer backend to persist CLI sessions
---@field backend? "tmux"|"zellij"|"kitty" Multiplexer backend to persist CLI sessions
mux = {
backend = vim.env.ZELLIJ and "zellij" or "tmux", -- default to tmux unless zellij is detected
backend = vim.env.ZELLIJ and "zellij" or vim.env.KITTY_LISTEN_ON and "kitty" or "tmux", -- default based on environment
enabled = false,
-- terminal: new sessions will be created for each CLI tool and shown in a Neovim terminal
-- window: when run inside a terminal multiplexer, new sessions will be created in a new tab
-- split: when run inside a terminal multiplexer, new sessions will be created in a new split
-- NOTE: zellij only supports `terminal`
-- NOTE: kitty supports all modes but does not persist sessions (tied to window lifecycle)
-- NOTE: with kitty, `terminal` opens a new OS window, which is more semantically correct for kitty
create = "terminal", ---@type "terminal"|"window"|"split"
split = {
vertical = true, -- vertical or horizontal split
Expand Down Expand Up @@ -894,6 +897,70 @@ and **CLI sessions** in your statusline.

</details>

## 😺 Kitty Terminal

Sidekick supports the Kitty terminal emulator as a session backend, allowing you to run CLI tools in external Kitty windows, tabs, or splits.

The Kitty backend supports:

- **External sessions**: CLI tools run in separate Kitty windows/tabs/splits
- **Multiple creation modes**:
- `terminal`: Create a new OS window
- `window`: Create a new tab in the current OS window
- `split`: Create a split in the current tab

### Requirements

To use kitty as the backend, you must have:

1. Kitty terminal emulator installed
2. [Remote control](https://sw.kovidgoyal.net/kitty/remote-control) is enabled.
- You can do this, by either starting `kitty` with the following command:

```bash
# For Linux only:
kitty -o allow_remote_control=yes --single-instance --listen-on unix:@mykitty

# Other UNIX systems:
kitty -o allow_remote_control=yes --single-instance --listen-on unix:/tmp/mykitty
```

- OR, by defining the following in your `kitty.conf`:

```text
# For Linux only:
allow_remote_control yes
listen_on unix:@mykitty

# Other UNIX systems:
allow_remote_control yes
listen_on unix:/tmp/mykitty
```

### Limitations

- **No session persistence**: Sessions are tied to the OS window lifecycle
- **No integrated terminal**: The `terminal` create mode opens a new OS-window, not a Neovim terminal
- **Cannot detach/reattach**: Unlike tmux/zellij, you can't detach and reattach

### Configuration Example

```lua
opts = {
cli = {
mux = {
enabled = true,
backend = "kitty",
create = "split", -- or "window" or "terminal"
split = {
vertical = true,
size = 0.5,
},
},
},
}
```

## ❓ FAQ

### Does sidekick.nvim replace Copilot's inline suggestions?
Expand Down Expand Up @@ -930,19 +997,22 @@ Use them together for the complete experience!

### Terminal sessions not persisting?

Make sure you have tmux or zellij installed and enable the multiplexer:
Make sure you have a terminal multiplexer installed and enable it:

```lua
opts = {
cli = {
mux = {
enabled = true,
backend = "tmux", -- or "zellij"
backend = "tmux", -- or "zellij" or "kitty"
},
},
}
```

> [!NOTE]
> Kitty sessions are not persistent and will close when the Kitty window is closed. For true persistence, use tmux or zellij.

### Do I need a GitHub Copilot subscription?

Yes, but only for the **NES feature** (Next Edit Suggestions). The **AI CLI integration** works independently with any CLI tool (Claude, Gemini, etc.) and doesn't require Copilot.
Expand Down
15 changes: 9 additions & 6 deletions lua/sidekick/cli/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,16 @@ end
function M.focus(opts)
opts = filter_opts(opts)
State.with(function(state)
if not state.terminal then
return
end
if state.terminal:is_focused() then
state.terminal:blur()
if state.terminal then
if state.terminal:is_focused() then
state.terminal:blur()
else
state.terminal:focus()
end
elseif state.session then
state.session:focus()
else
state.terminal:focus()
return
end
end, {
attach = true,
Expand Down
9 changes: 8 additions & 1 deletion lua/sidekick/cli/session/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ function B:attach() end
--- Detach from an existing session
function B:detach() end

--- Focus on an external session (kitty/tmux/zellij pane)
function B:focus() end

--- Start a new session
--- If the backend returns a Cmd, a new terminal session will be spawned
---@return sidekick.cli.terminal.Cmd?
Expand Down Expand Up @@ -121,7 +124,11 @@ function M.setup()
end
M.did_setup = true
Config.tools() -- load tools, since they may register session backends
local session_backends = { tmux = "sidekick.cli.session.tmux", zellij = "sidekick.cli.session.zellij" }
local session_backends = {
tmux = "sidekick.cli.session.tmux",
zellij = "sidekick.cli.session.zellij",
kitty = "sidekick.cli.session.kitty",
}
for name, mod in pairs(session_backends) do
if vim.fn.executable(name) == 1 then
M.register(name, require(mod))
Expand Down
Loading
Loading