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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ Note on quality vs cost: Using OpenAI `gpt-5-mini` or `gpt-4.1-mini` (faster) ge
- `:AiCommitMsgDisable` - Disable automatic commit message generation
- `:AiCommitMsgEnable` - Enable automatic commit message generation
- `:AiCommitMsgAllModels` - Generate commit messages across all configured provider models for your staged diff; opens a buffer with per-model results, timing, and (when pricing is configured) estimated cost — useful for model selection
- `:AiCommitMsgDebug` - Toggle debug mode. When enabled, shows verbose `DEBUG`-level notifications useful for troubleshooting
- `:AiCommitMsgTestMatrix [diffs_dir] [out_file]` - Run a prompt/model matrix against `.diff` fixtures. Set `AI_COMMIT_MSG_DRY_RUN=1` to only collect prompt sizes without API calls; otherwise writes JSONL lines to `out_file` if provided

## Choosing a Model
Expand Down
7 changes: 4 additions & 3 deletions lua/ai_commit_msg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,14 @@ end
function M.setup(opts)
M.config = vim.tbl_deep_extend("force", M.config, opts or {})

vim.notify("ai-commit-msg.nvim: Setup called", vim.log.levels.DEBUG)
local cfg_notify = require("ai_commit_msg.config").notify
cfg_notify("ai-commit-msg.nvim: Setup called", vim.log.levels.DEBUG)

if M.config.enabled then
require("ai_commit_msg.autocmds").setup(M.config)
vim.notify("ai-commit-msg.nvim: Autocmds registered", vim.log.levels.DEBUG)
cfg_notify("ai-commit-msg.nvim: Autocmds registered", vim.log.levels.DEBUG)
else
vim.notify("ai-commit-msg.nvim: Plugin disabled", vim.log.levels.DEBUG)
cfg_notify("ai-commit-msg.nvim: Plugin disabled", vim.log.levels.DEBUG)
end
end

Expand Down
17 changes: 9 additions & 8 deletions lua/ai_commit_msg/autocmds.lua
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
local M = {}
local augroup_name = "ai_commit_msg"
local augroup = nil
local cfg_notify = require("ai_commit_msg.config").notify

function M.setup(config)
augroup = vim.api.nvim_create_augroup(augroup_name, { clear = true })

vim.api.nvim_create_autocmd("BufWinEnter", {
vim.api.nvim_create_autocmd("FileType", {
group = augroup,
pattern = "COMMIT_EDITMSG",
pattern = "gitcommit",
callback = function(arg)
vim.notify("ai-commit-msg.nvim: COMMIT_EDITMSG buffer detected", vim.log.levels.DEBUG)
cfg_notify("ai-commit-msg.nvim: gitcommit buffer detected", vim.log.levels.DEBUG)

-- Setup keymaps
if config.keymaps.quit then
Expand All @@ -36,7 +37,7 @@ function M.setup(config)

-- Only prompt if HEAD changed (meaning a commit was made)
if head_after == head_before or head_after == "" then
vim.notify(
cfg_notify(
"ai-commit-msg.nvim: No commit was created (empty message or cancelled)",
vim.log.levels.DEBUG
)
Expand All @@ -59,11 +60,11 @@ function M.setup(config)
if exit_code == 0 then
vim.schedule(function()
vim.api.nvim_buf_delete(term_buf, { force = true })
vim.notify("Push successful", vim.log.levels.INFO)
cfg_notify("Push successful", vim.log.levels.INFO)
end)
else
vim.schedule(function()
vim.notify("Push failed - check terminal for details", vim.log.levels.ERROR)
cfg_notify("Push failed - check terminal for details", vim.log.levels.ERROR)
end)
end
end,
Expand All @@ -72,7 +73,7 @@ function M.setup(config)
end

if pull_enabled then
vim.notify("Pulling latest changes before push...", vim.log.levels.INFO)
cfg_notify("Pulling latest changes before push...", vim.log.levels.INFO)
local cmd = { "git", "pull" }
for _, a in ipairs(pull_args) do
table.insert(cmd, a)
Expand All @@ -83,7 +84,7 @@ function M.setup(config)
do_push()
else
local err = pull_obj.stderr or pull_obj.stdout or "git pull failed"
vim.notify("git pull failed; aborting push: " .. vim.fn.trim(err), vim.log.levels.ERROR)
cfg_notify("git pull failed; aborting push: " .. vim.fn.trim(err), vim.log.levels.ERROR)
end
end)
end)
Expand Down
25 changes: 25 additions & 0 deletions lua/ai_commit_msg/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,29 @@ M.default = {
},
}

--- Notify helper that respects the notifications config.
--- DEBUG messages only show when vim.g.ai_commit_msg_debug is set.
--- ERROR/WARN always show. INFO is gated by notifications config.
---@param msg string
---@param level number|nil vim.log.levels value
---@param opts table|nil extra options for vim.notify
function M.notify(msg, level, opts)
if level == vim.log.levels.DEBUG then
if vim.g.ai_commit_msg_debug then
vim.notify(msg, level, opts)
end
return
end
if level == vim.log.levels.ERROR or level == vim.log.levels.WARN then
vim.notify(msg, level, opts)
return
end
-- INFO: respect notifications config
local ok, plugin = pcall(require, "ai_commit_msg")
if ok and plugin.config and plugin.config.notifications == false then
return
end
vim.notify(msg, level, opts)
end

return M
68 changes: 24 additions & 44 deletions lua/ai_commit_msg/generator.lua
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
local M = {}
local cfg_notify = require("ai_commit_msg.config").notify

local function get_spinner()
local spinner = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }
return spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner + 1]
end

