refactor: reorganize, add package (opencode, osgrep, beads), add ai agent shell

This commit is contained in:
Rasyidan Akbar F. 2025-12-09 15:09:05 +07:00
commit a0f0f39399
58 changed files with 7867 additions and 42 deletions

View file

@ -0,0 +1,52 @@
return {
"okuuva/auto-save.nvim",
event = { "InsertLeave", "TextChanged" },
opts = {
enabled = true, -- Plugin is loaded but conditional on manual toggle
execution_message = {
enabled = true,
message = function()
return "AutoSave: saved at " .. vim.fn.strftime("%H:%M:%S")
end,
dim = 0.18,
cleaning_interval = 1250,
},
trigger_events = {
immediate_save = { "BufLeave", "FocusLost" },
defer_save = { "InsertLeave", "TextChanged" },
cancel_deferred_save = { "InsertEnter" },
},
condition = function(buf)
-- Only save if manually enabled
if not vim.g.auto_save_enabled then
return false
end
local fn = vim.fn
local utils = require("auto-save.utils.data")
-- Don't save for special buffer types
if utils.not_in(fn.getbufvar(buf, "&filetype"), {}) and fn.getbufvar(buf, "&buftype") == "" and fn.filereadable(fn.bufname(buf)) == 1 then
return true
end
return false
end,
write_all_buffers = false,
debounce_delay = 1000, -- Wait 1 second after typing stops before saving
},
keys = {
{
"<leader>ta",
function()
vim.g.auto_save_enabled = not vim.g.auto_save_enabled
if vim.g.auto_save_enabled then
vim.notify("Auto-save enabled", vim.log.levels.INFO)
else
vim.notify("Auto-save disabled", vim.log.levels.INFO)
end
end,
desc = "Toggle auto-save",
},
},
}

View file

