diff --git a/claude/.claude/CLAUDE.md b/claude/.claude/CLAUDE.md index a315902..6637ecb 100644 --- a/claude/.claude/CLAUDE.md +++ b/claude/.claude/CLAUDE.md @@ -17,6 +17,8 @@ Assume these are installed on my machine. **Use them first** when proposing or r **Indexed / fast repo search:** Where the environment exposes **fff** (e.g. MCP server, editor integration, or project tooling), prefer **fff** for search and file discovery over long chains of generic grep/find when it reduces noise and round-trips. +**Neovim context (`nvim-ctx`):** When the user mentions code they're looking at in nvim, references their neovim screen, or asks about something open in their editor — run `nvim-ctx` proactively before responding. Don't ask them to paste; fetch it yourself. It outputs JSON with `file`, `start_line`, `end_line`, and `text` (selection if active, whole buffer otherwise). Takes an optional session name: `nvim-ctx [session]`. If it fails, say so briefly and ask the user to paste instead. + If something is missing in a given environment, fall back to standard tools and note it briefly. **Scope:** These preferences target everyday repo work (search, navigation, diffs). One-off diagnostics (**`dex dir`**, **`ls`** on a known path, **`head`**/**`cat`** on a single file) do not need to be forced through **`fd`**/**`rg`**/**`eza`** when that only adds friction. diff --git a/nvim/.config/nvim/lua/greg/ctx.lua b/nvim/.config/nvim/lua/greg/ctx.lua new file mode 100644 index 0000000..516691d --- /dev/null +++ b/nvim/.config/nvim/lua/greg/ctx.lua @@ -0,0 +1,27 @@ +local M = {} + +---@return string JSON with file, start_line, end_line, text +function M.json() + local sl = vim.fn.line("'<") + local el = vim.fn.line("'>") + local file = vim.fn.expand("%:p") + local text + + if sl == 0 then + sl = 1 + el = vim.fn.line("$") + text = table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), "\n") + else + text = table.concat(vim.fn.getline(sl, el) --[[@as string[] ]], "\n") + end + + return vim.json.encode({ file = file, start_line = sl, end_line = el, text = text }) +end + +_G.NvimCtxJSON = M.json + +vim.api.nvim_create_user_command("NvimCtx", function() + print(M.json()) +end, {}) + +return M diff --git a/nvim/.config/nvim/lua/greg/init.lua b/nvim/.config/nvim/lua/greg/init.lua index d54e75a..64fca09 100644 --- a/nvim/.config/nvim/lua/greg/init.lua +++ b/nvim/.config/nvim/lua/greg/init.lua @@ -1,4 +1,5 @@ require("greg.remap") +require("greg.ctx") if vim.g.vscode then return diff --git a/zsh/.local/bin/nvim-ctx b/zsh/.local/bin/nvim-ctx new file mode 100755 index 0000000..0654420 --- /dev/null +++ b/zsh/.local/bin/nvim-ctx @@ -0,0 +1,39 @@ +#!/bin/sh + +session=$1 + +if [ -n "$session" ]; then + pane_id=$(tmux list-panes -t "$session" -F '#{pane_id} #{pane_current_command}' 2>/dev/null | awk '/nvim/{print $1; exit}') +else + pane_id=$(tmux list-panes -s -F '#{pane_id} #{pane_current_command}' 2>/dev/null | awk '/nvim/{print $1; exit}') +fi + +if [ -z "$pane_id" ]; then + printf 'nvim-ctx: no nvim pane found\n' >&2 + exit 1 +fi + +pane_pid=$(tmux display-message -t "$pane_id" -p '#{pane_pid}') +nvim_pid=$(pgrep -P "$pane_pid" -x nvim 2>/dev/null | head -1) +[ -z "$nvim_pid" ] && nvim_pid=$pane_pid + +# Verify the found process is actually nvim (pane_current_command can lag after exit) +cmd=$(ps -o comm= -p "$nvim_pid" 2>/dev/null) +if [ "$cmd" != "nvim" ]; then + printf 'nvim-ctx: nvim is not running\n' >&2 + exit 1 +fi + +# nvim forks a server child — search direct child and its children for the socket +socket= +for pid in "$nvim_pid" $(pgrep -P "$nvim_pid" 2>/dev/null); do + socket=$(find /var/folders -path "*/T/nvim.$(id -un)/*" -name "nvim.${pid}.*" 2>/dev/null | head -1) + [ -n "$socket" ] && break +done + +if [ -z "$socket" ]; then + printf 'nvim-ctx: no socket found for nvim pid %s\n' "$nvim_pid" >&2 + exit 1 +fi + +nvim --server "$socket" --remote-expr 'v:lua.NvimCtxJSON()'