local function notify(msg, level, config)
if config.notifications then
vim.notify(msg, level, {
title = "AI Commit",
})
end
end

local function call_api(config, diff, callback)
local providers = require("ai_commit_msg.providers")
return providers.call_api(config, diff, callback)
end

function M.generate(config, callback)
vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Starting generation", vim.log.levels.DEBUG)
cfg_notify("ai-commit-msg.nvim: Starting generation", vim.log.levels.DEBUG)
end)

local spinner_timer
local notif_id = "ai-commit-msg"

-- Start spinner if enabled
if config.spinner and config.notifications then
if config.spinner then
local function update_spinner()
if not spinner_timer or spinner_timer:is_closing() then
return
Expand All @@ -45,7 +38,7 @@ function M.generate(config, callback)
end
elseif config.notifications then
vim.schedule(function()
notify("Generating commit message...", vim.log.levels.INFO, config)
cfg_notify("Generating commit message...", vim.log.levels.INFO, { title = "AI Commit" })
end)
end

Expand All @@ -65,15 +58,11 @@ function M.generate(config, callback)

vim.schedule(function()
local error_msg = "Failed to get git diff: " .. (diff_res.stderr or "Unknown error")
vim.notify("ai-commit-msg.nvim: " .. error_msg, vim.log.levels.ERROR)
-- Clear spinner notification with error message
if config.notifications then
vim.notify("❌ " .. error_msg, vim.log.levels.ERROR, {
id = notif_id,
title = "AI Commit",
timeout = 3000,
})
end
cfg_notify("❌ " .. error_msg, vim.log.levels.ERROR, {
id = notif_id,
title = "AI Commit",
timeout = 3000,
})
if callback then
callback(false, error_msg)
end
Expand All @@ -91,25 +80,20 @@ function M.generate(config, callback)
spinner_timer = nil

vim.schedule(function()
local error_msg = "No staged changes to commit"
vim.notify("ai-commit-msg.nvim: " .. error_msg, vim.log.levels.WARN)
-- Clear spinner notification with warning message
if config.notifications then
vim.notify("⚠️ " .. error_msg, vim.log.levels.WARN, {
id = notif_id,
title = "AI Commit",
timeout = 3000,
})
end
cfg_notify("⚠️ No staged changes to commit", vim.log.levels.WARN, {
id = notif_id,
title = "AI Commit",
timeout = 3000,
})
if callback then
callback(false, error_msg)
callback(false, "No staged changes to commit")
end
end)
return
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Calling AI API", vim.log.levels.DEBUG)
cfg_notify("ai-commit-msg.nvim: Calling AI API", vim.log.levels.DEBUG)
end)

local start_time = vim.uv.hrtime()
Expand All @@ -124,15 +108,11 @@ function M.generate(config, callback)