@ -0,0 +1,80 @@
return {
-- Auto pairs
-- Automatically inserts a matching closing character
-- when you type an opening character like `"`, `[`, or `(`.
{
"nvim-mini/mini.pairs",
event = "VeryLazy",
opts = {
modes = { insert = true, command = true, terminal = false },
-- skip autopair when next character is one of these
skip_next = [=[[%w%%%'%[%"%%.%`%$]]=],
-- skip autopair when the cursor is inside these treesitter nodes
skip_ts = { "string" },
-- skip autopair when next character is closing pair
-- and there are more closing pairs than opening pairs
skip_unbalanced = true,
-- better deal with markdown code blocks
markdown = true,
},
config = function(_, opts)
LazyVim.mini.pairs(opts)
end,
},
-- Improves comment syntax, lets Neovim handle multiple
-- types of comments for a single language, and relaxes rules
-- for uncommenting.
{
"folke/ts-comments.nvim",
event = "VeryLazy",
opts = {},
},
-- Extends the a & i text objects, this adds the ability to select
-- arguments, function calls, text within quotes and brackets
{
"nvim-mini/mini.ai",
event = "VeryLazy",
opts = function()
local ai = require("mini.ai")
return {
n_lines = 500,
custom_textobjects = {
o = ai.gen_spec.treesitter({
a = { "@block.outer", "@conditional.outer", "@loop.outer" },
i = { "@block.inner", "@conditional.inner", "@loop.inner" },
}),
f = ai.gen_spec.treesitter({ a = "@function.outer", i = "@function.inner" }),
c = ai.gen_spec.treesitter({ a = "@class.outer", i = "@class.inner" }),
t = { "<(%p%w-)%f[^<%w][^<>]->.*()</[^/]->$" },
d = { "%f[%d]%d+" },
e = { { "%u[%l%d]+%f[^%l%d]", "%f[%S][%l%d]+%f[^%l%d]" }, "^().*()$" },
g = LazyVim.mini.ai_buffer,
u = ai.gen_spec.function_call(),
U = ai.gen_spec.function_call({ name_pattern = "[%w_]" }),
},
}
end,
config = function(_, opts)
require("mini.ai").setup(opts)
-- mini.clue handles text object hints automatically
end,
},
-- Configures LuaLS to support auto-completion and type checking
-- while editing your Neovim configuration.
{
"folke/lazydev.nvim",
ft = "lua",
cmd = "LazyDev",
opts = {
library = {
{ path = "${3rd}/luv/library", words = { "vim.uv" } },
{ path = "LazyVim", words = { "LazyVim" } },
{ path = "snacks.nvim", words = { "Snacks" } },
{ path = "lazy.nvim", words = { "LazyVim" } },
},
},
},
}

View file

@ -0,0 +1,75 @@
return {
-- gruvbox
{
"ellisonleao/gruvbox.nvim",
lazy = false,
priority = 1000,
opts = {},
config = function()
require("gruvbox").setup({})
vim.cmd.colorscheme("gruvbox")
end,
},
-- tokyonight
{
"folke/tokyonight.nvim",
lazy = true,
opts = { style = "moon" },
},
-- catppuccin
{
"catppuccin/nvim",
lazy = true,
priority = 1000,
name = "catppuccin",
opts = {
lsp_styles = {
underlines = {
errors = { "undercurl" },
hints = { "undercurl" },
warnings = { "undercurl" },
information = { "undercurl" },
},
},
-- integrations = {
-- aerial = true,
-- alpha = true,
-- cmp = true,
-- dashboard = true,
-- flash = true,
-- fzf = true,
-- grug_far = true,
-- gitsigns = true,
-- headlines = true,
-- illuminate = true,
-- indent_blankline = { enabled = true },
-- leap = true,
-- lsp_trouble = true,
-- mason = true,
-- mini = true,
-- navic = { enabled = true, custom_bg = "lualine" },
-- neotest = true,
-- neotree = true,
-- noice = true,
-- notify = true,
-- snacks = true,
-- telescope = true,
-- treesitter_context = true,
-- which_key = true,
-- },
},
-- specs = {
-- {
-- "akinsho/bufferline.nvim",
-- optional = true,
-- opts = function(_, opts)
-- if (vim.g.colors_name or ""):find("catppuccin") then
-- opts.highlights = require("catppuccin.special.bufferline").get_theme()
-- end
-- end,
-- },
-- },
},
}

View file

@ -0,0 +1,48 @@
return {
-- Modern completion engine with built-in fuzzy matching
-- Note: Installed via Nix with pre-built Rust backend (see neovim.nix)
{
"saghen/blink.cmp",
dependencies = {
"rafamadriz/friendly-snippets",
},
version = "1.*",
event = "InsertEnter",
build = nil, -- Disable build since Nix provides pre-compiled version
opts = {
-- Keymap preset options: 'default' | 'super-tab' | 'enter'
keymap = { preset = "super-tab" },
appearance = {
-- Use mono nerd font variant
nerd_font_variant = "mono",
},
-- Default completion sources
sources = {
default = { "lsp", "path", "snippets", "buffer" },
},
completion = {
documentation = {
auto_show = true,
auto_show_delay_ms = 500,
},
menu = {
draw = {
columns = { { "label", "label_description", gap = 1 }, { "kind_icon", "kind" } },
},
},
},
-- Fuzzy matching implementation
-- Using "rust" since Nix provides pre-built native module
fuzzy = {
use_typo_resistance = true,
use_frecency = true,
use_proximity = true,
},
},
opts_extend = { "sources.default" },
},
}

View file

@ -0,0 +1,33 @@
return {
{
"folke/snacks.nvim",
opts = {
dashboard = {
preset = {
header = [[
]],
},
},
},
},
}

View file

@ -0,0 +1,56 @@
return {
-- Telescope - Fuzzy finder over lists
{
"nvim-telescope/telescope.nvim",
tag = "0.1.8",
dependencies = {
"nvim-lua/plenary.nvim",
},
cmd = "Telescope",
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find Files" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live Grep" },
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Buffers" },
{ "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help Tags" },
},
opts = {
defaults = {
-- Removed which_key mapping - mini.clue handles keybind hints globally
},
},
},
-- Telescope FZF native - Better sorting performance
-- Note: Installed via Nix (see neovim.nix) to avoid build issues
{
"nvim-telescope/telescope-fzf-native.nvim",
dependencies = { "nvim-telescope/telescope.nvim" },
config = function()
require("telescope").load_extension("fzf")
end,
},
-- Neo-tree - File explorer tree
{
"nvim-neo-tree/neo-tree.nvim",
branch = "v3.x",
dependencies = {
"nvim-lua/plenary.nvim",
"MunifTanjim/nui.nvim",
"nvim-tree/nvim-web-devicons",
},
cmd = "Neotree",
keys = {
{ "<leader>e", "<cmd>Neotree toggle<cr>", desc = "Toggle Neo-tree" },
{ "<leader>o", "<cmd>Neotree focus<cr>", desc = "Focus Neo-tree" },
},
opts = {
filesystem = {
follow_current_file = {
enabled = true,
},
use_libuv_file_watcher = true,
},
},
},
}

View file

@ -0,0 +1,59 @@
return {
{
"stevearc/conform.nvim",
event = { "BufWritePre" },
cmd = { "ConformInfo" },
keys = {
{
"<leader>cf",
function()
require("conform").format({ async = true, lsp_fallback = true })
end,
mode = { "n", "v" },
desc = "Format buffer",
},
},
opts = {
formatters_by_ft = {
lua = { "stylua" },
nix = { "nixfmt" },
python = { "ruff_format", "ruff_organize_imports" },
javascript = { "prettierd", "prettier", stop_after_first = true },
typescript = { "prettierd", "prettier", stop_after_first = true },
javascriptreact = { "prettierd", "prettier", stop_after_first = true },
typescriptreact = { "prettierd", "prettier", stop_after_first = true },
json = { "prettierd", "prettier", stop_after_first = true },
jsonc = { "prettierd", "prettier", stop_after_first = true },
yaml = { "prettierd", "prettier", stop_after_first = true },
markdown = { "prettierd", "prettier", stop_after_first = true },
html = { "prettierd", "prettier", stop_after_first = true },
css = { "prettierd", "prettier", stop_after_first = true },
scss = { "prettierd", "prettier", stop_after_first = true },
rust = { "rustfmt" },
go = { "gofumpt" },
sh = { "shfmt" },
bash = { "shfmt" },
zsh = { "shfmt" },
},
-- Format on save
format_on_save = {
timeout_ms = 500,
lsp_fallback = true,
},
-- Customize formatters
formatters = {
shfmt = {
prepend_args = { "-i", "2" }, -- 2 space indent
},
ruff_format = {
command = "ruff",
args = { "format", "--stdin-filename", "$FILENAME" },
},
ruff_organize_imports = {
command = "ruff",
args = { "check", "--select", "I", "--fix", "--stdin-filename", "$FILENAME" },
},
},
},
},
}

View file

@ -0,0 +1,58 @@
return {
{
"mfussenegger/nvim-lint",
event = { "BufReadPost", "BufNewFile", "BufWritePost" },
opts = {
events = { "BufWritePost", "BufReadPost", "InsertLeave" },
linters_by_ft = {
javascript = { "eslint" },
typescript = { "eslint" },
javascriptreact = { "eslint" },
typescriptreact = { "eslint" },
python = { "ruff" },
sh = { "shellcheck" },
bash = { "shellcheck" },
},
linters = {},
},
config = function(_, opts)
local lint = require("lint")
-- Merge custom linter configs
for name, linter in pairs(opts.linters) do
if type(linter) == "table" and type(lint.linters[name]) == "table" then
lint.linters[name] = vim.tbl_deep_extend("force", lint.linters[name], linter)
else
lint.linters[name] = linter
end
end
lint.linters_by_ft = opts.linters_by_ft
-- Debounced lint function with proper timer management
local timer = vim.uv.new_timer()
local function debounce_lint()
-- Stop existing timer before starting new one to prevent EALREADY error
timer:stop()
timer:start(100, 0, vim.schedule_wrap(lint.try_lint))
end
-- Create autocommand for linting
vim.api.nvim_create_autocmd(opts.events, {
group = vim.api.nvim_create_augroup("nvim-lint", { clear = true }),
callback = debounce_lint,
})
-- Clean up timer on exit
vim.api.nvim_create_autocmd("VimLeavePre", {
group = vim.api.nvim_create_augroup("nvim-lint-cleanup", { clear = true }),
callback = function()
if timer and not timer:is_closing() then
timer:stop()
timer:close()
end
end,
})
end,
},
}

View file

@ -0,0 +1,106 @@
-- LSP configuration using Nix-managed servers
return {
-- Disable Mason plugins (we use Nix-managed servers instead)
{ "mason-org/mason.nvim", enabled = false },
{ "mason-org/mason-lspconfig.nvim", enabled = false },
{ "jay-babu/mason-null-ls.nvim", enabled = false },
{ "jay-babu/mason-nvim-dap.nvim", enabled = false },
{
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"saghen/blink.cmp", -- For LSP capabilities
},
config = function()
-- Get blink.cmp capabilities
local capabilities = require("blink.cmp").get_lsp_capabilities()
-- Common LSP attach keybindings
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("UserLspConfig", {}),
callback = function(ev)
local opts = { buffer = ev.buf }
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
vim.keymap.set("n", "<leader>d", vim.diagnostic.open_float, opts)
vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
end,
})
-- Configure each LSP server
local lspconfig = require("lspconfig")
-- Lua
lspconfig.lua_ls.setup({
capabilities = capabilities,
settings = {
Lua = {
runtime = { version = "LuaJIT" },
diagnostics = { globals = { "vim" } },
workspace = {
library = vim.api.nvim_get_runtime_file("", true),
checkThirdParty = false,
},
telemetry = { enable = false },
},
},
})
-- Nix
lspconfig.nil_ls.setup({
capabilities = capabilities,
settings = {
["nil"] = {
formatting = { command = { "nixfmt" } },
},
},
})
-- Python
lspconfig.pyright.setup({
capabilities = capabilities,
settings = {
python = {
analysis = {
typeCheckingMode = "basic",
autoSearchPaths = true,
useLibraryCodeForTypes = true,
},
},
},
})
-- TypeScript/JavaScript
lspconfig.ts_ls.setup({
capabilities = capabilities,
})
-- Rust
lspconfig.rust_analyzer.setup({
capabilities = capabilities,
settings = {
["rust-analyzer"] = {
checkOnSave = { command = "clippy" },
},
},
})
-- Go
lspconfig.gopls.setup({
capabilities = capabilities,
})
-- C/C++
lspconfig.clangd.setup({
capabilities = capabilities,
})
end,
},
}

