-- gemini-integration.lua -- Registers the active Neovim instance for the Antigravity MCP Server local last_active_task = nil local last_active_gate = nil vim.api.nvim_create_autocmd({"VimEnter", "FocusGained", "BufEnter", "BufWritePost"}, { callback = function(ev) -- Only register if Neovim has an active UI attached (ignore headless/embedded instances) if #vim.api.nvim_list_uis() > 0 then local server_name = vim.v.servername if server_name then -- 2. V2 Telemetry Push (HTTP with Knowledge Projection + UDP Fast Mirror) local file = vim.api.nvim_buf_get_name(0) local cursor = vim.api.nvim_win_get_cursor(0) local row, col = cursor[1], cursor[2] local payload = string.format( '{"session_id": "%s", "event": "%s", "file": "%s", "line": %d, "col": %d}', server_name:gsub("\\", "\\\\"), ev.event, file:gsub("\\", "\\\\"), row, col ) local current_buf = ev.buf vim.fn.jobstart({ "curl", "-s", "-X", "POST", "http://127.0.0.1:3000/nvim/telemetry", "-H", "Content-Type: application/json", "-d", payload }, { stdout_buffered = true, on_stdout = function(_, data) if data and #data > 0 then local raw = table.concat(data, "") if raw ~= "" then local ok, resp = pcall(vim.fn.json_decode, raw) if ok and resp then vim.schedule(function() if (resp.tech_debts or resp.adrs) and _G.gemini and _G.gemini.project_knowledge then _G.gemini.project_knowledge(resp.tech_debts, resp.adrs, current_buf) end if _G.gemini and _G.gemini.update_statusline then last_active_task = resp.active_task last_active_gate = resp.active_gate _G.gemini.update_statusline(resp.active_task, resp.active_gate) end end) end end end end, }) local uv = vim.uv or vim.loop local client = uv.new_udp() if client then client:send(payload, "127.0.0.1", 3002, function(err) client:close() end) end end end end, desc = "Register active Neovim instance for Gemini CLI", }) -- 3. Push-driven Server-Sent Events (SSE) subscriber (Zero Polling) local sse_job_id = nil local function start_sse_subscriber() if sse_job_id then pcall(vim.fn.jobstop, sse_job_id) sse_job_id = nil end sse_job_id = vim.fn.jobstart({ "curl", "-s", "-N", "http://127.0.0.1:3000/api/events/stream" }, { on_stdout = function(_, data) if not data then return end for _, line in ipairs(data) do if line:match("^data:%s*") then local raw_json = line:gsub("^data:%s*", "") local ok, ev = pcall(vim.fn.json_decode, raw_json) if ok and ev and ev.payload then local topic = ev.topic local payload = ev.payload local changed = false if topic == "task:event" then if payload.active_task ~= nil then last_active_task = payload.active_task changed = true elseif payload.status == "in_progress" or payload.status == "active" then last_active_task = payload.title or payload.task_id changed = true elseif payload.status == "completed" or payload.status == "expired" or payload.status == "failed" then if last_active_task == (payload.title or payload.task_id) then last_active_task = nil changed = true end end elseif topic == "gate:event" then if payload.active_gate ~= nil then last_active_gate = payload.active_gate changed = true elseif payload.status then last_active_gate = payload.status changed = true end end if changed and _G.gemini and _G.gemini.update_statusline then vim.schedule(function() _G.gemini.update_statusline(last_active_task, last_active_gate) end) end end end end end, on_exit = function() sse_job_id = nil -- Reconnect after server restart without busy looping vim.defer_fn(function() if #vim.api.nvim_list_uis() > 0 then start_sse_subscriber() end end, 5000) end, }) end -- Initialize SSE push listener when UI is active if #vim.api.nvim_list_uis() > 0 then start_sse_subscriber() end -- Load UI & Keymaps local is_win = vim.fn.has("win32") == 1 local ui_path = is_win and "C:/Users/reazul.ashraf/workspace/rust/mcp-memory/gemini-ui.lua" or "/mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory/gemini-ui.lua" local ui = dofile(ui_path) ui.setup() _G.gemini = ui -- Expose globally so Agent can call it via execute_lua