vim.schedule(function()
if not success then
vim.notify("ai-commit-msg.nvim: " .. result, vim.log.levels.ERROR)
-- Clear spinner notification with error message
if config.notifications then
vim.notify("❌ " .. result, vim.log.levels.ERROR, {
id = notif_id,
title = "AI Commit",
timeout = 3000,
})
end
cfg_notify("❌ " .. result, vim.log.levels.ERROR, {
id = notif_id,
title = "AI Commit",
timeout = 3000,
})
if callback then
callback(false, result)
end
Expand All @@ -152,10 +132,10 @@ function M.generate(config, callback)
end
end

vim.notify("ai-commit-msg.nvim: Generated message: " .. result:sub(1, 50) .. "...", vim.log.levels.DEBUG)
cfg_notify("ai-commit-msg.nvim: Generated message: " .. result:sub(1, 50) .. "...", vim.log.levels.DEBUG)
-- Clear spinner notification with success message
if config.notifications then
vim.notify("✅ Commit message generated (" .. duration_cost_str .. ")", vim.log.levels.INFO, {
if config.spinner or config.notifications then
cfg_notify("✅ Commit message generated (" .. duration_cost_str .. ")", vim.log.levels.INFO, {
id = notif_id,
title = "AI Commit",
timeout = 2000,
Expand Down
6 changes: 3 additions & 3 deletions lua/ai_commit_msg/harness.lua
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function M.run_matrix(opts)
local diff_files = list_diff_files(diffs_dir)

if #diff_files == 0 then
vim.notify("No .diff files found in " .. diffs_dir, vim.log.levels.WARN)
config_mod.notify("No .diff files found in " .. diffs_dir, vim.log.levels.WARN)
return
end

Expand All @@ -122,13 +122,13 @@ function M.run_matrix(opts)
for _, diff_path in ipairs(diff_files) do
local diff, err = read_file(diff_path)
if not diff then
vim.notify(err, vim.log.levels.ERROR)
config_mod.notify(err, vim.log.levels.ERROR)
else
local tiny = is_tiny_diff(diff, opts.tiny)
for _, pname in ipairs(provider_list) do
local pcfg = defaults.providers[pname]
if not pcfg then
vim.notify("Unknown provider in matrix: " .. tostring(pname), vim.log.levels.WARN)
config_mod.notify("Unknown provider in matrix: " .. tostring(pname), vim.log.levels.WARN)
else
local models = get_models_for_provider(pcfg)
for _, model in ipairs(models) do
Expand Down
4 changes: 2 additions & 2 deletions lua/ai_commit_msg/providers/anthropic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function M.call_api(config, diff, callback)
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
require("ai_commit_msg.config").notify("ai-commit-msg.nvim: Prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
end)

local payload_data = {
Expand Down Expand Up @@ -76,7 +76,7 @@ function M.call_api(config, diff, callback)
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Full API response: " .. vim.inspect(response), vim.log.levels.DEBUG)
require("ai_commit_msg.config").notify("ai-commit-msg.nvim: Full API response: " .. vim.inspect(response), vim.log.levels.DEBUG)
end)

if response.content and response.content[1] and response.content[1].text then
Expand Down
2 changes: 1 addition & 1 deletion lua/ai_commit_msg/providers/copilot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function M.call_api(config, diff, callback)
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Copilot prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
require("ai_commit_msg.config").notify("ai-commit-msg.nvim: Copilot prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
end)

local payload_data = {
Expand Down
4 changes: 2 additions & 2 deletions lua/ai_commit_msg/providers/gemini.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function M.call_api(config, diff, callback)
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
require("ai_commit_msg.config").notify("ai-commit-msg.nvim: Prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
end)

-- Prefer using systemInstruction + user content for clarity
Expand Down Expand Up @@ -90,7 +90,7 @@ function M.call_api(config, diff, callback)
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Full API response: " .. vim.inspect(response), vim.log.levels.DEBUG)
require("ai_commit_msg.config").notify("ai-commit-msg.nvim: Full API response: " .. vim.inspect(response), vim.log.levels.DEBUG)
end)

if response.candidates and response.candidates[1] then
Expand Down
4 changes: 2 additions & 2 deletions lua/ai_commit_msg/providers/openai.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function M.call_api(config, diff, callback)
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
require("ai_commit_msg.config").notify("ai-commit-msg.nvim: Prompt length: " .. #prompt .. " chars", vim.log.levels.DEBUG)
end)

local payload_data = {
Expand Down Expand Up @@ -99,7 +99,7 @@ function M.call_api(config, diff, callback)
end

vim.schedule(function()
vim.notify("ai-commit-msg.nvim: Full API response: " .. vim.inspect(response), vim.log.levels.DEBUG)
require("ai_commit_msg.config").notify("ai-commit-msg.nvim: Full API response: " .. vim.inspect(response), vim.log.levels.DEBUG)
end)

if response.choices and response.choices[1] and response.choices[1].message then
Expand Down
Loading