View file

@ -0,0 +1,4 @@
return {
"sphamba/smear-cursor.nvim",
opts = {},
}

View file

@ -0,0 +1,51 @@
return {
{
"nvim-treesitter/nvim-treesitter",
opts = {
ensure_installed = {
"bash",
"c",
"cpp",
"css",
"diff",
"dockerfile",
"git_config",
"git_rebase",
"gitcommit",
"gitignore",
"go",
"gomod",
"gosum",
"html",
"javascript",
"jsdoc",
"json",
"jsonc",
"latex",
"lua",
"luadoc",
"luap",
"markdown",
"markdown_inline",
"nix",
"norg",
"printf",
"python",
"query",
"regex",
"rust",
"scss",
"svelte",
"toml",
"tsx",
"typescript",
"typst",
"vim",
"vimdoc",
"vue",
"xml",
"yaml",
},
},
},
}

View file

@ -0,0 +1,174 @@
return {
-- Better UI for messages, cmdline and the popupmenu
{
"folke/noice.nvim",
event = "VeryLazy",
opts = {
lsp = {
override = {
["vim.lsp.util.convert_input_to_markdown_lines"] = true,
["vim.lsp.util.stylize_markdown"] = true,
["cmp.entry.get_documentation"] = true,
},
},
routes = {
{
filter = {
event = "msg_show",
any = {
{ find = "%d+L, %d+B" },
{ find = "; after #%d+" },
{ find = "; before #%d+" },
},
},
view = "mini",
},
},
presets = {
bottom_search = true,
command_palette = true,
long_message_to_split = true,
},
},
keys = {
{ "<leader>sn", "", desc = "+noice" },
{ "<leader>snl", function() require("noice").cmd("last") end, desc = "Noice Last Message" },
{ "<leader>snh", function() require("noice").cmd("history") end, desc = "Noice History" },
{ "<leader>sna", function() require("noice").cmd("all") end, desc = "Noice All" },
{ "<leader>snd", function() require("noice").cmd("dismiss") end, desc = "Dismiss All" },
},
dependencies = {
"MunifTanjim/nui.nvim",
},
},
-- Statusline
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = function()
return {
options = {
theme = "auto",
globalstatus = true,
disabled_filetypes = { statusline = { "dashboard", "alpha", "starter" } },
},
sections = {
lualine_a = { "mode" },
lualine_b = { "branch" },
lualine_c = {
{
"diagnostics",
symbols = {
error = " ",
warn = " ",
info = " ",
hint = " ",
},
},
{ "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } },
{ "filename", path = 1, symbols = { modified = " ", readonly = "", unnamed = "" } },
},
lualine_x = {
{
"diff",
symbols = {
added = " ",
modified = " ",
removed = " ",
},
},
},
lualine_y = {
{ "progress", separator = " ", padding = { left = 1, right = 0 } },
{ "location", padding = { left = 0, right = 1 } },
},
lualine_z = {
function()
return " " .. os.date("%R")
end,
},
},
extensions = { "lazy" },
}
end,
},
-- Bufferline
{
"akinsho/bufferline.nvim",
event = "VeryLazy",
keys = {
{ "<leader>bp", "<Cmd>BufferLineTogglePin<CR>", desc = "Toggle Pin" },
{ "<leader>bP", "<Cmd>BufferLineGroupClose ungrouped<CR>", desc = "Delete Non-Pinned Buffers" },
{ "<leader>bo", "<Cmd>BufferLineCloseOthers<CR>", desc = "Delete Other Buffers" },
{ "<leader>br", "<Cmd>BufferLineCloseRight<CR>", desc = "Delete Buffers to the Right" },
{ "<leader>bl", "<Cmd>BufferLineCloseLeft<CR>", desc = "Delete Buffers to the Left" },
{ "<S-h>", "<cmd>BufferLineCyclePrev<cr>", desc = "Prev Buffer" },
{ "<S-l>", "<cmd>BufferLineCycleNext<cr>", desc = "Next Buffer" },
{ "[b", "<cmd>BufferLineCyclePrev<cr>", desc = "Prev Buffer" },
{ "]b", "<cmd>BufferLineCycleNext<cr>", desc = "Next Buffer" },
},
opts = {
options = {
close_command = function(n) require("mini.bufremove").delete(n, false) end,
right_mouse_command = function(n) require("mini.bufremove").delete(n, false) end,
diagnostics = "nvim_lsp",
always_show_bufferline = false,
offsets = {
{
filetype = "neo-tree",
text = "Neo-tree",
highlight = "Directory",
text_align = "left",
},
},
},
},
dependencies = {
{
"nvim-mini/mini.bufremove",
keys = {
{ "<leader>bd", function() require("mini.bufremove").delete(0, false) end, desc = "Delete Buffer" },
{ "<leader>bD", function() require("mini.bufremove").delete(0, true) end, desc = "Delete Buffer (Force)" },
},
opts = {},
},
},
},
-- Indent guides
{
"lukas-reineke/indent-blankline.nvim",
event = { "BufReadPost", "BufNewFile" },
opts = {
indent = {
char = "",
tab_char = "",
},
scope = { show_start = false, show_end = false },
exclude = {
filetypes = {
"help",
"alpha",
"dashboard",
"neo-tree",
"Trouble",
"trouble",
"lazy",
"mason",
"notify",
"toggleterm",
"lazyterm",
},
},
},
main = "ibl",
},
-- Icons
{
"nvim-tree/nvim-web-devicons",
lazy = true,
},
}

