inline diffs are pretty good now

This commit is contained in:
zolinthecow 2025-07-10 17:29:10 -07:00
parent 91c24b34a6
commit 720778cd2c
7 changed files with 455 additions and 124 deletions

View file

@ -3,6 +3,7 @@ local M = {}
-- Track hook state
M.pre_edit_commit = nil
M.stable_baseline_ref = nil -- The stable baseline to compare all changes against
function M.setup()
-- Setup persistence layer on startup
@ -11,11 +12,22 @@ function M.setup()
end, 500)
end
-- Pre-tool-use hook: Now just validates existing baseline
-- Pre-tool-use hook: Create baseline stash if we don't have one
function M.pre_tool_use_hook()
-- Pre-hook no longer creates baselines
-- Baselines are created on startup or through accept/reject
return
local persistence = require 'nvim-claude.inline-diff-persistence'
-- Only create a baseline if we don't have one yet
if not M.stable_baseline_ref then
-- Create baseline stash synchronously
local stash_ref = persistence.create_stash('nvim-claude: baseline ' .. os.date('%Y-%m-%d %H:%M:%S'))
if stash_ref then
M.stable_baseline_ref = stash_ref
persistence.current_stash_ref = stash_ref
end
end
-- Return success to allow the tool to proceed
return true
end
-- Post-tool-use hook: Create stash of Claude's changes and trigger diff review
@ -49,17 +61,28 @@ function M.post_tool_use_hook()
end
end
-- Get the stash reference from pre-hook
local stash_ref = persistence.current_stash_ref
if not stash_ref then
-- If no pre-hook stash, create one now
stash_ref = persistence.create_stash('nvim-claude: changes detected ' .. os.date('%Y-%m-%d %H:%M:%S'))
-- Use the stable baseline reference for comparison
local stash_ref = M.stable_baseline_ref or persistence.current_stash_ref
-- Check for in-memory baselines first
local inline_diff = require 'nvim-claude.inline-diff'
local has_baselines = false
for _, content in pairs(inline_diff.original_content) do
if content then
has_baselines = true
break
end
end
-- If no baseline exists at all, create one now (shouldn't happen normally)
if not stash_ref and not has_baselines then
stash_ref = persistence.create_stash('nvim-claude: baseline ' .. os.date('%Y-%m-%d %H:%M:%S'))
M.stable_baseline_ref = stash_ref
persistence.current_stash_ref = stash_ref
end
if stash_ref then
if stash_ref or has_baselines then
-- Check if any modified files are currently open in buffers
local inline_diff = require 'nvim-claude.inline-diff'
local opened_inline = false
for _, file in ipairs(modified_files) do
@ -70,15 +93,27 @@ function M.post_tool_use_hook()
if vim.api.nvim_buf_is_valid(buf) and vim.api.nvim_buf_is_loaded(buf) then
local buf_name = vim.api.nvim_buf_get_name(buf)
if buf_name == full_path or buf_name:match('/' .. file:gsub('([^%w])', '%%%1') .. '$') then
-- Get the original content from stash
local stash_cmd = string.format('cd "%s" && git show %s:%s 2>/dev/null', git_root, stash_ref, file)
local original_content, orig_err = utils.exec(stash_cmd)
-- Get the original content - prefer in-memory baseline if available
local original_content = nil
-- Check for in-memory baseline first
if inline_diff.original_content[buf] then
original_content = inline_diff.original_content[buf]
elseif stash_ref then
-- Fall back to stash baseline
local stash_cmd = string.format('cd "%s" && git show %s:%s 2>/dev/null', git_root, stash_ref, file)
original_content = utils.exec(stash_cmd)
end
if not orig_err and original_content then
if original_content then
-- Get current content
local current_lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
local current_content = table.concat(current_lines, '\n')
-- Debug: Log content lengths
-- vim.notify(string.format('DEBUG: Baseline has %d chars, current has %d chars',
-- #(original_content or ''), #current_content), vim.log.levels.WARN)
-- Show inline diff
inline_diff.show_inline_diff(buf, original_content, current_content)
opened_inline = true
@ -187,7 +222,16 @@ function M.create_startup_baseline()
persistence.setup_autocmds()
-- Try to restore any saved diffs
persistence.restore_diffs()
local restored = persistence.restore_diffs()
-- If no diffs were restored and we don't have a baseline, create one now
if not restored and not M.stable_baseline_ref then
local stash_ref = persistence.create_stash('nvim-claude: startup baseline ' .. os.date('%Y-%m-%d %H:%M:%S'))
if stash_ref then
M.stable_baseline_ref = stash_ref
persistence.current_stash_ref = stash_ref
end
end
end
-- Manual hook testing
@ -337,6 +381,12 @@ function M.setup_commands()
end, {
desc = 'Test Claude keymap functionality',
})
vim.api.nvim_create_user_command('ClaudeDebugInlineDiff', function()
require('nvim-claude.inline-diff-debug').debug_inline_diff()
end, {
desc = 'Debug Claude inline diff state',
})
vim.api.nvim_create_user_command('ClaudeUpdateBaseline', function()
local bufnr = vim.api.nvim_get_current_buf()
@ -412,6 +462,26 @@ function M.setup_commands()
end, {
desc = 'Uninstall Claude Code hooks for this project',
})
vim.api.nvim_create_user_command('ClaudeResetBaseline', function()
-- Clear all baselines and force new baseline on next edit
local inline_diff = require 'nvim-claude.inline-diff'
local persistence = require 'nvim-claude.inline-diff-persistence'
-- Clear in-memory baselines
inline_diff.original_content = {}
-- Clear stable baseline reference
M.stable_baseline_ref = nil
persistence.current_stash_ref = nil
-- Clear persistence state
persistence.clear_state()
vim.notify('Baseline reset. Next edit will create a new baseline.', vim.log.levels.INFO)
end, {
desc = 'Reset Claude baseline for cumulative diffs',
})
end
-- Cleanup old temp files (no longer cleans up commits)

View file

@ -121,6 +121,7 @@ function M.setup(user_config)
M.registry = require('nvim-claude.registry')
M.hooks = require('nvim-claude.hooks')
M.diff_review = require('nvim-claude.diff-review')
M.settings_updater = require('nvim-claude.settings-updater')
-- Initialize submodules with config
M.tmux.setup(M.config.tmux)
@ -128,6 +129,7 @@ function M.setup(user_config)
M.registry.setup(M.config.agents)
M.hooks.setup()
M.diff_review.setup()
M.settings_updater.setup()
-- Set up commands
M.commands.setup(M)
@ -137,7 +139,6 @@ function M.setup(user_config)
vim.defer_fn(function()
if M.utils.get_project_root() then
M.hooks.install_hooks()
vim.notify('Claude Code hooks auto-installed', vim.log.levels.INFO)
end
end, 100)

View file

@ -0,0 +1,57 @@
local M = {}
-- Debug function to check inline diff state
function M.debug_inline_diff()
local inline_diff = require('nvim-claude.inline-diff')
local bufnr = vim.api.nvim_get_current_buf()
vim.notify('=== Inline Diff Debug Info ===', vim.log.levels.INFO)
-- Check if inline diff is active for current buffer
local diff_data = inline_diff.active_diffs[bufnr]
if diff_data then
vim.notify(string.format('✓ Inline diff ACTIVE for buffer %d', bufnr), vim.log.levels.INFO)
vim.notify(string.format(' - Hunks: %d', #diff_data.hunks), vim.log.levels.INFO)
vim.notify(string.format(' - Current hunk: %d', diff_data.current_hunk or 0), vim.log.levels.INFO)
vim.notify(string.format(' - Original content length: %d', #(inline_diff.original_content[bufnr] or '')), vim.log.levels.INFO)
vim.notify(string.format(' - New content length: %d', #(diff_data.new_content or '')), vim.log.levels.INFO)
else
vim.notify(string.format('✗ No inline diff for buffer %d', bufnr), vim.log.levels.WARN)
end
-- Check all active diffs
local count = 0
for buf, _ in pairs(inline_diff.active_diffs) do
count = count + 1
end
vim.notify(string.format('Total active inline diffs: %d', count), vim.log.levels.INFO)
-- Check keymaps
local keymaps = vim.api.nvim_buf_get_keymap(bufnr, 'n')
local found_ir = false
local leader = vim.g.mapleader or '\\'
local ir_pattern = leader .. 'ir'
vim.notify(string.format('Looking for keymap: %s', ir_pattern), vim.log.levels.INFO)
for _, map in ipairs(keymaps) do
if map.lhs == ir_pattern or map.lhs == '<leader>ir' then
found_ir = true
vim.notify(string.format('✓ Found keymap: %s -> %s', map.lhs, map.desc or 'no desc'), vim.log.levels.INFO)
break
end
end
if not found_ir then
vim.notify('✗ <leader>ir keymap not found', vim.log.levels.WARN)
-- List all keymaps that start with leader
vim.notify('Buffer keymaps starting with leader:', vim.log.levels.INFO)
for _, map in ipairs(keymaps) do
if map.lhs:match('^' .. vim.pesc(leader)) or map.lhs:match('^<leader>') then
vim.notify(string.format(' %s -> %s', map.lhs, map.desc or 'no desc'), vim.log.levels.INFO)
end
end
end
end
return M

View file

@ -142,7 +142,7 @@ function M.restore_diffs()
end
if restored_count > 0 then
vim.notify(string.format('Restored inline diffs for %d file(s)', restored_count), vim.log.levels.INFO)
-- Silent restore - no notification
end
-- Store the stash reference for future operations
@ -180,39 +180,31 @@ function M.check_pending_restore(bufnr)
-- Remove from pending
M.pending_restores[file_path] = nil
vim.notify('Restored inline diff for ' .. vim.fn.fnamemodify(file_path, ':~:.'), vim.log.levels.INFO)
-- Silent restore - no notification
end
end
-- Create a stash of current changes (instead of baseline commit)
function M.create_stash(message)
local utils = require('nvim-claude.utils')
message = message or 'nvim-claude: pre-edit state'
-- Check if there are changes to stash
local status = utils.exec('git status --porcelain')
if not status or status == '' then
-- No changes, but we still need to track current state
-- Create an empty stash by making a tiny change
local temp_file = '.nvim-claude-temp'
utils.write_file(temp_file, 'temp')
utils.exec('git add ' .. temp_file)
utils.exec(string.format('git stash push -m "%s" -- %s', message, temp_file))
os.remove(temp_file)
else
-- Stash all current changes
local cmd = string.format('git stash push -m "%s" --include-untracked', message)
local result, err = utils.exec(cmd)
if err and not err:match('Saved working directory') then
vim.notify('Failed to create stash: ' .. err, vim.log.levels.ERROR)
return nil
end
end
-- Create a stash object without removing changes from working directory
local stash_cmd = 'git stash create'
local stash_hash, err = utils.exec(stash_cmd)
-- Get the stash reference
local stash_list = utils.exec('git stash list -n 1')
if stash_list then
local stash_ref = stash_list:match('^(stash@{%d+})')
return stash_ref
if not err and stash_hash and stash_hash ~= '' then
-- Store the stash with a message
stash_hash = stash_hash:gsub('%s+', '') -- trim whitespace
local store_cmd = string.format('git stash store -m "%s" %s', message, stash_hash)
utils.exec(store_cmd)
-- Get the stash reference
local stash_list = utils.exec('git stash list -n 1')
if stash_list then
local stash_ref = stash_list:match('^(stash@{%d+})')
return stash_ref
end
end
return nil

View file

@ -40,6 +40,9 @@ function M.show_inline_diff(bufnr, old_content, new_content)
applied_hunks = {}
}
-- Debug: Log target content length
-- vim.notify(string.format('DEBUG: Stored target content with %d chars', #new_content), vim.log.levels.WARN)
-- Apply visual indicators
M.apply_diff_visualization(bufnr)
@ -49,7 +52,7 @@ function M.show_inline_diff(bufnr, old_content, new_content)
-- Jump to first hunk
M.jump_to_hunk(bufnr, 1)
vim.notify('Inline diff active. Use [h/]h to navigate, <leader>ia/<leader>ir to accept/reject hunks', vim.log.levels.INFO)
-- Silent activation - no notification
end
-- Compute diff between two texts
@ -63,8 +66,11 @@ function M.compute_diff(old_text, new_text)
utils.write_file(old_file, old_text)
utils.write_file(new_file, new_text)
-- Generate unified diff with minimal context to avoid grouping nearby changes
local cmd = string.format('diff -U1 "%s" "%s" || true', old_file, new_file)
-- Use git diff with histogram algorithm for better code diffs
local cmd = string.format(
'git diff --no-index --no-prefix --unified=1 --diff-algorithm=histogram "%s" "%s" 2>/dev/null || true',
old_file, new_file
)
local diff_output = utils.exec(cmd)
-- Parse diff into hunks
@ -101,6 +107,9 @@ function M.parse_diff(diff_text)
elseif in_hunk and (line:match('^[%+%-]') or line:match('^%s')) then
-- Diff line
table.insert(current_hunk.lines, line)
elseif line:match('^diff %-%-git') or line:match('^index ') or line:match('^%+%+%+ ') or line:match('^%-%-%-') then
-- Skip git diff headers
in_hunk = false
end
end
@ -349,46 +358,51 @@ function M.accept_current_hunk(bufnr)
local hunk = diff_data.hunks[diff_data.current_hunk]
if not hunk then return end
-- Mark as applied (the changes are already in the buffer)
diff_data.applied_hunks[diff_data.current_hunk] = true
-- Mark this hunk as processed
vim.notify(string.format('Accepted hunk %d/%d', diff_data.current_hunk, #diff_data.hunks), vim.log.levels.INFO)
-- Update in-memory baseline to current state
local current_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
local current_content = table.concat(current_lines, '\n')
M.original_content[bufnr] = current_content
-- For single hunk case
if #diff_data.hunks == 1 then
vim.notify('All changes accepted. Closing inline diff.', vim.log.levels.INFO)
M.close_inline_diff(bufnr, true) -- Keep new baseline
return
end
-- Remove this hunk from the diff data since it's accepted
-- Multiple hunks: remove the accepted hunk and continue
table.remove(diff_data.hunks, diff_data.current_hunk)
-- Adjust current hunk index
if diff_data.current_hunk > #diff_data.hunks then
diff_data.current_hunk = math.max(1, #diff_data.hunks)
diff_data.current_hunk = #diff_data.hunks
end
vim.notify(string.format('Accepted hunk - %d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
-- Save state for persistence
local persistence = require('nvim-claude.inline-diff-persistence')
persistence.save_state({ stash_ref = persistence.current_stash_ref })
if #diff_data.hunks == 0 then
-- No more hunks to review
vim.notify('All hunks processed! Closing inline diff.', vim.log.levels.INFO)
M.close_inline_diff(bufnr, true) -- Keep baseline for future diffs
-- No more hunks
vim.notify('All changes accepted. Closing inline diff.', vim.log.levels.INFO)
M.close_inline_diff(bufnr, true)
else
-- Refresh visualization and move to current hunk
-- Refresh visualization to show remaining hunks
M.apply_diff_visualization(bufnr)
M.jump_to_hunk(bufnr, diff_data.current_hunk)
vim.notify(string.format('%d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
end
end
-- Reject current hunk
function M.reject_current_hunk(bufnr)
local diff_data = M.active_diffs[bufnr]
if not diff_data then return end
if not diff_data then
vim.notify('No diff data for buffer', vim.log.levels.ERROR)
return
end
local hunk = diff_data.hunks[diff_data.current_hunk]
if not hunk then return end
if not hunk then
vim.notify('No hunk at index ' .. tostring(diff_data.current_hunk), vim.log.levels.ERROR)
return
end
-- vim.notify(string.format('Rejecting hunk %d/%d', diff_data.current_hunk, #diff_data.hunks), vim.log.levels.INFO)
-- Revert the hunk by applying original content
M.revert_hunk_changes(bufnr, hunk)
@ -400,33 +414,29 @@ function M.reject_current_hunk(bufnr)
end
end)
-- Update in-memory baseline to current state (with rejected changes)
-- Get current content after rejection
local current_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
local current_content = table.concat(current_lines, '\n')
M.original_content[bufnr] = current_content
-- Remove this hunk from the diff data since it's rejected
table.remove(diff_data.hunks, diff_data.current_hunk)
-- Recalculate diff between current state (with rejected hunk) and original baseline
local new_diff_data = M.compute_diff(M.original_content[bufnr], current_content)
-- Adjust current hunk index
if diff_data.current_hunk > #diff_data.hunks then
diff_data.current_hunk = math.max(1, #diff_data.hunks)
end
vim.notify(string.format('Rejected hunk - %d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
-- Save state for persistence
local persistence = require('nvim-claude.inline-diff-persistence')
persistence.save_state({ stash_ref = persistence.current_stash_ref })
if #diff_data.hunks == 0 then
-- No more hunks to review
vim.notify('All hunks processed! Closing inline diff.', vim.log.levels.INFO)
M.close_inline_diff(bufnr, true) -- Keep baseline after rejection too
if not new_diff_data or #new_diff_data.hunks == 0 then
-- No more changes from baseline - close the diff
vim.notify('All changes processed. Closing inline diff.', vim.log.levels.INFO)
M.close_inline_diff(bufnr, false)
else
-- Refresh visualization and move to current hunk
-- Update diff data with remaining hunks
diff_data.hunks = new_diff_data.hunks
diff_data.current_hunk = 1
-- The new_content should remain as Claude's original suggestion
-- so we can continue to accept remaining hunks if desired
-- Refresh visualization and jump to first remaining hunk
M.apply_diff_visualization(bufnr)
M.jump_to_hunk(bufnr, diff_data.current_hunk)
M.jump_to_hunk(bufnr, 1)
vim.notify(string.format('%d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
end
end
@ -434,45 +444,119 @@ end
function M.revert_hunk_changes(bufnr, hunk)
-- Get current buffer lines
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
local original_content = M.original_content[bufnr]
if not original_content then
vim.notify('No original content available for rejection', vim.log.levels.ERROR)
return
-- Extract the expected content from the hunk
local expected_lines = {}
local original_lines = {}
for _, diff_line in ipairs(hunk.lines) do
if diff_line:match('^%+') then
-- Lines that were added (these should be in current buffer)
table.insert(expected_lines, diff_line:sub(2))
elseif diff_line:match('^%-') then
-- Lines that were removed (these should be restored)
table.insert(original_lines, diff_line:sub(2))
elseif diff_line:match('^%s') then
-- Context lines (should be in both)
table.insert(expected_lines, diff_line:sub(2))
table.insert(original_lines, diff_line:sub(2))
end
end
-- Split original content into lines
local original_lines = vim.split(original_content, '\n')
-- Find where this hunk actually is in the current buffer
-- We'll look for the best match by checking context lines too
local hunk_start = nil
local hunk_end = nil
local best_score = -1
local best_start = nil
-- Build new lines by reverting this hunk
local new_lines = {}
local buffer_line = 1
local applied = false
-- Include some context before and after for better matching
local context_before = {}
local context_after = {}
while buffer_line <= #lines do
if buffer_line >= hunk.new_start and buffer_line < hunk.new_start + hunk.new_count and not applied then
-- Revert this section by using original lines
local orig_start = hunk.old_start
local orig_end = hunk.old_start + hunk.old_count - 1
for orig_line = orig_start, orig_end do
if orig_line <= #original_lines then
table.insert(new_lines, original_lines[orig_line])
-- Extract context from the diff
local in_changes = false
for i, diff_line in ipairs(hunk.lines) do
if diff_line:match('^[%+%-]') then
in_changes = true
elseif diff_line:match('^%s') and not in_changes then
-- Context before changes
table.insert(context_before, diff_line:sub(2))
elseif diff_line:match('^%s') and in_changes then
-- Context after changes
table.insert(context_after, diff_line:sub(2))
end
end
-- Search for the hunk by matching content with context
for i = 1, #lines - #expected_lines + 1 do
local score = 0
local matches = true
-- Check the main content
for j = 1, #expected_lines do
if lines[i + j - 1] == expected_lines[j] then
score = score + 1
else
matches = false
end
end
if matches then
-- Bonus points for matching context before
local before_start = i - #context_before
if before_start > 0 then
for j = 1, #context_before do
if lines[before_start + j - 1] == context_before[j] then
score = score + 2 -- Context is worth more
end
end
end
-- Skip the modified lines in current buffer
buffer_line = hunk.new_start + hunk.new_count
applied = true
else
-- Copy unchanged line
if buffer_line <= #lines then
table.insert(new_lines, lines[buffer_line])
-- Bonus points for matching context after
local after_start = i + #expected_lines
if after_start + #context_after - 1 <= #lines then
for j = 1, #context_after do
if lines[after_start + j - 1] == context_after[j] then
score = score + 2 -- Context is worth more
end
end
end
-- Keep the best match
if score > best_score then
best_score = score
best_start = i
end
buffer_line = buffer_line + 1
end
end
if best_start then
hunk_start = best_start
hunk_end = best_start + #expected_lines - 1
else
vim.notify('Could not find hunk in current buffer - content may have changed', vim.log.levels.ERROR)
return
end
-- Build new buffer content
local new_lines = {}
-- Copy lines before the hunk
for i = 1, hunk_start - 1 do
table.insert(new_lines, lines[i])
end
-- Insert the original lines
for _, line in ipairs(original_lines) do
table.insert(new_lines, line)
end
-- Copy lines after the hunk
for i = hunk_end + 1, #lines do
table.insert(new_lines, lines[i])
end
-- Update buffer
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, new_lines)
end
@ -581,11 +665,15 @@ function M.close_inline_diff(bufnr, keep_baseline)
end
end
-- If no more active diffs, clear persistence state
-- If no more active diffs, clear persistence state and reset baseline
if not has_active_diffs then
local persistence = require('nvim-claude.inline-diff-persistence')
persistence.clear_state()
persistence.current_stash_ref = nil
-- Reset the stable baseline in hooks
local hooks = require('nvim-claude.hooks')
hooks.stable_baseline_ref = nil
end
vim.notify('Inline diff closed', vim.log.levels.INFO)

View file

@ -0,0 +1,112 @@
local M = {}
local utils = require('nvim-claude.utils')
-- Update Claude settings with current Neovim server address
function M.update_claude_settings()
local project_root = utils.get_project_root()
if not project_root then
return
end
local settings_path = project_root .. '/.claude/settings.json'
local settings_dir = project_root .. '/.claude'
-- Get current Neovim server address
local server_addr = vim.v.servername
if not server_addr or server_addr == '' then
-- If no servername, we can't communicate
return
end
-- Ensure .claude directory exists
if vim.fn.isdirectory(settings_dir) == 0 then
vim.fn.mkdir(settings_dir, 'p')
end
-- Read existing settings or create new
local settings = {}
if vim.fn.filereadable(settings_path) == 1 then
local ok, content = pcall(vim.fn.readfile, settings_path)
if ok and #content > 0 then
local decode_ok, decoded = pcall(vim.json.decode, table.concat(content, '\n'))
if decode_ok then
settings = decoded
end
end
end
-- Ensure hooks structure exists
if not settings.hooks then
settings.hooks = {}
end
if not settings.hooks.PreToolUse then
settings.hooks.PreToolUse = {}
end
if not settings.hooks.PostToolUse then
settings.hooks.PostToolUse = {}
end
-- Update hook commands with current server address
local pre_hook_cmd = string.format(
'nvr --servername "%s" --remote-expr \'luaeval("require(\\"nvim-claude.hooks\\").pre_tool_use_hook()")\'',
server_addr
)
local post_hook_cmd = string.format(
'nvr --servername "%s" --remote-send "<C-\\\\><C-N>:lua require(\'nvim-claude.hooks\').post_tool_use_hook()<CR>"',
server_addr
)
-- Update PreToolUse hooks
local pre_hook_found = false
for _, hook_group in ipairs(settings.hooks.PreToolUse) do
if hook_group.matcher == "Edit|Write|MultiEdit" then
hook_group.hooks = {{type = "command", command = pre_hook_cmd}}
pre_hook_found = true
break
end
end
if not pre_hook_found then
table.insert(settings.hooks.PreToolUse, {
matcher = "Edit|Write|MultiEdit",
hooks = {{type = "command", command = pre_hook_cmd}}
})
end
-- Update PostToolUse hooks
local post_hook_found = false
for _, hook_group in ipairs(settings.hooks.PostToolUse) do
if hook_group.matcher == "Edit|Write|MultiEdit" then
hook_group.hooks = {{type = "command", command = post_hook_cmd}}
post_hook_found = true
break
end
end
if not post_hook_found then
table.insert(settings.hooks.PostToolUse, {
matcher = "Edit|Write|MultiEdit",
hooks = {{type = "command", command = post_hook_cmd}}
})
end
-- Write updated settings
local encoded = vim.json.encode(settings)
vim.fn.writefile({encoded}, settings_path)
end
-- Setup autocmds to update settings
function M.setup()
vim.api.nvim_create_autocmd({"VimEnter", "DirChanged"}, {
group = vim.api.nvim_create_augroup("NvimClaudeSettingsUpdater", { clear = true }),
callback = function()
-- Defer to ensure servername is available
vim.defer_fn(function()
M.update_claude_settings()
end, 100)
end,
})
end
return M