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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,18 @@ current file, selection, diagnostics, and more.

</details>

### Custom Working Directory

You can override the working directory used when resolving context variables
by passing a `cwd` option to `send()`, `prompt()`, or `render()`:

```lua
require("sidekick.cli").send({
msg = "{file}",
cwd = "/path/to/custom/directory"
})
```

### Snacks.nvim Picker Integration

If you're using [snacks.nvim](https://github.com/folke/snacks.nvim), you can send picker selections directly to Sidekick's AI CLI tools. This is useful for sending search results, grep matches, or file selections as context.
Expand Down
10 changes: 6 additions & 4 deletions lua/sidekick/cli/context/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ M.context = {
local C = {}
C.__index = C

function C.new()
---@param cwd? string Optional cwd override for path resolution
function C.new(cwd)
local self = setmetatable({}, C)
self.ctx = M.ctx()
self.ctx = M.ctx(cwd)
self.context = {}
return self
end
Expand Down Expand Up @@ -190,7 +191,8 @@ function M.fn(name)
return Config.cli.context[name] or M.context[name] or nil
end

function M.ctx()
---@param cwd_override? string Optional cwd override for path resolution
function M.ctx(cwd_override)
---@param w integer
local wins = vim.tbl_filter(function(w)
local buf = vim.api.nvim_win_get_buf(w)
Expand All @@ -206,7 +208,7 @@ function M.ctx()
return {
win = win,
buf = buf,
cwd = vim.fs.normalize(vim.fn.getcwd(win)),
cwd = vim.fs.normalize(cwd_override or vim.fn.getcwd(win)),
row = cursor[1],
col = cursor[2] + 1,
range = M.selection(buf),
Expand Down
9 changes: 7 additions & 2 deletions lua/sidekick/cli/context/location.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ function M.get(ctx, opts)
if not name or name == "" then
name = "[No Name]"
else
local cwd = ctx.cwd or vim.fn.getcwd(0)
local ok, rel = pcall(vim.fs.relpath, cwd, name)
local cwd = vim.fs.normalize(ctx.cwd or vim.fn.getcwd(0))
local normalized_name = vim.fs.normalize(name)
-- Try vim.fs.relpath first
local ok, rel = pcall(vim.fs.relpath, normalized_name, cwd)
if ok and rel and rel ~= "" and rel ~= "." then
name = rel
elseif normalized_name:sub(1, #cwd + 1) == cwd .. "/" then
-- Fallback: manual string prefix removal
name = normalized_name:sub(#cwd + 2)
end
end

Expand Down
14 changes: 10 additions & 4 deletions lua/sidekick/cli/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ local M = {}

---@class sidekick.cli.Send: sidekick.cli.Show,sidekick.cli.Message
---@field submit? boolean
---@field cwd? string Override cwd for context resolution (e.g., parent directory)

--- Keymap options similar to `vim.keymap.set` and `lazy.nvim` mappings
---@class sidekick.cli.Keymap: vim.keymap.set.Opts
Expand All @@ -53,8 +54,12 @@ local function filter_opts(opts)
return opts
end

---@class sidekick.cli.PromptOpts
---@field cb? fun(msg?:string, text?:sidekick.Text[])
---@field cwd? string Override cwd for context resolution

--- Select a prompt to send
---@param opts? sidekick.cli.Prompt|{cb:nil}
---@param opts? sidekick.cli.PromptOpts
---@overload fun(cb:fun(msg?:string))
function M.prompt(opts)
opts = opts or {}
Expand Down Expand Up @@ -161,8 +166,9 @@ end

-- Render a message template or prompt
---@param opts? sidekick.cli.Message|string
function M.render(opts)
return Context.get():render(opts or "")
---@param cwd? string Optional cwd override for context resolution
function M.render(opts, cwd)
return Context.get(cwd):render(opts or "")
end

--- Send a message or prompt to a CLI
Expand All @@ -178,7 +184,7 @@ function M.send(opts)

local msg, text = "", opts.text ---@type string?, sidekick.Text[]?
if not text then
msg, text = M.render(opts)
msg, text = M.render(opts, opts.cwd)
if msg == "" or not text then
Util.warn("Nothing to send.")
return
Expand Down
7 changes: 2 additions & 5 deletions lua/sidekick/cli/ui/prompt.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ local Context = require("sidekick.cli.context")

local M = {}

---@class sidekick.cli.Prompt
---@field cb fun(msg?:string, text?:sidekick.Text[])

---@param opts sidekick.cli.Prompt
---@param opts sidekick.cli.PromptOpts
function M.select(opts)
assert(type(opts) == "table", "opts must be a table")
local prompts = vim.tbl_keys(Config.cli.prompts) ---@type string[]
table.sort(prompts)
local context = Context.get()
local context = Context.get(opts.cwd)

---@param msg string
local function tpl(msg)
Expand Down