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
7 changes: 7 additions & 0 deletions lua/sidekick/cli/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ local M = {}
---@field prompt? string
---@field text? sidekick.Text[]

---@alias sidekick.cli.Mode "new"|"continue"|"resume"

---@class sidekick.cli.Config
---@field cmd string[] Command to run the CLI tool
---@field env? table<string, string|false> Environment variables to set when running the command
Expand All @@ -21,12 +23,15 @@ local M = {}
---@field mux_focus? boolean wether the tool needs to be focused in order to receive input
---@field format? fun(text:sidekick.Text[], str:string):string?
---@field native_scroll? boolean whether the tool handles scrolling natively
---@field continue? string[] Extra args appended to `cmd` to continue the most recent session
---@field resume? string[] Extra args appended to `cmd` to resume a session (typically via the tool's own picker)

---@class sidekick.cli.Show
---@field name? string
---@field focus? boolean
---@field filter? sidekick.cli.Filter
---@field all? boolean
---@field mode? sidekick.cli.Mode|false mode used when starting a new session (bypasses the picker)

---@class sidekick.cli.Hide
---@field name? string
Expand Down Expand Up @@ -91,6 +96,7 @@ function M.show(opts)
attach = true,
filter = opts.filter,
focus = opts.focus,
mode = opts.mode,
show = true,
})
end
Expand All @@ -112,6 +118,7 @@ function M.toggle(opts)
end, {
attach = true,
filter = opts.filter,
mode = opts.mode,
})
end

Expand Down
10 changes: 9 additions & 1 deletion lua/sidekick/cli/session/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,18 @@ function B.sessions()
error("Backend:sessions() not implemented")
end

---@param state sidekick.cli.session.Opts
---@param state sidekick.cli.session.Opts|{mode?:sidekick.cli.Mode}
function M.new(state)
local tool = state.tool
tool = type(tool) == "string" and Config.get_tool(tool) or tool --[[@as sidekick.cli.Tool]]
local mode = state.mode --[[@as sidekick.cli.Mode?]]
if mode and mode ~= "new" then
local extra = tool.config[mode]
if extra and #extra > 0 then
local cmd = vim.list_extend(vim.deepcopy(tool.cmd), extra)
tool = tool:clone({ cmd = cmd })
end
end
local backend = state.backend or (Config.cli.mux.enabled and Config.cli.mux.backend or "terminal")
local super = assert(M.backends[backend], "unknown backend: " .. backend)
local meta = getmetatable(state)
Expand Down
57 changes: 54 additions & 3 deletions lua/sidekick/cli/state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ local M = {}
---@field session? sidekick.cli.Session
---@field started? boolean
---@field terminal? sidekick.cli.Terminal
---@field mode? sidekick.cli.Mode

---@class sidekick.cli.Filter
---@field attached? boolean
Expand All @@ -30,6 +31,7 @@ local M = {}
---@field focus? boolean
---@field attach? boolean
---@field all? boolean
---@field mode? sidekick.cli.Mode|false

---@param t sidekick.cli.State
---@param filter? sidekick.cli.Filter
Expand Down Expand Up @@ -137,6 +139,45 @@ function M.get(filter)
return ret
end

---@param tool sidekick.cli.Tool
---@param cb fun(mode?: sidekick.cli.Mode)
---@param forced? sidekick.cli.Mode|false
local function pick_mode(tool, cb, forced)
local has_continue = type(tool.config.continue) == "table" and #tool.config.continue > 0
local has_resume = type(tool.config.resume) == "table" and #tool.config.resume > 0
if not (has_continue or has_resume) then
return cb(nil)
end
local default = forced == nil and Config.cli.resume or forced
if default == false or default == "new" then
return cb(nil)
elseif default == "continue" and has_continue then
return cb("continue")
elseif default == "resume" and has_resume then
return cb("resume")
end
local choices = { "new" } ---@type sidekick.cli.Mode[]
if has_continue then
choices[#choices + 1] = "continue"
end
if has_resume then
choices[#choices + 1] = "resume"
end
local labels = {
new = "new session",
continue = "continue most recent session",
resume = "resume a session (pick in tool)",
}
vim.ui.select(choices, {
prompt = ("Start `%s`:"):format(tool.name),
format_item = function(m)
return labels[m]
end,
}, function(choice)
cb(choice)
end)
end

--- Executes a callback with one or more attached sessions.
---@param cb fun(state: sidekick.cli.State, attached?: boolean):any?
---@param opts? sidekick.cli.With
Expand All @@ -149,8 +190,18 @@ function M.with(cb, opts)
if not state then
return
end
local ret, attached = M.attach(state, { show = opts.show, focus = opts.focus })
cb(ret, attached)
local function proceed()
local ret, attached = M.attach(state, { show = opts.show, focus = opts.focus })
cb(ret, attached)
end
if state.session == nil then
pick_mode(state.tool, function(mode)
state.mode = mode
proceed()
end, opts.mode)
else
proceed()
end
end)

local filter_attached = Util.merge(opts.filter, { attached = true })
Expand Down Expand Up @@ -182,7 +233,7 @@ function M.attach(state, opts)
local tool = state.tool

-- if the session is already attached, the below is a no-op
local session = state.session or Session.new({ tool = tool.name })
local session = state.session or Session.new({ tool = tool.name, mode = state.mode })
session = Session.attach(session)

state = M.get_state(session) -- update state
Expand Down
4 changes: 4 additions & 0 deletions lua/sidekick/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ local defaults = {
-- Work with AI cli tools directly from within Neovim
cli = {
watch = true, -- notify Neovim of file changes done by AI CLI tools
-- When starting a tool that supports resume/continue, pick how to start it:
-- "ask" (default): show a picker when the tool has `continue` or `resume` defined
-- "new" / "continue" / "resume": always start with that mode, no picker
resume = "ask", ---@type "ask"|"new"|"continue"|"resume"
---@class sidekick.win.Opts
win = {
--- This is run when a new terminal is created, before starting it.
Expand Down