Adding more features

This commit is contained in:
2026-01-15 20:58:56 -05:00
parent 84c8bcf92c
commit f5df1a9ac0
40 changed files with 9145 additions and 458 deletions

View File

@@ -287,6 +287,118 @@ local function cmd_agent_stop()
end
end
--- Run the agentic loop with a task
---@param task string The task to accomplish
---@param agent_name? string Optional agent name
local function cmd_agentic_run(task, agent_name)
local agentic = require("codetyper.agent.agentic")
local logs_panel = require("codetyper.logs_panel")
local logs = require("codetyper.agent.logs")
-- Open logs panel
logs_panel.open()
logs.info("Starting agentic task: " .. task:sub(1, 50) .. "...")
utils.notify("Running agentic task...", vim.log.levels.INFO)
-- Get current file for context
local current_file = vim.fn.expand("%:p")
local files = {}
if current_file ~= "" then
table.insert(files, current_file)
end
agentic.run({
task = task,
files = files,
agent = agent_name or "coder",
on_status = function(status)
logs.thinking(status)
end,
on_tool_start = function(name, args)
logs.info("Tool: " .. name)
end,
on_tool_end = function(name, result, err)
if err then
logs.error(name .. " failed: " .. err)
else
logs.debug(name .. " completed")
end
end,
on_file_change = function(path, action)
logs.info("File " .. action .. ": " .. path)
end,
on_message = function(msg)
if msg.role == "assistant" and type(msg.content) == "string" and msg.content ~= "" then
logs.thinking(msg.content:sub(1, 100) .. "...")
end
end,
on_complete = function(result, err)
if err then
logs.error("Task failed: " .. err)
utils.notify("Agentic task failed: " .. err, vim.log.levels.ERROR)
else
logs.info("Task completed successfully")
utils.notify("Agentic task completed!", vim.log.levels.INFO)
if result and result ~= "" then
-- Show summary in a float
vim.schedule(function()
vim.notify("Result:\n" .. result:sub(1, 500), vim.log.levels.INFO)
end)
end
end
end,
})
end
--- List available agents
local function cmd_agentic_list()
local agentic = require("codetyper.agent.agentic")
local agents = agentic.list_agents()
local lines = {
"Available Agents",
"================",
"",
}
for _, agent in ipairs(agents) do
local badge = agent.builtin and "[builtin]" or "[custom]"
table.insert(lines, string.format(" %s %s", agent.name, badge))
table.insert(lines, string.format(" %s", agent.description))
table.insert(lines, "")
end
table.insert(lines, "Use :CoderAgenticRun <task> [agent] to run a task")
table.insert(lines, "Use :CoderAgenticInit to create custom agents")
utils.notify(table.concat(lines, "\n"))
end
--- Initialize .coder/agents/ and .coder/rules/ directories
local function cmd_agentic_init()
local agentic = require("codetyper.agent.agentic")
agentic.init()
local agents_dir = vim.fn.getcwd() .. "/.coder/agents"
local rules_dir = vim.fn.getcwd() .. "/.coder/rules"
local lines = {
"Initialized Coder directories:",
"",
" " .. agents_dir,
" - example.md (template for custom agents)",
"",
" " .. rules_dir,
" - code-style.md (template for project rules)",
"",
"Edit these files to customize agent behavior.",
"Create new .md files to add more agents/rules.",
}
utils.notify(table.concat(lines, "\n"))
end
--- Show chat type switcher modal (Ask/Agent)
local function cmd_type_toggle()
local switcher = require("codetyper.chat_switcher")
@@ -844,6 +956,65 @@ end
--- Main command dispatcher
---@param args table Command arguments
--- Show LLM accuracy statistics
local function cmd_llm_stats()
local llm = require("codetyper.llm")
local stats = llm.get_accuracy_stats()
local lines = {
"LLM Provider Accuracy Statistics",
"================================",
"",
string.format("Ollama:"),
string.format(" Total requests: %d", stats.ollama.total),
string.format(" Correct: %d", stats.ollama.correct),
string.format(" Accuracy: %.1f%%", stats.ollama.accuracy * 100),
"",
string.format("Copilot:"),
string.format(" Total requests: %d", stats.copilot.total),
string.format(" Correct: %d", stats.copilot.correct),
string.format(" Accuracy: %.1f%%", stats.copilot.accuracy * 100),
"",
"Note: Smart selection prefers Ollama when brain memories",
"provide enough context. Accuracy improves over time via",
"pondering (verification with other LLMs).",
}
vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO)
end
--- Report feedback on last LLM response
---@param was_good boolean Whether the response was good
local function cmd_llm_feedback(was_good)
local llm = require("codetyper.llm")
-- Get the last used provider from logs or default
local provider = "ollama" -- Default assumption
-- Try to get actual last provider from logs
pcall(function()
local logs = require("codetyper.agent.logs")
local entries = logs.get(10)
for i = #entries, 1, -1 do
local entry = entries[i]
if entry.message and entry.message:match("^LLM:") then
provider = entry.message:match("LLM: (%w+)") or provider
break
end
end
end)
llm.report_feedback(provider, was_good)
local feedback_type = was_good and "positive" or "negative"
utils.notify(string.format("Reported %s feedback for %s", feedback_type, provider), vim.log.levels.INFO)
end
--- Reset LLM accuracy statistics
local function cmd_llm_reset_stats()
local selector = require("codetyper.llm.selector")
selector.reset_accuracy_stats()
utils.notify("LLM accuracy statistics reset", vim.log.levels.INFO)
end
local function coder_cmd(args)
local subcommand = args.fargs[1] or "toggle"
@@ -872,6 +1043,17 @@ local function coder_cmd(args)
["logs-toggle"] = cmd_logs_toggle,
["queue-status"] = cmd_queue_status,
["queue-process"] = cmd_queue_process,
-- Agentic commands
["agentic-run"] = function(args)
local task = table.concat(vim.list_slice(args.fargs, 2), " ")
if task == "" then
utils.notify("Usage: Coder agentic-run <task> [agent]", vim.log.levels.WARN)
return
end
cmd_agentic_run(task)
end,
["agentic-list"] = cmd_agentic_list,
["agentic-init"] = cmd_agentic_init,
["index-project"] = cmd_index_project,
["index-status"] = cmd_index_status,
memories = cmd_memories,
@@ -901,6 +1083,41 @@ local function coder_cmd(args)
end
end
end,
-- LLM smart selection commands
["llm-stats"] = cmd_llm_stats,
["llm-feedback-good"] = function()
cmd_llm_feedback(true)
end,
["llm-feedback-bad"] = function()
cmd_llm_feedback(false)
end,
["llm-reset-stats"] = cmd_llm_reset_stats,
-- Cost tracking commands
["cost"] = function()
local cost = require("codetyper.cost")
cost.toggle()
end,
["cost-clear"] = function()
local cost = require("codetyper.cost")
cost.clear()
end,
-- Credentials management commands
["add-api-key"] = function()
local credentials = require("codetyper.credentials")
credentials.interactive_add()
end,
["remove-api-key"] = function()
local credentials = require("codetyper.credentials")
credentials.interactive_remove()
end,
["credentials"] = function()
local credentials = require("codetyper.credentials")
credentials.show_status()
end,
["switch-provider"] = function()
local credentials = require("codetyper.credentials")
credentials.interactive_switch_provider()
end,
}
local cmd_fn = commands[subcommand]
@@ -922,10 +1139,14 @@ function M.setup()
"ask", "ask-close", "ask-toggle", "ask-clear",
"transform", "transform-cursor",
"agent", "agent-close", "agent-toggle", "agent-stop",
"agentic-run", "agentic-list", "agentic-init",
"type-toggle", "logs-toggle",
"queue-status", "queue-process",
"index-project", "index-status", "memories", "forget",
"auto-toggle", "auto-set",
"llm-stats", "llm-feedback-good", "llm-feedback-bad", "llm-reset-stats",
"cost", "cost-clear",
"add-api-key", "remove-api-key", "credentials", "switch-provider",
}
end,
desc = "Codetyper.nvim commands",
@@ -997,6 +1218,31 @@ function M.setup()
cmd_agent_stop()
end, { desc = "Stop running agent" })
-- Agentic commands (full IDE-like agent functionality)
vim.api.nvim_create_user_command("CoderAgenticRun", function(opts)
local task = opts.args
if task == "" then
vim.ui.input({ prompt = "Task: " }, function(input)
if input and input ~= "" then
cmd_agentic_run(input)
end
end)
else
cmd_agentic_run(task)
end
end, {
desc = "Run agentic task (IDE-like multi-file changes)",
nargs = "*",
})
vim.api.nvim_create_user_command("CoderAgenticList", function()
cmd_agentic_list()
end, { desc = "List available agents" })
vim.api.nvim_create_user_command("CoderAgenticInit", function()
cmd_agentic_init()
end, { desc = "Initialize .coder/agents/ and .coder/rules/ directories" })
-- Chat type switcher command
vim.api.nvim_create_user_command("CoderType", function()
cmd_type_toggle()
@@ -1075,6 +1321,147 @@ function M.setup()
end,
})
-- Brain feedback command - teach the brain from your experience
vim.api.nvim_create_user_command("CoderFeedback", function(opts)
local brain = require("codetyper.brain")
if not brain.is_initialized() then
vim.notify("Brain not initialized", vim.log.levels.WARN)
return
end
local feedback_type = opts.args:lower()
local current_file = vim.fn.expand("%:p")
if feedback_type == "good" or feedback_type == "accept" or feedback_type == "+" then
-- Learn positive feedback
brain.learn({
type = "user_feedback",
file = current_file,
timestamp = os.time(),
data = {
feedback = "accepted",
description = "User marked code as good/accepted",
},
})
vim.notify("Brain: Learned positive feedback ✓", vim.log.levels.INFO)
elseif feedback_type == "bad" or feedback_type == "reject" or feedback_type == "-" then
-- Learn negative feedback
brain.learn({
type = "user_feedback",
file = current_file,
timestamp = os.time(),
data = {
feedback = "rejected",
description = "User marked code as bad/rejected",
},
})
vim.notify("Brain: Learned negative feedback ✗", vim.log.levels.INFO)
elseif feedback_type == "stats" or feedback_type == "status" then
-- Show brain stats
local stats = brain.stats()
local msg = string.format(
"Brain Stats:\n• Nodes: %d\n• Edges: %d\n• Pending: %d\n• Deltas: %d",
stats.node_count or 0,
stats.edge_count or 0,
stats.pending_changes or 0,
stats.delta_count or 0
)
vim.notify(msg, vim.log.levels.INFO)
else
vim.notify("Usage: CoderFeedback <good|bad|stats>", vim.log.levels.INFO)
end
end, {
desc = "Give feedback to the brain (good/bad/stats)",
nargs = "?",
complete = function()
return { "good", "bad", "stats" }
end,
})
-- Brain stats command
vim.api.nvim_create_user_command("CoderBrain", function(opts)
local brain = require("codetyper.brain")
if not brain.is_initialized() then
vim.notify("Brain not initialized", vim.log.levels.WARN)
return
end
local action = opts.args:lower()
if action == "stats" or action == "" then
local stats = brain.stats()
local lines = {
"╭─────────────────────────────────╮",
"│ CODETYPER BRAIN │",
"╰─────────────────────────────────╯",
"",
string.format(" Nodes: %d", stats.node_count or 0),
string.format(" Edges: %d", stats.edge_count or 0),
string.format(" Deltas: %d", stats.delta_count or 0),
string.format(" Pending: %d", stats.pending_changes or 0),
"",
" The more you use Codetyper,",
" the smarter it becomes!",
}
vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO)
elseif action == "commit" then
local hash = brain.commit("Manual commit")
if hash then
vim.notify("Brain: Committed changes (hash: " .. hash:sub(1, 8) .. ")", vim.log.levels.INFO)
else
vim.notify("Brain: Nothing to commit", vim.log.levels.INFO)
end
elseif action == "flush" then
brain.flush()
vim.notify("Brain: Flushed to disk", vim.log.levels.INFO)
elseif action == "prune" then
local pruned = brain.prune()
vim.notify("Brain: Pruned " .. pruned .. " low-value nodes", vim.log.levels.INFO)
else
vim.notify("Usage: CoderBrain <stats|commit|flush|prune>", vim.log.levels.INFO)
end
end, {
desc = "Brain management commands",
nargs = "?",
complete = function()
return { "stats", "commit", "flush", "prune" }
end,
})
-- Cost estimation command
vim.api.nvim_create_user_command("CoderCost", function()
local cost = require("codetyper.cost")
cost.toggle()
end, { desc = "Show LLM cost estimation window" })
-- Credentials management commands
vim.api.nvim_create_user_command("CoderAddApiKey", function()
local credentials = require("codetyper.credentials")
credentials.interactive_add()
end, { desc = "Add or update LLM provider API key" })
vim.api.nvim_create_user_command("CoderRemoveApiKey", function()
local credentials = require("codetyper.credentials")
credentials.interactive_remove()
end, { desc = "Remove LLM provider credentials" })
vim.api.nvim_create_user_command("CoderCredentials", function()
local credentials = require("codetyper.credentials")
credentials.show_status()
end, { desc = "Show credentials status" })
vim.api.nvim_create_user_command("CoderSwitchProvider", function()
local credentials = require("codetyper.credentials")
credentials.interactive_switch_provider()
end, { desc = "Switch active LLM provider" })
-- Setup default keymaps
M.setup_keymaps()
end