View file

@ -0,0 +1,79 @@
return {
-- Disable which-key (we use mini.clue instead)
{ "folke/which-key.nvim", enabled = false },
-- mini.clue - Lightweight keybind hints
{
"nvim-mini/mini.clue",
event = "VeryLazy",
config = function()
local miniclue = require("mini.clue")
miniclue.setup({
triggers = {
-- Leader triggers
{ mode = "n", keys = "<Leader>" },
{ mode = "x", keys = "<Leader>" },
-- Built-in completion
{ mode = "i", keys = "<C-x>" },
-- `g` key
{ mode = "n", keys = "g" },
{ mode = "x", keys = "g" },
-- Marks
{ mode = "n", keys = "'" },
{ mode = "n", keys = "`" },
{ mode = "x", keys = "'" },
{ mode = "x", keys = "`" },
-- Registers
{ mode = "n", keys = '"' },
{ mode = "x", keys = '"' },
{ mode = "i", keys = "<C-r>" },
{ mode = "c", keys = "<C-r>" },
-- Window commands
{ mode = "n", keys = "<C-w>" },
-- `z` key
{ mode = "n", keys = "z" },
{ mode = "x", keys = "z" },
-- Bracketed commands
{ mode = "n", keys = "[" },
{ mode = "n", keys = "]" },
},
clues = {
-- Enhance this by adding descriptions for <Leader> mapping groups
miniclue.gen_clues.builtin_completion(),
miniclue.gen_clues.g(),
miniclue.gen_clues.marks(),
miniclue.gen_clues.registers(),
miniclue.gen_clues.windows(),
miniclue.gen_clues.z(),
-- Custom leader key descriptions
{ mode = "n", keys = "<Leader>b", desc = "+buffer" },
{ mode = "n", keys = "<Leader>c", desc = "+code" },
{ mode = "n", keys = "<Leader>d", desc = "+diagnostics" },
{ mode = "n", keys = "<Leader>e", desc = "+explorer" },
{ mode = "n", keys = "<Leader>f", desc = "+find" },
{ mode = "n", keys = "<Leader>o", desc = "+open" },
{ mode = "n", keys = "<Leader>r", desc = "+rename" },
{ mode = "n", keys = "<Leader>s", desc = "+search" },
{ mode = "n", keys = "<Leader>t", desc = "+toggle" },
},
window = {
delay = 300, -- Show hints after 300ms
config = {
width = "auto",
border = "rounded",
},
},
})
end,
},
}