From 6b25aef917cd15f5202b504359fe74850e1d9075 Mon Sep 17 00:00:00 2001 From: Carlos Gutierrez Date: Tue, 13 Jan 2026 22:07:02 -0500 Subject: [PATCH] fixing configuration to change the windows --- lua/codetyper/agent/logs.lua | 18 +++ lua/codetyper/ask.lua | 114 ++++++++++++++ lua/codetyper/llm/claude.lua | 288 ----------------------------------- 3 files changed, 132 insertions(+), 288 deletions(-) diff --git a/lua/codetyper/agent/logs.lua b/lua/codetyper/agent/logs.lua index 2300897..9393bd3 100644 --- a/lua/codetyper/agent/logs.lua +++ b/lua/codetyper/agent/logs.lua @@ -147,6 +147,24 @@ function M.error(message, data) M.log("error", "ERROR: " .. message, data) end +--- Log warning +---@param message string +---@param data? table +function M.warning(message, data) + M.log("warning", "WARN: " .. message, data) +end + +--- Add log entry (compatibility function for scheduler) +--- Accepts {type = "info", message = "..."} format +---@param entry table Log entry with type and message +function M.add(entry) + if entry.type == "clear" then + M.clear() + return + end + M.log(entry.type or "info", entry.message or "", entry.data) +end + --- Log thinking/reasoning step ---@param step string Description of what's happening function M.thinking(step) diff --git a/lua/codetyper/ask.lua b/lua/codetyper/ask.lua index eefbacd..4b2b880 100644 --- a/lua/codetyper/ask.lua +++ b/lua/codetyper/ask.lua @@ -24,6 +24,8 @@ local state = { referenced_files = {}, target_width = nil, -- Store the target width to maintain it agent_mode = false, -- Whether agent mode is enabled (can make file changes) + log_listener_id = nil, -- Listener ID for LLM logs + show_logs = true, -- Whether to show LLM logs in chat } --- Get the ask window configuration @@ -59,6 +61,7 @@ local function create_output_buffer() "║ C-Enter → send ║", "║ C-n → new chat ║", "║ C-f → add current file ║", + "║ L → toggle LLM logs ║", "║ :CoderType → switch mode ║", "║ q → close │ K/J → jump ║", "╚═════════════════════════════════╝", @@ -195,6 +198,11 @@ local function setup_output_keymaps(buf) M.copy_last_response() end, opts) + -- Toggle LLM logs with L + vim.keymap.set("n", "L", function() + M.toggle_logs() + end, opts) + -- Jump to input with i or J vim.keymap.set("n", "i", function() M.focus_input() @@ -276,6 +284,88 @@ local function setup_width_autocmd() }) end +--- Append log entry to output buffer +---@param entry table Log entry from agent/logs +local function append_log_to_output(entry) + if not state.show_logs then + return + end + + if not state.output_buf or not vim.api.nvim_buf_is_valid(state.output_buf) then + return + end + + -- Skip clear events + if entry.level == "clear" then + return + end + + -- Format the log entry with icons + local icons = { + info = "ℹ️", + debug = "🔍", + request = "📤", + response = "📥", + tool = "🔧", + error = "❌", + warning = "⚠️", + } + + local icon = icons[entry.level] or "•" + local formatted = string.format("[%s] %s %s", entry.timestamp, icon, entry.message) + + vim.schedule(function() + if not state.output_buf or not vim.api.nvim_buf_is_valid(state.output_buf) then + return + end + + vim.bo[state.output_buf].modifiable = true + + local lines = vim.api.nvim_buf_get_lines(state.output_buf, 0, -1, false) + + -- Add a subtle log line + table.insert(lines, " " .. formatted) + + vim.api.nvim_buf_set_lines(state.output_buf, 0, -1, false, lines) + vim.bo[state.output_buf].modifiable = false + + -- Scroll to bottom + if state.output_win and vim.api.nvim_win_is_valid(state.output_win) then + local line_count = vim.api.nvim_buf_line_count(state.output_buf) + pcall(vim.api.nvim_win_set_cursor, state.output_win, { line_count, 0 }) + end + end) +end + +--- Setup log listener for LLM logs +local function setup_log_listener() + -- Remove existing listener if any + if state.log_listener_id then + pcall(function() + local logs = require("codetyper.agent.logs") + logs.remove_listener(state.log_listener_id) + end) + state.log_listener_id = nil + end + + -- Add new listener + local ok, logs = pcall(require, "codetyper.agent.logs") + if ok then + state.log_listener_id = logs.add_listener(append_log_to_output) + end +end + +--- Remove log listener +local function remove_log_listener() + if state.log_listener_id then + pcall(function() + local logs = require("codetyper.agent.logs") + logs.remove_listener(state.log_listener_id) + end) + state.log_listener_id = nil + end +end + --- Open the ask panel function M.open() -- Use the is_open() function which validates window state @@ -335,6 +425,9 @@ function M.open() state.is_open = true + -- Setup log listener for LLM logs + setup_log_listener() + -- Setup autocmd to maintain width setup_width_autocmd() @@ -366,6 +459,9 @@ function M.open() state.is_open = false state.target_width = nil + -- Remove log listener + remove_log_listener() + -- Clean up autocmd groups pcall(vim.api.nvim_del_augroup_by_id, close_group) if ask_augroup then @@ -466,6 +562,9 @@ end --- Close the ask panel function M.close() + -- Remove the log listener + remove_log_listener() + -- Remove the width maintenance autocmd first if ask_augroup then pcall(vim.api.nvim_del_augroup_by_id, ask_augroup) @@ -796,6 +895,7 @@ function M.clear_history() "║ C-Enter → send ║", "║ C-n → new chat ║", "║ C-f → add current file ║", + "║ L → toggle LLM logs ║", "║ :CoderType → switch mode ║", "║ q → close │ K/J → jump ║", "╚═════════════════════════════════╝", @@ -881,4 +981,18 @@ function M.get_history() return state.history end +--- Toggle LLM log visibility in chat +---@return boolean New state +function M.toggle_logs() + state.show_logs = not state.show_logs + utils.notify("LLM logs " .. (state.show_logs and "enabled" or "disabled")) + return state.show_logs +end + +--- Check if logs are enabled +---@return boolean +function M.logs_enabled() + return state.show_logs +end + return M diff --git a/lua/codetyper/llm/claude.lua b/lua/codetyper/llm/claude.lua index 64c76d7..c66d5e2 100644 --- a/lua/codetyper/llm/claude.lua +++ b/lua/codetyper/llm/claude.lua @@ -361,292 +361,4 @@ function M.format_messages_for_claude(messages) return formatted end ---- Generate with tool use support for agentic mode ----@param messages table[] Conversation history ----@param context table Context information ----@param tool_definitions table Tool definitions ----@param callback fun(response: table|nil, error: string|nil) Callback with raw response -function M.generate_with_tools(messages, context, tool_definitions, callback) - local api_key = get_api_key() - if not api_key then - callback(nil, "Claude API key not configured") - return - end - - local tools_module = require("codetyper.agent.tools") - local agent_prompts = require("codetyper.prompts.agent") - - -- Build system prompt with agent instructions - local system_prompt = llm.build_system_prompt(context) - system_prompt = system_prompt .. "\n\n" .. agent_prompts.system - system_prompt = system_prompt .. "\n\n" .. agent_prompts.tool_instructions - - -- Build request body with tools - local body = { - model = get_model(), - max_tokens = 4096, - system = system_prompt, - messages = M.format_messages_for_claude(messages), - tools = tools_module.to_claude_format(), - } - - local json_body = vim.json.encode(body) - - local cmd = { - "curl", - "-s", - "-X", "POST", - API_URL, - "-H", "Content-Type: application/json", - "-H", "x-api-key: " .. api_key, - "-H", "anthropic-version: 2023-06-01", - "-d", json_body, - } - - vim.fn.jobstart(cmd, { - stdout_buffered = true, - on_stdout = function(_, data) - if not data or #data == 0 or (data[1] == "" and #data == 1) then - return - end - - local response_text = table.concat(data, "\n") - local ok, response = pcall(vim.json.decode, response_text) - - if not ok then - vim.schedule(function() - callback(nil, "Failed to parse Claude response") - end) - return - end - - if response.error then - vim.schedule(function() - callback(nil, response.error.message or "Claude API error") - end) - return - end - - -- Return raw response for parser to handle - vim.schedule(function() - callback(response, nil) - end) - end, - on_stderr = function(_, data) - if data and #data > 0 and data[1] ~= "" then - vim.schedule(function() - callback(nil, "Claude API request failed: " .. table.concat(data, "\n")) - end) - end - end, - on_exit = function(_, code) - if code ~= 0 then - vim.schedule(function() - callback(nil, "Claude API request failed with code: " .. code) - end) - end - end, - }) -end - ---- Format messages for Claude API ----@param messages table[] Internal message format ----@return table[] Claude API message format -function M.format_messages_for_claude(messages) - local formatted = {} - - for _, msg in ipairs(messages) do - if msg.role == "user" then - if type(msg.content) == "table" then - -- Tool results - table.insert(formatted, { - role = "user", - content = msg.content, - }) - else - table.insert(formatted, { - role = "user", - content = msg.content, - }) - end - elseif msg.role == "assistant" then - -- Build content array for assistant messages - local content = {} - - -- Add text if present - if msg.content and msg.content ~= "" then - table.insert(content, { - type = "text", - text = msg.content, - }) - end - - -- Add tool uses if present - if msg.tool_calls then - for _, tool_call in ipairs(msg.tool_calls) do - table.insert(content, { - type = "tool_use", - id = tool_call.id, - name = tool_call.name, - input = tool_call.parameters, - }) - end - end - - if #content > 0 then - table.insert(formatted, { - role = "assistant", - content = content, - }) - end - end - end - - return formatted -end - ---- Generate with tool use support for agentic mode ----@param messages table[] Conversation history ----@param context table Context information ----@param tool_definitions table Tool definitions ----@param callback fun(response: table|nil, error: string|nil) Callback with raw response -function M.generate_with_tools(messages, context, tool_definitions, callback) - local api_key = get_api_key() - if not api_key then - callback(nil, "Claude API key not configured") - return - end - - local tools_module = require("codetyper.agent.tools") - local agent_prompts = require("codetyper.prompts.agent") - - -- Build system prompt with agent instructions - local system_prompt = llm.build_system_prompt(context) - system_prompt = system_prompt .. "\n\n" .. agent_prompts.system - system_prompt = system_prompt .. "\n\n" .. agent_prompts.tool_instructions - - -- Build request body with tools - local body = { - model = get_model(), - max_tokens = 4096, - system = system_prompt, - messages = M.format_messages_for_claude(messages), - tools = tools_module.to_claude_format(), - } - - local json_body = vim.json.encode(body) - - local cmd = { - "curl", - "-s", - "-X", "POST", - API_URL, - "-H", "Content-Type: application/json", - "-H", "x-api-key: " .. api_key, - "-H", "anthropic-version: 2023-06-01", - "-d", json_body, - } - - vim.fn.jobstart(cmd, { - stdout_buffered = true, - on_stdout = function(_, data) - if not data or #data == 0 or (data[1] == "" and #data == 1) then - return - end - - local response_text = table.concat(data, "\n") - local ok, response = pcall(vim.json.decode, response_text) - - if not ok then - vim.schedule(function() - callback(nil, "Failed to parse Claude response") - end) - return - end - - if response.error then - vim.schedule(function() - callback(nil, response.error.message or "Claude API error") - end) - return - end - - -- Return raw response for parser to handle - vim.schedule(function() - callback(response, nil) - end) - end, - on_stderr = function(_, data) - if data and #data > 0 and data[1] ~= "" then - vim.schedule(function() - callback(nil, "Claude API request failed: " .. table.concat(data, "\n")) - end) - end - end, - on_exit = function(_, code) - if code ~= 0 then - vim.schedule(function() - callback(nil, "Claude API request failed with code: " .. code) - end) - end - end, - }) -end - ---- Format messages for Claude API ----@param messages table[] Internal message format ----@return table[] Claude API message format -function M.format_messages_for_claude(messages) - local formatted = {} - - for _, msg in ipairs(messages) do - if msg.role == "user" then - if type(msg.content) == "table" then - -- Tool results - table.insert(formatted, { - role = "user", - content = msg.content, - }) - else - table.insert(formatted, { - role = "user", - content = msg.content, - }) - end - elseif msg.role == "assistant" then - -- Build content array for assistant messages - local content = {} - - -- Add text if present - if msg.content and msg.content ~= "" then - table.insert(content, { - type = "text", - text = msg.content, - }) - end - - -- Add tool uses if present - if msg.tool_calls then - for _, tool_call in ipairs(msg.tool_calls) do - table.insert(content, { - type = "tool_use", - id = tool_call.id, - name = tool_call.name, - input = tool_call.parameters, - }) - end - end - - if #content > 0 then - table.insert(formatted, { - role = "assistant", - content = content, - }) - end - end - end - - return formatted -end - return M