Skip to content
Merged
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
36 changes: 28 additions & 8 deletions lua/goose/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ function M.toggle_focus()
end

function M.change_mode(mode)
local info_mod = require("goose.info")
info_mod.set_config_value(info_mod.GOOSE_INFO.MODE, mode)
local info = require("goose.info")
info.set(info.KEY.MODE, mode)

if state.windows then
require('goose.ui.topbar').render()
Expand All @@ -57,26 +57,38 @@ function M.change_mode(mode)
end

function M.goose_mode_chat()
M.change_mode(require('goose.info').GOOSE_MODE.CHAT)
M.change_mode(require('goose.info').MODE.CHAT)
end

function M.goose_mode_auto()
M.change_mode(require('goose.info').GOOSE_MODE.AUTO)
M.change_mode(require('goose.info').MODE.AUTO)
end

function M.configure_provider()
core.configure_provider()
end

function M.mention_skill()
core.mention_skill()
end

function M.open_config()
local info = require('goose.info').parse_goose_info()
local info = require('goose.info')

local result = vim.system({ 'goose', 'info', '-v' }):wait()
if result.code ~= 0 then
vim.notify("Could not get config path", vim.log.levels.ERROR)
return
end

if not info.config_file then
local config_path = result.stdout:match("Config yaml:%s*(.-)[\n$]")
if not config_path then
vim.notify("Could not find config file path", vim.log.levels.ERROR)
return nil
return
end

require('goose.ui.ui').open_file_in_code_window(info.config_file)
require('goose.ui.ui').open_file_in_code_window(vim.trim(config_path))
info.invalidate()
end

function M.inspect_session()
Expand Down Expand Up @@ -282,6 +294,14 @@ M.commands = {
end
},

mention_skill = {
name = "GooseMentionSkill",
desc = "Mention a skill from ~/.claude/skills",
fn = function()
M.mention_skill()
end
},

open_config = {
name = "GooseOpenConfig",
desc = "Open goose config file",
Expand Down
1 change: 1 addition & 0 deletions lua/goose/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ M.defaults = {
next_message = ']]',
prev_message = '[[',
mention_file = '@',
mention_skill = '#',
toggle_pane = '<tab>',
prev_prompt_history = '<up>',
next_prompt_history = '<down>'
Expand Down
23 changes: 15 additions & 8 deletions lua/goose/context.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ local config = require("goose.config");
local M = {}

M.context = {
-- current file
current_file = nil,
cursor_data = nil,

-- attachments
mentioned_files = nil,
mentioned_skills = nil,
selections = nil,
linter_errors = nil
}

function M.unload_attachments()
M.context.mentioned_files = nil
M.context.mentioned_skills = nil
M.context.selections = nil
M.context.linter_errors = nil
end
Expand Down Expand Up @@ -83,7 +83,7 @@ function M.add_file(file)
end

if vim.fn.filereadable(file) ~= 1 then
vim.notify("File not added to context. Could not read.")
vim.notify("File not added to context. Could not read.", vim.log.levels.WARN)
return
end

Expand All @@ -92,6 +92,16 @@ function M.add_file(file)
end
end

function M.add_skill(skill_name)
if not M.context.mentioned_skills then
M.context.mentioned_skills = {}
end

if not vim.tbl_contains(M.context.mentioned_skills, skill_name) then
table.insert(M.context.mentioned_skills, skill_name)
end
end

function M.delta_context()
local context = vim.deepcopy(M.context)
local last_context = require('goose.state').last_sent_context
Expand Down Expand Up @@ -164,11 +174,8 @@ function M.format_message(prompt)
local info = require('goose.info')
local context = nil

if info.parse_goose_info().goose_mode == info.GOOSE_MODE.CHAT then
-- For chat mode only send selection context
context = {
selections = M.context.selections
}
if info.mode() == info.MODE.CHAT then
context = { selections = M.context.selections }
else
context = M.delta_context()
end
Expand Down
24 changes: 20 additions & 4 deletions lua/goose/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local context = require("goose.context")
local session = require("goose.session")
local ui = require("goose.ui.ui")
local job = require('goose.job')
local keymap = require('goose.config').get('keymap')

function M.select_session()
local all_sessions = session.get_sessions()
Expand Down Expand Up @@ -124,16 +125,16 @@ function M.add_file_to_context()
mention_cb(file.name)
context.add_file(file.path)
end)
end)
end, keymap.window.mention_file)
end

