From d2d9a102a0d7f71af21c9bf191a859f7855abd33 Mon Sep 17 00:00:00 2001 From: Sergey Kuleshov Date: Tue, 24 Feb 2026 22:51:30 +0200 Subject: [PATCH] feat: add debug mode and improve notification logic - add `:AiCommitMsgDebug` command to toggle verbose logging - centralize notifications via a helper that respects user settings - gate debug-level messages behind `vim.g.ai_commit_msg_debug --- README.md | 1 + lua/ai_commit_msg.lua | 7 ++- lua/ai_commit_msg/autocmds.lua | 17 +++--- lua/ai_commit_msg/config.lua | 25 +++++++++ lua/ai_commit_msg/generator.lua | 68 ++++++++--------------- lua/ai_commit_msg/harness.lua | 6 +- lua/ai_commit_msg/providers/anthropic.lua | 4 +- lua/ai_commit_msg/providers/copilot.lua | 2 +- lua/ai_commit_msg/providers/gemini.lua | 4 +- lua/ai_commit_msg/providers/openai.lua | 4 +- plugin/ai-commit-msg.lua | 26 +++++---- 11 files changed, 88 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 928c3d8..d6dfe4e 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/lua/ai_commit_msg.lua b/lua/ai_commit_msg.lua index 893623f..d8f0658 100644 --- a/lua/ai_commit_msg.lua +++ b/lua/ai_commit_msg.lua @@ -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 diff --git a/lua/ai_commit_msg/autocmds.lua b/lua/ai_commit_msg/autocmds.lua index 70f68d2..b0ebf07 100644 --- a/lua/ai_commit_msg/autocmds.lua +++ b/lua/ai_commit_msg/autocmds.lua @@ -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 @@ -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 ) @@ -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, @@ -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) @@ -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) diff --git a/lua/ai_commit_msg/config.lua b/lua/ai_commit_msg/config.lua index 7cb59f7..5123013 100644 --- a/lua/ai_commit_msg/config.lua +++ b/lua/ai_commit_msg/config.lua @@ -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 diff --git a/lua/ai_commit_msg/generator.lua b/lua/ai_commit_msg/generator.lua index d26e398..8f36a9d 100644 --- a/lua/ai_commit_msg/generator.lua +++ b/lua/ai_commit_msg/generator.lua @@ -1,18 +1,11 @@ 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) @@ -20,14 +13,14 @@ 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 @@ -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 @@ -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 @@ -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() @@ -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 @@ -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, diff --git a/lua/ai_commit_msg/harness.lua b/lua/ai_commit_msg/harness.lua index 469b361..eca76e1 100644 --- a/lua/ai_commit_msg/harness.lua +++ b/lua/ai_commit_msg/harness.lua @@ -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 @@ -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 diff --git a/lua/ai_commit_msg/providers/anthropic.lua b/lua/ai_commit_msg/providers/anthropic.lua index 3e40df9..15f940b 100644 --- a/lua/ai_commit_msg/providers/anthropic.lua +++ b/lua/ai_commit_msg/providers/anthropic.lua @@ -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 = { @@ -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 diff --git a/lua/ai_commit_msg/providers/copilot.lua b/lua/ai_commit_msg/providers/copilot.lua index 5f61dcc..c862dc6 100644 --- a/lua/ai_commit_msg/providers/copilot.lua +++ b/lua/ai_commit_msg/providers/copilot.lua @@ -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 = { diff --git a/lua/ai_commit_msg/providers/gemini.lua b/lua/ai_commit_msg/providers/gemini.lua index bd51603..fcf5623 100644 --- a/lua/ai_commit_msg/providers/gemini.lua +++ b/lua/ai_commit_msg/providers/gemini.lua @@ -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 @@ -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 diff --git a/lua/ai_commit_msg/providers/openai.lua b/lua/ai_commit_msg/providers/openai.lua index 59e37d1..ef88eba 100644 --- a/lua/ai_commit_msg/providers/openai.lua +++ b/lua/ai_commit_msg/providers/openai.lua @@ -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 = { @@ -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 diff --git a/plugin/ai-commit-msg.lua b/plugin/ai-commit-msg.lua index 3605d04..5ff956c 100644 --- a/plugin/ai-commit-msg.lua +++ b/plugin/ai-commit-msg.lua @@ -8,7 +8,9 @@ if vim.g.loaded_ai_commit_msg == 1 then end vim.g.loaded_ai_commit_msg = 1 -vim.notify("ai-commit-msg.nvim: Plugin loaded", vim.log.levels.DEBUG) +local cfg_notify = require("ai_commit_msg.config").notify + +cfg_notify("ai-commit-msg.nvim: Plugin loaded", vim.log.levels.DEBUG) -- Create user commands vim.api.nvim_create_user_command("AiCommitMsg", function() @@ -21,24 +23,26 @@ end, { desc = "Generate AI commit message" }) vim.api.nvim_create_user_command("AiCommitMsgDisable", function() require("ai_commit_msg").disable() - vim.notify("AI Commit Message disabled") + cfg_notify("AI Commit Message disabled", vim.log.levels.INFO) end, { desc = "Disable AI commit message generation" }) vim.api.nvim_create_user_command("AiCommitMsgEnable", function() require("ai_commit_msg").enable() - vim.notify("AI Commit Message enabled") + cfg_notify("AI Commit Message enabled", vim.log.levels.INFO) end, { desc = "Enable AI commit message generation" }) vim.api.nvim_create_user_command("AiCommitMsgDebug", function() + vim.g.ai_commit_msg_debug = not vim.g.ai_commit_msg_debug + vim.notify("ai-commit-msg.nvim debug: " .. (vim.g.ai_commit_msg_debug and "ON" or "OFF"), vim.log.levels.INFO) local plugin = require("ai_commit_msg") - vim.notify("Plugin config: " .. vim.inspect(plugin.config), vim.log.levels.INFO) + cfg_notify("Plugin config: " .. vim.inspect(plugin.config), vim.log.levels.INFO) -- Check if autocmds are registered local autocmds = vim.api.nvim_get_autocmds({ group = "ai_commit_msg" }) if #autocmds > 0 then - vim.notify("Autocmds registered: " .. vim.inspect(autocmds), vim.log.levels.INFO) + cfg_notify("Autocmds registered: " .. vim.inspect(autocmds), vim.log.levels.INFO) else - vim.notify("No autocmds registered!", vim.log.levels.WARN) + cfg_notify("No autocmds registered!", vim.log.levels.WARN) end end, { desc = "Debug AI commit message plugin" }) @@ -58,9 +62,9 @@ vim.api.nvim_create_user_command("AiCommitMsgTestMatrix", function(opts) }) if dry then - vim.notify("Dry-run: collected " .. tostring(#results) .. " prompt stats", vim.log.levels.INFO) + cfg_notify("Dry-run: collected " .. tostring(#results) .. " prompt stats", vim.log.levels.INFO) else - vim.notify("Matrix run complete: " .. tostring(#results) .. " cases", vim.log.levels.INFO) + cfg_notify("Matrix run complete: " .. tostring(#results) .. " cases", vim.log.levels.INFO) end end, { desc = "Run prompt/model matrix over .diff fixtures", @@ -86,7 +90,7 @@ vim.api.nvim_create_user_command("AiCommitMsgAllModels", function() vim.system(git_cmd, {}, function(diff_res) if diff_res.code ~= 0 then vim.schedule(function() - vim.notify("Failed to get git diff: " .. (diff_res.stderr or "Unknown error"), vim.log.levels.ERROR) + cfg_notify("Failed to get git diff: " .. (diff_res.stderr or "Unknown error"), vim.log.levels.ERROR) end) return end @@ -94,7 +98,7 @@ vim.api.nvim_create_user_command("AiCommitMsgAllModels", function() local diff = diff_res.stdout or "" if diff == "" then vim.schedule(function() - vim.notify("No staged changes to commit", vim.log.levels.WARN) + cfg_notify("No staged changes to commit", vim.log.levels.WARN) end) return end @@ -147,7 +151,7 @@ vim.api.nvim_create_user_command("AiCommitMsgAllModels", function() vim.api.nvim_buf_set_option(buf, "buftype", "nofile") vim.api.nvim_buf_set_option(buf, "filetype", "gitcommit") vim.api.nvim_set_current_buf(buf) - vim.notify("All-models results buffer opened", vim.log.levels.INFO) + cfg_notify("All-models results buffer opened", vim.log.levels.INFO) end) end) end, { desc = "Generate commit messages across all models for staged diff" })