function M.configure_provider()
local info_mod = require("goose.info")
local info = require("goose.info")
require("goose.provider").select(function(selection)
if not selection then return end

info_mod.set_config_value(info_mod.GOOSE_INFO.PROVIDER, selection.provider)
info_mod.set_config_value(info_mod.GOOSE_INFO.MODEL, selection.model)
info.set(info.KEY.PROVIDER, selection.provider)
info.set(info.KEY.MODEL, selection.model)

if state.windows then
require('goose.ui.topbar').render()
Expand All @@ -143,6 +144,21 @@ function M.configure_provider()
end)
end

function M.mention_skill()
if not require('goose.info').is_extension_enabled('skills') then
vim.notify("Skills extension is not enabled", vim.log.levels.WARN)
return
end

require('goose.ui.mention').mention(function(mention_cb)
require("goose.skills").select(function(skill)
if not skill then return end
mention_cb(skill.name)
context.add_skill(skill.name)
end)
end, keymap.window.mention_skill)
end

function M.stop()
if (state.goose_run_job) then job.stop(state.goose_run_job) end
state.goose_run_job = nil
Expand Down
107 changes: 64 additions & 43 deletions lua/goose/info.lua
Original file line number Diff line number Diff line change
@@ -1,63 +1,84 @@
local M = {}
local util = require('goose.util')

M.GOOSE_INFO = {
MODEL = "GOOSE_MODEL",
PROVIDER = "GOOSE_PROVIDER",
MODE = "GOOSE_MODE",
CONFIG = "Config yaml",
}

M.GOOSE_MODE = {
M.MODE = {
CHAT = "chat",
AUTO = "auto"
}

-- Parse the output of `goose info -v` command
function M.parse_goose_info()
local result = {}
M.KEY = {
MODE = "GOOSE_MODE",
MODEL = "GOOSE_MODEL",
PROVIDER = "GOOSE_PROVIDER"
}

local cache = nil

local handle = io.popen("goose info -v")
if not handle then
return result
end
local function load_config()
if cache then return cache end

local output = handle:read("*a")
handle:close()
local result = vim.system({ 'goose', 'info', '-v' }):wait()
if result.code ~= 0 then return nil end

local model = output:match(M.GOOSE_INFO.MODEL .. ":%s*(.-)\n") or output:match(M.GOOSE_INFO.MODEL .. ":%s*(.-)$")
if model then
result.goose_model = vim.trim(model)
end
local config_path = result.stdout:match("Config yaml:%s*(.-)[\n$]")
if not config_path then return nil end

local provider = output:match(M.GOOSE_INFO.PROVIDER .. ":%s*(.-)\n") or
output:match(M.GOOSE_INFO.PROVIDER .. ":%s*(.-)$")
if provider then
result.goose_provider = vim.trim(provider)
end
config_path = vim.trim(config_path)

local mode = output:match(M.GOOSE_INFO.MODE .. ":%s*(.-)\n") or output:match(M.GOOSE_INFO.MODE .. ":%s*(.-)$")
if mode then
result.goose_mode = vim.trim(mode)
end
local file = io.open(config_path, "r")
if not file then return nil end

local content = file:read("*a")
file:close()

local config_file = output:match(M.GOOSE_INFO.CONFIG .. ":%s*(.-)\n") or
output:match(M.GOOSE_INFO.CONFIG .. ":%s*(.-)$")
if config_file then
result.config_file = vim.trim(config_file)
local data = require('goose.util').parse_yaml(content)
if data then
cache = {
path = config_path,
data = data
}
end

return result
return cache
end

-- Set a value in the goose config file
function M.set_config_value(key, value)
local info = M.parse_goose_info()
if not info.config_file then
return false, "Could not find config file path"
end
function M.model()
local cfg = load_config()
return cfg and cfg.data[M.KEY.MODEL]
end

function M.provider()
local cfg = load_config()
return cfg and cfg.data[M.KEY.PROVIDER]
end

function M.mode()
local cfg = load_config()
return cfg and cfg.data[M.KEY.MODE]
end

function M.extensions()
local cfg = load_config()
return cfg and cfg.data.extensions or {}
end

function M.is_extension_enabled(name)
local exts = M.extensions()
return exts[name] and exts[name].enabled == true
end

function M.set(key, value)
local cfg = load_config()
if not cfg then return false, "Could not load config" end

local util = require('goose.util')
local ok, err = util.set_yaml_value(cfg.path, key, value)
if ok then cache = nil end

return ok, err
end

return util.set_yaml_value(info.config_file, key, value)
function M.invalidate()
cache = nil
end

return M
Loading