Merge pull request #10 from harivansh-afk/phase-3

phase 3
This commit is contained in:
Hari 2026-03-31 00:25:22 -04:00 committed by GitHub
commit 03302eaf08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 4940 additions and 860 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@ result
.bw-session .bw-session
config/karabiner/assets/ config/karabiner/assets/
config/karabiner/automatic_backups/ config/karabiner/automatic_backups/
tmp/

View file

@ -1,34 +1,25 @@
local lsp = require('config.lsp') pcall(vim.cmd.packadd, "blink.cmp")
vim.lsp.config('*', { local lsp = require "config.lsp"
capabilities = lsp.capabilities(),
vim.lsp.config("*", {
on_attach = lsp.on_attach,
capabilities = lsp.capabilities(),
}) })
vim.api.nvim_create_autocmd('LspAttach', { for _, server in ipairs {
group = vim.api.nvim_create_augroup('UserLspConfig', {}), "lua_ls",
callback = function(ev) "pyright",
local client = vim.lsp.get_client_by_id(ev.data.client_id) "ts_ls",
if client then "rust_analyzer",
lsp.on_attach(client, ev.buf) "gopls",
end "clangd",
end, "bashls",
}) "jsonls",
"html",
for _, server in ipairs({ "cssls",
'lua_ls', } do
'pyright', local ok, config = pcall(require, "lsp." .. server)
'ts_ls', if ok and config then vim.lsp.config(server, config) end
'rust_analyzer', vim.lsp.enable(server)
'gopls',
'clangd',
'bashls',
'jsonls',
'html',
'cssls',
}) do
local ok, config = pcall(require, 'lsp.' .. server)
if ok then
vim.lsp.config(server, config)
end
vim.lsp.enable(server)
end end

View file

@ -1,68 +1,56 @@
vim.g.mapleader = ' ' vim.g.mapleader = " "
vim.g.maplocalleader = ',' vim.g.maplocalleader = ","
local home = os.getenv('HOME') or '' local home = os.getenv "HOME" or ""
local local_bin = home .. '/.local/bin' local local_bin = home .. "/.local/bin"
if not (os.getenv('PATH') or ''):find(local_bin, 1, true) then if not (os.getenv "PATH" or ""):find(local_bin, 1, true) then
local new_path = local_bin .. ':' .. (os.getenv('PATH') or '') local new_path = local_bin .. ":" .. (os.getenv "PATH" or "")
vim.env.PATH = new_path vim.env.PATH = new_path
vim.uv.os_setenv('PATH', new_path) vim.uv.os_setenv("PATH", new_path)
end end
function _G.map(mode, lhs, rhs, opts) function _G.map(mode, lhs, rhs, opts)
vim.keymap.set(mode, lhs, rhs, vim.tbl_extend('keep', opts or {}, { silent = true })) vim.keymap.set(mode, lhs, rhs, vim.tbl_extend("keep", opts or {}, { silent = true }))
end end
function _G.bmap(mode, lhs, rhs, opts) function _G.bmap(mode, lhs, rhs, opts) _G.map(mode, lhs, rhs, vim.tbl_extend("force", opts or {}, { buffer = 0 })) end
_G.map(mode, lhs, rhs, vim.tbl_extend('force', opts or {}, { buffer = 0 }))
end
local disabled_plugins = { local disabled_plugins = {
'2html_plugin', "2html_plugin",
'bugreport', "bugreport",
'getscript', "getscript",
'getscriptPlugin', "getscriptPlugin",
'gzip', "gzip",
'logipat', "logipat",
'matchit', "matchit",
'netrw', "netrw",
'netrwFileHandlers', "netrwFileHandlers",
'netrwPlugin', "netrwPlugin",
'netrwSettings', "netrwSettings",
'optwin', "optwin",
'rplugin', "rplugin",
'rrhelper', "rrhelper",
'synmenu', "synmenu",
'tar', "tar",
'tarPlugin', "tarPlugin",
'tohtml', "tohtml",
'tutor', "tutor",
'vimball', "vimball",
'vimballPlugin', "vimballPlugin",
'zip', "zip",
'zipPlugin', "zipPlugin",
} }
for _, plugin in ipairs(disabled_plugins) do for _, plugin in ipairs(disabled_plugins) do
vim.g['loaded_' .. plugin] = 1 vim.g["loaded_" .. plugin] = 1
end end
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' vim.g.lz_n = {
local lazylock = vim.fn.stdpath('state') .. '/lazy-lock.json' load = function(name) vim.cmd.packadd(name:match "[^/]+$" or name) end,
if not vim.uv.fs_stat(lazypath) then }
vim.fn.system({
'git',
'clone',
'--filter=blob:none',
'--branch=stable',
'https://github.com/folke/lazy.nvim.git',
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
require('lazy').setup('plugins', { vim.pack.add {
defaults = { lazy = false }, "https://github.com/lumen-oss/lz.n",
change_detection = { enabled = false }, }
lockfile = lazylock,
}) require("lz.n").load "plugins"

View file

@ -1,28 +1,45 @@
local M = {} local M = {}
local cached_capabilities
local function load_blink()
pcall(vim.cmd.packadd, "blink.cmp")
local ok_blink, blink = pcall(require, "blink.cmp")
if ok_blink then return blink end
local ok_lz, lz = pcall(require, "lz.n")
if ok_lz then
pcall(lz.trigger_load, "saghen/blink.cmp")
ok_blink, blink = pcall(require, "blink.cmp")
if ok_blink then return blink end
end
end
function M.on_attach(_, bufnr) function M.on_attach(_, bufnr)
local function buf(mode, lhs, rhs) local function buf(mode, lhs, rhs) bmap(mode, lhs, rhs, { buffer = bufnr }) end
bmap(mode, lhs, rhs, { buffer = bufnr })
end
buf('n', 'gd', vim.lsp.buf.definition) buf("n", "gd", vim.lsp.buf.definition)
buf('n', 'gD', vim.lsp.buf.declaration) buf("n", "gD", vim.lsp.buf.declaration)
buf('n', '<C-]>', vim.lsp.buf.definition) buf("n", "<C-]>", vim.lsp.buf.definition)
buf('n', 'gi', vim.lsp.buf.implementation) buf("n", "gi", vim.lsp.buf.implementation)
buf('n', 'gr', vim.lsp.buf.references) buf("n", "gr", vim.lsp.buf.references)
buf('n', 'K', vim.lsp.buf.hover) buf("n", "K", vim.lsp.buf.hover)
buf('n', '<leader>rn', vim.lsp.buf.rename) buf("n", "<leader>rn", vim.lsp.buf.rename)
buf({ 'n', 'v' }, '<leader>ca', vim.lsp.buf.code_action) buf({ "n", "v" }, "<leader>ca", vim.lsp.buf.code_action)
buf('n', '<leader>f', function() vim.lsp.buf.format({ async = true }) end) buf("n", "<leader>f", function() vim.lsp.buf.format { async = true } end)
end end
function M.capabilities() function M.capabilities()
local caps = vim.lsp.protocol.make_client_capabilities() if cached_capabilities then return vim.deepcopy(cached_capabilities) end
local ok, blink = pcall(require, 'blink.cmp')
if ok then local capabilities = vim.lsp.protocol.make_client_capabilities()
caps = blink.get_lsp_capabilities(caps) local blink = load_blink()
end if blink and blink.get_lsp_capabilities then
return caps capabilities = vim.tbl_deep_extend("force", capabilities, blink.get_lsp_capabilities({}, false))
end
cached_capabilities = capabilities
return vim.deepcopy(cached_capabilities)
end end
return M return M

View file

@ -1,48 +1,54 @@
vim.pack.add({
"https://github.com/saghen/blink.cmp",
}, { load = function() end })
return { return {
'saghen/blink.cmp', "saghen/blink.cmp",
version = '*', event = { "InsertEnter", "LspAttach" },
event = { 'InsertEnter', 'LspAttach' }, keys = { { "<c-n>", mode = "i" } },
opts = { after = function()
keymap = { require("blink.cmp").setup {
['<Tab>'] = { 'select_and_accept', 'snippet_forward', 'fallback' }, keymap = {
['<S-Tab>'] = { 'snippet_backward', 'fallback' }, ["<Tab>"] = { "select_and_accept", "snippet_forward", "fallback" },
['<c-p>'] = { 'select_prev', 'fallback' }, ["<S-Tab>"] = { "snippet_backward", "fallback" },
['<c-n>'] = { 'show', 'select_next', 'fallback' }, ["<c-p>"] = { "select_prev", "fallback" },
['<c-y>'] = { 'select_and_accept', 'fallback' }, ["<c-n>"] = { "show", "select_next", "fallback" },
['<c-e>'] = { 'cancel', 'fallback' }, ["<c-y>"] = { "select_and_accept", "fallback" },
['<c-u>'] = { 'scroll_documentation_up', 'fallback' }, ["<c-e>"] = { "cancel", "fallback" },
['<c-d>'] = { 'scroll_documentation_down', 'fallback' }, ["<c-u>"] = { "scroll_documentation_up", "fallback" },
["<c-d>"] = { "scroll_documentation_down", "fallback" },
},
cmdline = { enabled = false },
completion = {
accept = {
auto_brackets = { enabled = true },
}, },
cmdline = { enabled = false }, documentation = {
completion = { auto_show = true,
accept = { window = {
auto_brackets = { enabled = true }, border = "single",
}, scrollbar = false,
documentation = { winhighlight = "Normal:BlinkCmpDoc,FloatBorder:BlinkCmpDocBorder",
auto_show = true, },
window = {
border = 'single',
scrollbar = false,
winhighlight = 'Normal:BlinkCmpDoc,FloatBorder:BlinkCmpDocBorder',
},
},
menu = {
auto_show = true,
border = 'single',
scrollbar = false,
winhighlight = 'Normal:BlinkCmpMenu,FloatBorder:BlinkCmpMenuBorder,CursorLine:BlinkCmpMenuSelection',
draw = {
treesitter = { 'lsp' },
columns = {
{ 'kind_icon', gap = 1 },
{ 'label', 'label_description', gap = 1 },
},
},
},
ghost_text = { enabled = true },
}, },
sources = { menu = {
default = { 'lsp', 'path', 'buffer', 'snippets' }, auto_show = true,
border = "single",
scrollbar = false,
winhighlight = "Normal:BlinkCmpMenu,FloatBorder:BlinkCmpMenuBorder,CursorLine:BlinkCmpMenuSelection",
draw = {
treesitter = { "lsp" },
columns = {
{ "kind_icon", gap = 1 },
{ "label", "label_description", gap = 1 },
},
},
}, },
}, ghost_text = { enabled = true },
},
sources = {
default = { "lsp", "path", "buffer", "snippets" },
},
}
end,
} }

View file

@ -1,171 +1,108 @@
vim.pack.add({
"https://github.com/windwp/nvim-autopairs",
"https://github.com/folke/flash.nvim",
"https://github.com/kylechui/nvim-surround",
"https://github.com/kevinhwang91/nvim-ufo",
"https://github.com/kevinhwang91/promise-async",
"https://github.com/barrettruth/pending.nvim",
"https://github.com/barrettruth/preview.nvim",
}, { load = function() end })
return { return {
{ {
'windwp/nvim-autopairs', "windwp/nvim-autopairs",
config = true, event = "InsertEnter",
}, after = function() require("nvim-autopairs").setup() end,
{ },
'folke/flash.nvim', {
opts = { "folke/flash.nvim",
modes = { search = { enabled = true } }, after = function()
}, require("flash").setup {
config = function(_, opts) modes = { search = { enabled = true } },
require('flash').setup(opts) }
map({ 'n', 'x', 'o' }, 's', function() require('flash').jump() end) end,
map({ 'n', 'x', 'o' }, 'S', function() require('flash').treesitter() end) keys = {
map('o', 'r', function() require('flash').remote() end) {
map({ 'o', 'x' }, 'R', function() require('flash').treesitter_search() end) "s",
map('c', '<c-s>', function() require('flash').toggle() end) function() require("flash").jump() end,
end, mode = { "n", "x", "o" },
},
{
'kylechui/nvim-surround',
config = true,
},
{
'kevinhwang91/nvim-ufo',
dependencies = { 'kevinhwang91/promise-async' },
opts = {
provider_selector = function()
return { 'treesitter', 'indent' }
end,
},
config = function(_, opts)
require('ufo').setup(opts)
map('n', 'zR', require('ufo').openAllFolds)
map('n', 'zM', require('ufo').closeAllFolds)
end,
},
{
enabled = false,
'barrettruth/cp.nvim',
dependencies = { 'ibhagwan/fzf-lua' },
init = function()
-- Keep uv cache in-project so cp.nvim scraping works in restricted environments.
if vim.env.UV_CACHE_DIR == nil or vim.env.UV_CACHE_DIR == '' then
local uv_cache_dir = vim.fn.getcwd() .. '/.uv-cache'
vim.fn.mkdir(uv_cache_dir, 'p')
vim.env.UV_CACHE_DIR = uv_cache_dir
end
vim.g.cp = {
languages = {
python = {
extension = 'py',
commands = {
run = { 'python3', '{source}' },
debug = { 'python3', '{source}' },
},
},
},
platforms = {
codeforces = {
enabled_languages = { 'python' },
default_language = 'python',
},
atcoder = {
enabled_languages = { 'python' },
default_language = 'python',
},
cses = {
enabled_languages = { 'python' },
default_language = 'python',
},
},
open_url = true,
ui = {
picker = 'fzf-lua',
},
}
end,
config = function()
local function open_url(url)
local ok_ui_open, opened = pcall(vim.ui.open, url)
if ok_ui_open and opened ~= false then
return
end
local opener = nil
if vim.fn.has('macunix') == 1 then
opener = 'open'
elseif vim.fn.has('unix') == 1 then
opener = 'xdg-open'
end
if opener then
vim.fn.jobstart({ opener, url }, { detach = true })
end
end
local function open_current_cp_problem_url()
local ok_state, state = pcall(require, 'cp.state')
local ok_cache, cache = pcall(require, 'cp.cache')
if not (ok_state and ok_cache) then
return
end
local platform = state.get_platform()
local contest_id = state.get_contest_id()
local problem_id = state.get_problem_id()
if not (platform and contest_id and problem_id) then
return
end
cache.load()
local contest = cache.get_contest_data(platform, contest_id)
if contest and contest.url then
open_url(contest.url:format(problem_id))
end
end
-- cp.nvim only opens URLs when first entering a contest; extend this locally for next/prev.
local ok_setup, setup = pcall(require, 'cp.setup')
local ok_config, cp_config = pcall(require, 'cp.config')
if ok_setup and ok_config and not setup._url_open_patch_applied then
local original_navigate_problem = setup.navigate_problem
setup.navigate_problem = function(direction, language)
local ok_state, state = pcall(require, 'cp.state')
local old_problem_id = ok_state and state.get_problem_id() or nil
original_navigate_problem(direction, language)
local cfg = cp_config.get_config()
local new_problem_id = ok_state and state.get_problem_id() or nil
local moved = old_problem_id ~= nil and new_problem_id ~= nil and old_problem_id ~= new_problem_id
if cfg and cfg.open_url and moved then
vim.schedule(open_current_cp_problem_url)
end
end
setup._url_open_patch_applied = true
end
map('n', '<leader>cr', '<cmd>CP run<cr>', { desc = 'CP run' })
map('n', '<leader>cp', '<cmd>CP panel<cr>', { desc = 'CP panel' })
map('n', '<leader>ce', '<cmd>CP edit<cr>', { desc = 'CP edit tests' })
map('n', '<leader>cn', '<cmd>CP next<cr>', { desc = 'CP next problem' })
map('n', '<leader>cN', '<cmd>CP prev<cr>', { desc = 'CP previous problem' })
map('n', '<leader>cc', '<cmd>CP pick<cr>', { desc = 'CP contest picker' })
map('n', '<leader>ci', '<cmd>CP interact<cr>', { desc = 'CP interact' })
map('n', '<leader>co', open_current_cp_problem_url, { desc = 'CP open problem url' })
end,
},
{
'barrettruth/pending.nvim',
init = function()
map('n', '<leader>p', '<cmd>Pending<cr><cmd>only<cr>')
end,
},
{
'barrettruth/preview.nvim',
init = function()
vim.g.preview = {
typst = true,
latex = true,
github = {
output = function(ctx)
return '/tmp/' .. vim.fn.fnamemodify(ctx.file, ':t:r') .. '.html'
end,
}, },
mermaid = true, {
} "S",
end, function() require("flash").treesitter() end,
}, mode = { "n", "x", "o" },
},
{
"r",
function() require("flash").remote() end,
mode = "o",
},
{
"R",
function() require("flash").treesitter_search() end,
mode = { "o", "x" },
},
{
"<c-s>",
function() require("flash").toggle() end,
mode = "c",
},
},
},
{
"kylechui/nvim-surround",
after = function() require("nvim-surround").setup() end,
keys = {
{ "cs", mode = "n" },
{ "ds", mode = "n" },
{ "ys", mode = "n" },
{ "yS", mode = "n" },
{ "yss", mode = "n" },
{ "ySs", mode = "n" },
},
},
{
"kevinhwang91/nvim-ufo",
event = "BufReadPost",
before = function() vim.cmd.packadd "promise-async" end,
after = function()
require("ufo").setup {
provider_selector = function() return { "treesitter", "indent" } end,
}
end,
keys = {
{
"zR",
function() require("ufo").openAllFolds() end,
mode = "n",
},
{
"zM",
function() require("ufo").closeAllFolds() end,
mode = "n",
},
},
},
{
"barrettruth/pending.nvim",
cmd = "Pending",
keys = {
{ "<leader>p", "<cmd>Pending<cr><cmd>only<cr>" },
},
},
{
"barrettruth/preview.nvim",
cmd = "Preview",
ft = { "markdown", "tex", "typst" },
before = function()
vim.g.preview = {
typst = true,
latex = true,
github = {
output = function(ctx) return "/tmp/" .. vim.fn.fnamemodify(ctx.file, ":t:r") .. ".html" end,
},
mermaid = true,
}
end,
},
} }

View file

@ -1,3 +1,7 @@
vim.pack.add({
"https://github.com/ibhagwan/fzf-lua",
}, { load = function() end })
---@param kind 'issue'|'pr' ---@param kind 'issue'|'pr'
---@param state 'all'|'open'|'closed' ---@param state 'all'|'open'|'closed'
local function gh_picker(kind, state) local function gh_picker(kind, state)
@ -5,8 +9,10 @@ local function gh_picker(kind, state)
vim.notify("gh CLI not found", vim.log.levels.WARN) vim.notify("gh CLI not found", vim.log.levels.WARN)
return return
end end
local next_state = ({ all = "open", open = "closed", closed = "all" })[state] local next_state = ({ all = "open", open = "closed", closed = "all" })[state]
local label = kind == "pr" and "PRs" or "Issues" local label = kind == "pr" and "PRs" or "Issues"
require("fzf-lua").fzf_exec(("gh %s list --limit 100 --state %s"):format(kind, state), { require("fzf-lua").fzf_exec(("gh %s list --limit 100 --state %s"):format(kind, state), {
prompt = ("%s (%s)> "):format(label, state), prompt = ("%s (%s)> "):format(label, state),
header = ":: <c-o> to toggle all/open/closed", header = ":: <c-o> to toggle all/open/closed",
@ -22,9 +28,27 @@ end
return { return {
"ibhagwan/fzf-lua", "ibhagwan/fzf-lua",
dependencies = { "nvim-tree/nvim-web-devicons" }, cmd = "FzfLua",
config = function(_, opts) before = function()
pcall(vim.cmd.packadd, "nvim-web-devicons")
pcall(vim.cmd.packadd, "nonicons.nvim")
end,
after = function()
local fzf = require "fzf-lua" local fzf = require "fzf-lua"
local opts = {
"default-title",
winopts = {
border = "single",
preview = {
layout = "vertical",
vertical = "down:50%",
},
},
fzf_opts = {
["--layout"] = "reverse",
},
}
fzf.setup(opts) fzf.setup(opts)
local ok, fzf_reload = pcall(require, "config.fzf_reload") local ok, fzf_reload = pcall(require, "config.fzf_reload")
@ -32,45 +56,43 @@ return {
fzf_reload.setup(opts) fzf_reload.setup(opts)
fzf_reload.reload() fzf_reload.reload()
end end
map("n", "<C-f>", function()
local git_dir = vim.fn.system("git rev-parse --git-dir 2>/dev/null"):gsub("\n", "")
if vim.v.shell_error == 0 and git_dir ~= "" then
fzf.git_files()
else
fzf.files()
end
end)
map("n", "<leader>ff", "<cmd>FzfLua files<cr>")
map("n", "<leader>fg", "<cmd>FzfLua live_grep<cr>")
map("n", "<leader>fb", "<cmd>FzfLua buffers<cr>")
map("n", "<leader>fh", "<cmd>FzfLua help_tags<cr>")
map("n", "<leader>fr", "<cmd>FzfLua resume<cr>")
map("n", "<leader>fo", "<cmd>FzfLua oldfiles<cr>")
map("n", "<leader>fc", "<cmd>FzfLua commands<cr>")
map("n", "<leader>fk", "<cmd>FzfLua keymaps<cr>")
map("n", "<leader>f/", "<cmd>FzfLua search_history<cr>")
map("n", "<leader>f:", "<cmd>FzfLua command_history<cr>")
map("n", "<leader>fe", "<cmd>FzfLua files cwd=~/.config<cr>")
map("n", "gq", "<cmd>FzfLua quickfix<cr>")
map("n", "gl", "<cmd>FzfLua loclist<cr>")
map("n", "<leader>GB", "<cmd>FzfLua git_branches<cr>")
map("n", "<leader>Gc", "<cmd>FzfLua git_commits<cr>")
map("n", "<leader>Gs", "<cmd>FzfLua git_status<cr>")
map("n", "<leader>Gp", function() gh_picker("pr", "open") end)
map("n", "<leader>Gi", function() gh_picker("issue", "open") end)
end, end,
opts = { keys = {
"default-title", {
winopts = { "<C-f>",
border = "single", function()
preview = { local fzf = require "fzf-lua"
layout = "vertical", local git_dir = vim.fn.system("git rev-parse --git-dir 2>/dev/null"):gsub("\n", "")
vertical = "down:50%", if vim.v.shell_error == 0 and git_dir ~= "" then
}, fzf.git_files()
else
fzf.files()
end
end,
}, },
fzf_opts = { { "<leader>ff", "<cmd>FzfLua files<cr>" },
["--layout"] = "reverse", { "<leader>fg", "<cmd>FzfLua live_grep<cr>" },
{ "<leader>fb", "<cmd>FzfLua buffers<cr>" },
{ "<leader>fh", "<cmd>FzfLua help_tags<cr>" },
{ "<leader>fr", "<cmd>FzfLua resume<cr>" },
{ "<leader>fo", "<cmd>FzfLua oldfiles<cr>" },
{ "<leader>fc", "<cmd>FzfLua commands<cr>" },
{ "<leader>fk", "<cmd>FzfLua keymaps<cr>" },
{ "<leader>f/", "<cmd>FzfLua search_history<cr>" },
{ "<leader>f:", "<cmd>FzfLua command_history<cr>" },
{ "<leader>fe", "<cmd>FzfLua files cwd=~/.config<cr>" },
{ "gq", "<cmd>FzfLua quickfix<cr>" },
{ "gl", "<cmd>FzfLua loclist<cr>" },
{ "<leader>GB", "<cmd>FzfLua git_branches<cr>" },
{ "<leader>Gc", "<cmd>FzfLua git_commits<cr>" },
{ "<leader>Gs", "<cmd>FzfLua git_status<cr>" },
{
"<leader>Gp",
function() gh_picker("pr", "open") end,
},
{
"<leader>Gi",
function() gh_picker("issue", "open") end,
}, },
}, },
} }

View file

@ -1,102 +1,68 @@
local function file_loc() vim.pack.add({
local root = vim.trim(vim.fn.system('git rev-parse --show-toplevel')) "https://github.com/lewis6991/gitsigns.nvim",
if vim.v.shell_error ~= 0 or root == '' then "https://github.com/barrettruth/forge.nvim",
return nil "https://github.com/barrettruth/diffs.nvim",
end }, { load = function() end })
local path = vim.api.nvim_buf_get_name(0)
if path == '' or path:sub(1, #root + 1) ~= root .. '/' then
return nil
end
return ('%s:%d'):format(path:sub(#root + 2), vim.fn.line('.'))
end
local function gh_browse()
if vim.fn.executable('gh') ~= 1 then
vim.notify('gh CLI not found', vim.log.levels.WARN)
return
end
local loc = file_loc()
if loc then
vim.system({ 'gh', 'browse', loc })
else
vim.system({ 'gh', 'browse' })
end
end
return { return {
{ {
'tpope/vim-fugitive', "lewis6991/gitsigns.nvim",
config = function() event = "BufReadPre",
map('n', '<leader>gg', '<cmd>Git<cr><cmd>only<cr>') after = function()
map('n', '<leader>gc', '<cmd>Git commit<cr>') require("gitsigns").setup {
map('n', '<leader>gp', '<cmd>Git push<cr>') signs = {
map('n', '<leader>gl', '<cmd>Git pull<cr>') add = { text = "██" },
map('n', '<leader>gb', '<cmd>Git blame<cr>') change = { text = "██" },
map('n', '<leader>gd', '<cmd>Gvdiffsplit<cr>') delete = { text = "▄▄" },
map('n', '<leader>gr', '<cmd>Gread<cr>') topdelete = { text = "▀▀" },
map('n', '<leader>gw', '<cmd>Gwrite<cr>') changedelete = { text = "██" },
map({ 'n', 'v' }, '<leader>go', gh_browse)
end,
},
{
'lewis6991/gitsigns.nvim',
opts = {
signs = {
add = { text = '██' },
change = { text = '██' },
delete = { text = '▄▄' },
topdelete = { text = '▀▀' },
changedelete = { text = '██' },
},
signs_staged = {
add = { text = '▓▓' },
change = { text = '▓▓' },
delete = { text = '▄▄' },
topdelete = { text = '▀▀' },
changedelete = { text = '▓▓' },
},
signs_staged_enable = true,
}, },
config = function(_, opts) signs_staged = {
require('gitsigns').setup(opts) add = { text = "▓▓" },
map('n', ']g', '<cmd>Gitsigns next_hunk<cr>') change = { text = "▓▓" },
map('n', '[g', '<cmd>Gitsigns prev_hunk<cr>') delete = { text = "▄▄" },
map('n', '<leader>ghs', '<cmd>Gitsigns stage_hunk<cr>') topdelete = { text = "▀▀" },
map('n', '<leader>ghr', '<cmd>Gitsigns reset_hunk<cr>') changedelete = { text = "▓▓" },
map('n', '<leader>ghp', '<cmd>Gitsigns preview_hunk<cr>') },
map('n', '<leader>gB', '<cmd>Gitsigns toggle_current_line_blame<cr>') signs_staged_enable = true,
end, }
},
{ map("n", "]g", "<cmd>Gitsigns next_hunk<cr>")
'barrettruth/forge.nvim', map("n", "[g", "<cmd>Gitsigns prev_hunk<cr>")
dependencies = { 'ibhagwan/fzf-lua' }, map("n", "<leader>ghs", "<cmd>Gitsigns stage_hunk<cr>")
init = function() map("n", "<leader>ghr", "<cmd>Gitsigns reset_hunk<cr>")
vim.g.forge = vim.g.forge or {} map("n", "<leader>ghp", "<cmd>Gitsigns preview_hunk<cr>")
end, map("n", "<leader>gB", "<cmd>Gitsigns toggle_current_line_blame<cr>")
config = function() end,
map('n', '<c-t>', '<cmd>Forge<cr>', { desc = 'Forge picker' }) },
end, {
}, "barrettruth/forge.nvim",
{ cmd = "Forge",
'barrettruth/diffs.nvim', before = function() vim.g.forge = vim.g.forge or {} end,
enabled = true, after = function() pcall(vim.cmd.packadd, "fzf-lua") end,
init = function() keys = {
vim.g.diffs = { { "<c-t>", "<cmd>Forge<cr>", desc = "Forge picker" },
integrations = {
fugitive = {
enabled = true,
horizontal = false,
vertical = false,
},
},
hide_prefix = true,
highlights = {
warn_max_lines = false,
gutter = true,
blend_alpha = 0.5,
intra = { enabled = true },
},
}
end,
}, },
},
{
"barrettruth/diffs.nvim",
before = function()
vim.g.diffs = {
integrations = {
fugitive = {
enabled = true,
horizontal = false,
vertical = false,
},
},
hide_prefix = true,
highlights = {
warn_max_lines = false,
gutter = true,
blend_alpha = 0.5,
intra = { enabled = true },
},
}
end,
},
} }

View file

@ -1,4 +1,9 @@
vim.pack.add({
"https://github.com/neovim/nvim-lspconfig",
}, { load = function() end })
return { return {
'neovim/nvim-lspconfig', {
lazy = false, "neovim/nvim-lspconfig",
},
} }

View file

@ -1,87 +1,144 @@
return { vim.pack.add({
{ "https://github.com/barrettruth/canola.nvim",
'barrettruth/canola.nvim', "https://github.com/barrettruth/canola-collection",
branch = 'canola', }, { load = function() end })
dependencies = { 'nvim-tree/nvim-web-devicons' },
init = function()
vim.g.canola = {
columns = { 'icon' },
delete = { wipe = false, recursive = true },
hidden = { enabled = false },
keymaps = {
['g?'] = { callback = 'actions.show_help', mode = 'n' },
['<CR>'] = 'actions.select',
['<C-v>'] = { callback = 'actions.select', opts = { vertical = true } },
['<C-x>'] = { callback = 'actions.select', opts = { horizontal = true } },
['<C-p>'] = 'actions.preview',
['<C-c>'] = { callback = 'actions.close', mode = 'n' },
['-'] = { callback = 'actions.parent', mode = 'n' },
['g.'] = { callback = 'actions.toggle_hidden', mode = 'n' },
['<C-t>'] = false,
},
}
map('n', '-', '<cmd>Canola<cr>')
map('n', '<leader>e', '<cmd>Canola<cr>')
local ns = vim.api.nvim_create_namespace('canola_git_trailing') local ns = vim.api.nvim_create_namespace("canola_git_trailing")
local symbols = { local symbols = {
M = { 'M', 'DiagnosticWarn' }, M = { "M", "DiagnosticWarn" },
A = { 'A', 'DiagnosticOk' }, A = { "A", "DiagnosticOk" },
D = { 'D', 'DiagnosticError' }, D = { "D", "DiagnosticError" },
R = { 'R', 'DiagnosticWarn' }, R = { "R", "DiagnosticWarn" },
['?'] = { '?', 'DiagnosticInfo' }, ["?"] = { "?", "DiagnosticInfo" },
['!'] = { '!', 'Comment' }, ["!"] = { "!", "Comment" },
} }
local function apply_git_status(buf) local function apply_git_status(buf)
if not vim.api.nvim_buf_is_valid(buf) then return end if not vim.api.nvim_buf_is_valid(buf) then return end
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1) vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
local ok, canola = pcall(require, 'canola') local ok, canola = pcall(require, "canola")
if not ok then return end if not ok then return end
local dir = canola.get_current_dir(buf) local dir = canola.get_current_dir(buf)
if not dir then return end if not dir then return end
local git_ok, git = pcall(require, 'canola-git') local git_ok, git = pcall(require, "canola-git")
if not git_ok then return end if not git_ok then return end
local dir_cache = git._cache[dir] local dir_cache = git._cache[dir]
if not dir_cache or not dir_cache.status then return end if not dir_cache or not dir_cache.status then return end
local lines = vim.api.nvim_buf_line_count(buf) local lines = vim.api.nvim_buf_line_count(buf)
for lnum = 0, lines - 1 do for lnum = 0, lines - 1 do
local entry = canola.get_entry_on_line(buf, lnum + 1) local entry = canola.get_entry_on_line(buf, lnum + 1)
if entry then if entry then
local status = dir_cache.status[entry.name] local status = dir_cache.status[entry.name]
if status then if status then
local ch = status:sub(1, 1) local ch = status:sub(1, 1)
if ch == ' ' then ch = status:sub(2, 2) end if ch == " " then ch = status:sub(2, 2) end
local sym = symbols[ch] local sym = symbols[ch]
if sym then if sym then
vim.api.nvim_buf_set_extmark(buf, ns, lnum, 0, { vim.api.nvim_buf_set_extmark(buf, ns, lnum, 0, {
virt_text = { { ' ' .. sym[1], sym[2] } }, virt_text = { { " " .. sym[1], sym[2] } },
virt_text_pos = 'eol', virt_text_pos = "eol",
invalidate = true, invalidate = true,
}) })
end end
end end
end end
end end
end end
vim.api.nvim_create_autocmd('User', { return {
pattern = 'CanolaReadPost', {
callback = function(args) "barrettruth/canola.nvim",
local buf = args.buf cmd = "Canola",
apply_git_status(buf) before = function()
vim.defer_fn(function() apply_git_status(buf) end, 500) pcall(vim.cmd.packadd, "nvim-web-devicons")
end, pcall(vim.cmd.packadd, "nonicons.nvim")
})
end, vim.g.canola = {
}, columns = { "icon" },
{ hidden = { enabled = false },
'barrettruth/canola-collection', highlights = { filename = {}, columns = true },
dependencies = { 'barrettruth/canola.nvim' }, save = "auto",
}, extglob = true,
delete = { wipe = false, recursive = true },
float = { border = "single" },
keymaps = {
["g?"] = { callback = "actions.show_help", mode = "n" },
["<CR>"] = "actions.select",
["<C-v>"] = { callback = "actions.select", opts = { vertical = true } },
["<C-x>"] = { callback = "actions.select", opts = { horizontal = true } },
["<C-p>"] = "actions.preview",
["<C-c>"] = { callback = "actions.close", mode = "n" },
["-"] = { callback = "actions.parent", mode = "n" },
["g."] = { callback = "actions.toggle_hidden", mode = "n" },
["<C-t>"] = false,
},
}
end,
after = function()
vim.cmd.packadd("canola-collection")
local augroup = vim.api.nvim_create_augroup("UserCanolaConfig", { clear = true })
local detail_columns = { "git_status", "permissions", "owner", "size", "mtime" }
local base_columns = vim.deepcopy(vim.g.canola.columns or {})
local show_all = false
vim.api.nvim_create_autocmd("FileType", {
group = augroup,
pattern = "canola",
callback = function(args)
local bufnr = args.buf
vim.keymap.set("n", "gC", function()
show_all = not show_all
require("canola").set_columns(show_all and detail_columns or base_columns)
end, {
buffer = bufnr,
desc = "toggle canola columns",
})
vim.keymap.set("n", "gX", function()
local canola = require("canola")
local entry = canola.get_cursor_entry()
local dir = canola.get_current_dir()
if not entry or not dir then return end
vim.ui.input({ prompt = "chmod: ", default = "755" }, function(mode)
if not mode then return end
vim.uv.fs_chmod(dir .. entry.name, tonumber(mode, 8), function(err)
if err then
vim.schedule(function() vim.notify(err, vim.log.levels.ERROR) end)
return
end
vim.schedule(function() require("canola.actions").refresh.callback() end)
end)
end)
end, {
buffer = bufnr,
desc = "chmod entry",
})
end,
})
vim.api.nvim_create_autocmd("User", {
group = augroup,
pattern = "CanolaReadPost",
callback = function(args)
local buf = args.buf
apply_git_status(buf)
vim.defer_fn(function() apply_git_status(buf) end, 500)
end,
})
end,
keys = {
{ "-", "<cmd>Canola<cr>" },
{ "<leader>e", "<cmd>Canola<cr>" },
},
},
} }

View file

@ -1,56 +1,71 @@
vim.pack.add({
"https://github.com/nvim-treesitter/nvim-treesitter",
"https://github.com/nvim-treesitter/nvim-treesitter-textobjects",
}, { load = function() end })
vim.api.nvim_create_autocmd("PackChanged", {
callback = function(ev)
local name, kind = ev.data.spec.name, ev.data.kind
if kind == "delete" then return end
if name == "nvim-treesitter" then vim.schedule(function() vim.cmd "TSUpdate all" end) end
end,
})
return { return {
{ {
'nvim-treesitter/nvim-treesitter', "nvim-treesitter/nvim-treesitter",
build = ':TSUpdate', after = function() require("nvim-treesitter").setup { auto_install = true } end,
dependencies = { 'nvim-treesitter/nvim-treesitter-textobjects' }, },
config = function() {
require('nvim-treesitter-textobjects').setup({ "nvim-treesitter/nvim-treesitter-textobjects",
select = { after = function()
enable = true, require("nvim-treesitter-textobjects").setup {
lookahead = true, select = {
keymaps = { enable = true,
['af'] = '@function.outer', lookahead = true,
['if'] = '@function.inner', keymaps = {
['ac'] = '@class.outer', ["af"] = "@function.outer",
['ic'] = '@class.inner', ["if"] = "@function.inner",
['aa'] = '@parameter.outer', ["ac"] = "@class.outer",
['ia'] = '@parameter.inner', ["ic"] = "@class.inner",
['ai'] = '@conditional.outer', ["aa"] = "@parameter.outer",
['ii'] = '@conditional.inner', ["ia"] = "@parameter.inner",
['al'] = '@loop.outer', ["ai"] = "@conditional.outer",
['il'] = '@loop.inner', ["ii"] = "@conditional.inner",
['ab'] = '@block.outer', ["al"] = "@loop.outer",
['ib'] = '@block.inner', ["il"] = "@loop.inner",
}, ["ab"] = "@block.outer",
}, ["ib"] = "@block.inner",
move = { },
enable = true, },
set_jumps = true, move = {
goto_next_start = { enable = true,
[']f'] = '@function.outer', set_jumps = true,
[']c'] = '@class.outer', goto_next_start = {
[']a'] = '@parameter.inner', ["]f"] = "@function.outer",
}, ["]c"] = "@class.outer",
goto_next_end = { ["]a"] = "@parameter.inner",
[']F'] = '@function.outer', },
[']C'] = '@class.outer', goto_next_end = {
}, ["]F"] = "@function.outer",
goto_previous_start = { ["]C"] = "@class.outer",
['[f'] = '@function.outer', },
['[c'] = '@class.outer', goto_previous_start = {
['[a'] = '@parameter.inner', ["[f"] = "@function.outer",
}, ["[c"] = "@class.outer",
goto_previous_end = { ["[a"] = "@parameter.inner",
['[F'] = '@function.outer', },
['[C'] = '@class.outer', goto_previous_end = {
}, ["[F"] = "@function.outer",
}, ["[C"] = "@class.outer",
swap = { },
enable = true, },
swap_next = { ['<leader>sn'] = '@parameter.inner' }, swap = {
swap_previous = { ['<leader>sp'] = '@parameter.inner' }, enable = true,
}, swap_next = { ["<leader>sn"] = "@parameter.inner" },
}) swap_previous = { ["<leader>sp"] = "@parameter.inner" },
end, },
}, }
end,
},
} }

View file

@ -1,15 +1,29 @@
vim.pack.add({
"https://github.com/harivansh-afk/cozybox.nvim",
"https://github.com/nvim-lualine/lualine.nvim",
"https://github.com/barrettruth/nonicons.nvim",
"https://github.com/nvim-tree/nvim-web-devicons",
}, { load = function() end })
return { return {
{ {
"harivansh-afk/cozybox.nvim", "harivansh-afk/cozybox.nvim",
lazy = false, after = function() require("theme").setup() end,
priority = 1000, },
config = function() require("theme").setup() end, {
"nvim-tree/nvim-web-devicons",
},
{
"barrettruth/nonicons.nvim",
before = function() vim.cmd.packadd "nvim-web-devicons" end,
}, },
{ {
"nvim-lualine/lualine.nvim", "nvim-lualine/lualine.nvim",
dependencies = { "nvim-tree/nvim-web-devicons" }, before = function()
config = function() vim.cmd.packadd "nvim-web-devicons"
local theme_status = function() return require("theme").statusline_label() end pcall(vim.cmd.packadd, "nonicons.nvim")
end,
after = function()
local theme = { local theme = {
normal = { normal = {
a = { gui = "bold" }, a = { gui = "bold" },
@ -24,6 +38,7 @@ return {
a = { gui = "bold" }, a = { gui = "bold" },
}, },
} }
require("lualine").setup { require("lualine").setup {
options = { options = {
icons_enabled = false, icons_enabled = false,
@ -42,8 +57,4 @@ return {
} }
end, end,
}, },
{
"barrettruth/nonicons.nvim",
dependencies = { "nvim-tree/nvim-web-devicons" },
},
} }

View file

@ -0,0 +1,96 @@
{
"plugins": {
"blink.cmp": {
"rev": "451168851e8e2466bc97ee3e026c3dcb9141ce07",
"src": "https://github.com/saghen/blink.cmp"
},
"canola-collection": {
"rev": "888ee61c54873e0c57df07d35e38284e23bb978c",
"src": "https://github.com/barrettruth/canola-collection"
},
"canola.nvim": {
"rev": "4a0dd41ca39793342177b2cdb8e784243da5a936",
"src": "https://github.com/barrettruth/canola.nvim"
},
"cozybox.nvim": {
"rev": "be246810d74e3030cc5790685db3b9b8aacda5e3",
"src": "https://github.com/harivansh-afk/cozybox.nvim"
},
"diffs.nvim": {
"rev": "0cb16a0e2384f1d3dd6330f6ea517de8e07aa8e8",
"src": "https://github.com/barrettruth/diffs.nvim"
},
"flash.nvim": {
"rev": "fcea7ff883235d9024dc41e638f164a450c14ca2",
"src": "https://github.com/folke/flash.nvim"
},
"forge.nvim": {
"rev": "0dc433a32c1dac7bdfc313cf73070f03f27b2ef2",
"src": "https://github.com/barrettruth/forge.nvim"
},
"fzf-lua": {
"rev": "bde73a6886b607246095aa59f396de5e0d036890",
"src": "https://github.com/ibhagwan/fzf-lua"
},
"gitsigns.nvim": {
"rev": "50c205548d8b037b7ff6378fca6d21146f0b6161",
"src": "https://github.com/lewis6991/gitsigns.nvim"
},
"lualine.nvim": {
"rev": "47f91c416daef12db467145e16bed5bbfe00add8",
"src": "https://github.com/nvim-lualine/lualine.nvim"
},
"lz.n": {
"rev": "3a696418821fa8e4963a0a59dd1f8d40fedb6824",
"src": "https://github.com/lumen-oss/lz.n"
},
"nonicons.nvim": {
"rev": "2c5fad40a79d80338b49e6fbd3db9b2c1141a4ed",
"src": "https://github.com/barrettruth/nonicons.nvim"
},
"nvim-autopairs": {
"rev": "59bce2eef357189c3305e25bc6dd2d138c1683f5",
"src": "https://github.com/windwp/nvim-autopairs"
},
"nvim-lspconfig": {
"rev": "16812abf0e8d8175155f26143a8504e8253e92b0",
"src": "https://github.com/neovim/nvim-lspconfig"
},
"nvim-surround": {
"rev": "61319d4bd1c5e336e197defa15bd104c51f0fb29",
"src": "https://github.com/kylechui/nvim-surround"
},
"nvim-treesitter": {
"rev": "cf12346a3414fa1b06af75c79faebe7f76df080a",
"src": "https://github.com/nvim-treesitter/nvim-treesitter"
},
"nvim-treesitter-textobjects": {
"rev": "93d60a475f0b08a8eceb99255863977d3a25f310",
"src": "https://github.com/nvim-treesitter/nvim-treesitter-textobjects"
},
"nvim-ufo": {
"rev": "ab3eb124062422d276fae49e0dd63b3ad1062cfc",
"src": "https://github.com/kevinhwang91/nvim-ufo"
},
"nvim-web-devicons": {
"rev": "d7462543c9e366c0d196c7f67a945eaaf5d99414",
"src": "https://github.com/nvim-tree/nvim-web-devicons"
},
"pending.nvim": {
"rev": "e58cf6665b2d12cc63e1fd7a87169a9d2c20f7b5",
"src": "https://github.com/barrettruth/pending.nvim"
},
"preview.nvim": {
"rev": "ddf9b14c7f0fe00bb47833304625b648a97018a6",
"src": "https://github.com/barrettruth/preview.nvim"
},
"promise-async": {
"rev": "119e8961014c9bfaf1487bf3c2a393d254f337e2",
"src": "https://github.com/kevinhwang91/promise-async"
},
"vim-fugitive": {
"rev": "3b753cf8c6a4dcde6edee8827d464ba9b8c4a6f0",
"src": "https://github.com/tpope/vim-fugitive"
}
}
}

View file

@ -1,31 +1,47 @@
local api = vim.api local api = vim.api
local augroup = api.nvim_create_augroup('UserAutocmds', { clear = true }) local augroup = api.nvim_create_augroup("UserAutocmds", { clear = true })
api.nvim_create_autocmd('TextYankPost', { local function maybe_load_canola(bufnr)
group = augroup, local name = api.nvim_buf_get_name(bufnr)
callback = function() if name == "" or vim.bo[bufnr].filetype == "canola" or vim.fn.isdirectory(name) == 0 then return end
vim.highlight.on_yank({ higroup = 'Visual', timeout = 200 })
end, pcall(vim.cmd.packadd, "canola.nvim")
pcall(vim.cmd, "silent keepalt Canola " .. vim.fn.fnameescape(name))
end
api.nvim_create_autocmd("TextYankPost", {
group = augroup,
callback = function() vim.highlight.on_yank { higroup = "Visual", timeout = 200 } end,
}) })
api.nvim_create_autocmd('BufReadPost', { api.nvim_create_autocmd("BufReadPost", {
group = augroup, group = augroup,
callback = function() callback = function()
if ({ gitcommit = true, gitrebase = true })[vim.bo.filetype] then if ({ gitcommit = true, gitrebase = true })[vim.bo.filetype] then return end
return local mark = api.nvim_buf_get_mark(0, '"')
end if mark[1] > 0 and mark[1] <= api.nvim_buf_line_count(0) then pcall(api.nvim_win_set_cursor, 0, mark) end
local mark = api.nvim_buf_get_mark(0, '"') end,
if mark[1] > 0 and mark[1] <= api.nvim_buf_line_count(0) then
pcall(api.nvim_win_set_cursor, 0, mark)
end
end,
}) })
api.nvim_create_autocmd('VimResized', { api.nvim_create_autocmd("BufEnter", {
group = augroup, group = augroup,
callback = function() nested = true,
local tab = vim.fn.tabpagenr() callback = function(args)
vim.cmd('tabdo wincmd =') if vim.v.vim_did_enter == 1 then maybe_load_canola(args.buf) end
vim.cmd('tabnext ' .. tab) end,
end, })
api.nvim_create_autocmd("VimEnter", {
group = augroup,
nested = true,
callback = function() maybe_load_canola(0) end,
})
api.nvim_create_autocmd("VimResized", {
group = augroup,
callback = function()
local tab = vim.fn.tabpagenr()
vim.cmd "tabdo wincmd ="
vim.cmd("tabnext " .. tab)
end,
}) })

View file

@ -0,0 +1,42 @@
vim.pack.add({
'https://github.com/tpope/vim-fugitive',
})
local function file_loc()
local root = vim.trim(vim.fn.system('git rev-parse --show-toplevel'))
if vim.v.shell_error ~= 0 or root == '' then
return nil
end
local path = vim.api.nvim_buf_get_name(0)
if path == '' or path:sub(1, #root + 1) ~= root .. '/' then
return nil
end
return ('%s:%d'):format(path:sub(#root + 2), vim.fn.line('.'))
end
local function gh_browse()
if vim.fn.executable('gh') ~= 1 then
vim.notify('gh CLI not found', vim.log.levels.WARN)
return
end
local loc = file_loc()
if loc then
vim.system({ 'gh', 'browse', loc })
else
vim.system({ 'gh', 'browse' })
end
end
map('n', '<C-g>', '<cmd>Git<cr><cmd>only<cr>')
map('n', '<leader>gg', '<cmd>Git<cr><cmd>only<cr>')
map('n', '<leader>gc', '<cmd>Git commit<cr>')
map('n', '<leader>gp', '<cmd>Git push<cr>')
map('n', '<leader>gl', '<cmd>Git pull<cr>')
map('n', '<leader>gb', '<cmd>Git blame<cr>')
map('n', '<leader>gd', '<cmd>Gvdiffsplit<cr>')
map('n', '<leader>gr', '<cmd>Gread<cr>')
map('n', '<leader>gw', '<cmd>Gwrite<cr>')
map({ 'n', 'v' }, '<leader>go', gh_browse)

View file

@ -1,21 +1,20 @@
map('n', '<leader>w', '<cmd>w<cr>') map("n", "<leader>w", "<cmd>w<cr>")
map('n', '<leader>q', '<cmd>q<cr>') map("n", "<leader>q", "<cmd>q<cr>")
map('n', '<C-g>', '<cmd>Git<cr><cmd>only<cr>')
map('n', '<Tab>', '<cmd>bnext<cr>') map("n", "<Tab>", "<cmd>bnext<cr>")
map('n', '<S-Tab>', '<cmd>bprev<cr>') map("n", "<S-Tab>", "<cmd>bprev<cr>")
map('n', '<leader>x', '<cmd>bdelete<cr>') map("n", "<leader>x", "<cmd>bdelete<cr>")
map('n', '<leader>b', '<cmd>enew<cr>') map("n", "<leader>b", "<cmd>enew<cr>")
map('n', '<C-h>', '<C-w>h') map("n", "<C-h>", "<C-w>h")
map('n', '<C-j>', '<C-w>j') map("n", "<C-j>", "<C-w>j")
map('n', '<C-k>', '<C-w>k') map("n", "<C-k>", "<C-w>k")
map('n', '<C-l>', '<C-w>l') map("n", "<C-l>", "<C-w>l")
map('n', 'J', 'mzJ`z') map("n", "J", "mzJ`z")
map('x', 'x', '"_x') map("x", "x", '"_x')
map('x', 'p', '"_dP') map("x", "p", '"_dP')
map('n', '<Esc>', '<cmd>nohlsearch<cr>') map("n", "<Esc>", "<cmd>nohlsearch<cr>")
map('n', '<leader>t', '<cmd>setlocal wrap!<cr>') map("n", "<leader>t", "<cmd>setlocal wrap!<cr>")
map('t', '<Esc>', '<C-\\><C-n>') map("t", "<Esc>", "<C-\\><C-n>")

60
flake.lock generated
View file

@ -76,6 +76,27 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"neovim-nightly",
"nixpkgs"
]
},
"locked": {
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
@ -153,6 +174,44 @@
"type": "github" "type": "github"
} }
}, },
"neovim-nightly": {
"inputs": {
"flake-parts": "flake-parts_2",
"neovim-src": "neovim-src",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1774915815,
"narHash": "sha256-LocQzkSjVS4G0AKMBiEIVdBKCNTMZXQFjQMWFId4Jpg=",
"owner": "nix-community",
"repo": "neovim-nightly-overlay",
"rev": "9001416dc5d0ca24c8e4b5a44bfe7cd6fbeb1dd1",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "neovim-nightly-overlay",
"type": "github"
}
},
"neovim-src": {
"flake": false,
"locked": {
"lastModified": 1774915197,
"narHash": "sha256-yor+eo8CVi7wBp7CjAMQnVoK+m197gsl7MvUzaqicns=",
"owner": "neovim",
"repo": "neovim",
"rev": "dbc4800dda2b0dc3290dc79955f857256e0694e2",
"type": "github"
},
"original": {
"owner": "neovim",
"repo": "neovim",
"type": "github"
}
},
"nix-darwin": { "nix-darwin": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -264,6 +323,7 @@
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"googleworkspace-cli": "googleworkspace-cli", "googleworkspace-cli": "googleworkspace-cli",
"home-manager": "home-manager", "home-manager": "home-manager",
"neovim-nightly": "neovim-nightly",
"nix-darwin": "nix-darwin", "nix-darwin": "nix-darwin",
"nix-homebrew": "nix-homebrew", "nix-homebrew": "nix-homebrew",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",

View file

@ -37,6 +37,11 @@
url = "github:nix-community/disko"; url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
neovim-nightly = {
url = "github:nix-community/neovim-nightly-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = outputs =

View file

@ -8,10 +8,14 @@ let
nvimConfig = lib.cleanSourceWith { nvimConfig = lib.cleanSourceWith {
src = ../config/nvim; src = ../config/nvim;
filter = filter =
path: type: builtins.baseNameOf path != ".git" && builtins.baseNameOf path != "lazy-lock.json"; path: type:
let
baseName = builtins.baseNameOf path;
in
baseName != ".git" && baseName != "lazy-lock.json" && baseName != "nvim-pack-lock.json";
}; };
lazyLockSeed = ../config/nvim/lazy-lock.json; packLockSeed = ../config/nvim/nvim-pack-lock.json;
lazyLockPath = "${config.xdg.stateHome}/nvim/lazy-lock.json"; packLockPath = "${config.xdg.stateHome}/nvim/nvim-pack-lock.json";
python = pkgs.writeShellScriptBin "python" '' python = pkgs.writeShellScriptBin "python" ''
exec ${pkgs.python3}/bin/python3 "$@" exec ${pkgs.python3}/bin/python3 "$@"
''; '';
@ -60,13 +64,15 @@ in
recursive = true; recursive = true;
}; };
home.activation.seedNvimLazyLock = lib.hm.dag.entryAfter [ "writeBoundary" ] '' xdg.configFile."nvim/nvim-pack-lock.json".source = config.lib.file.mkOutOfStoreSymlink packLockPath;
home.activation.seedNvimPackLock = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
state_dir="${config.xdg.stateHome}/nvim" state_dir="${config.xdg.stateHome}/nvim"
lockfile="${lazyLockPath}" lockfile="${packLockPath}"
if [ ! -e "$lockfile" ]; then if [ ! -e "$lockfile" ]; then
mkdir -p "$state_dir" mkdir -p "$state_dir"
cp ${lazyLockSeed} "$lockfile" cp ${packLockSeed} "$lockfile"
chmod u+w "$lockfile" chmod u+w "$lockfile"
fi fi
''; '';

View file

@ -32,6 +32,10 @@
setopt localoptions noshwordsplit setopt localoptions noshwordsplit
unset prompt_pure_async_render_requested unset prompt_pure_async_render_requested
prompt_pure_set_colors
_codex_pure_default_arrow=$prompt_pure_colors[git:arrow]
_codex_pure_default_success=$prompt_pure_colors[prompt:success]
typeset -g prompt_pure_git_branch_color=$prompt_pure_colors[git:branch] typeset -g prompt_pure_git_branch_color=$prompt_pure_colors[git:branch]
[[ -n ''${prompt_pure_git_last_dirty_check_timestamp+x} ]] && prompt_pure_git_branch_color=$prompt_pure_colors[git:branch:cached] [[ -n ''${prompt_pure_git_last_dirty_check_timestamp+x} ]] && prompt_pure_git_branch_color=$prompt_pure_colors[git:branch:cached]

View file

@ -9,6 +9,89 @@
}: }:
let let
packageSets = import ../../lib/package-sets.nix { inherit inputs lib pkgs; }; packageSets = import ../../lib/package-sets.nix { inherit inputs lib pkgs; };
sandboxDomain = "netty.harivan.sh";
forgejoDomain = "git.harivan.sh";
forgejoApiUrl = "http://127.0.0.1:3000";
sandboxAgentPackage = pkgs.callPackage ../../pkgs/sandbox-agent { };
sandboxAgentDir = "/home/${username}/.config/sandbox-agent";
sandboxAgentPath =
packageSets.core
++ packageSets.extras
++ [
pkgs.bubblewrap
pkgs.git
pkgs.nodejs
pkgs.pnpm
sandboxAgentPackage
];
sandboxAgentEnvCheck = pkgs.writeShellScript "sandbox-agent-env-check" ''
[ -f "${sandboxAgentDir}/agent.env" ] && [ -f "${sandboxAgentDir}/public.env" ]
'';
sandboxAgentWrapper = pkgs.writeShellScript "sandbox-agent-public" ''
set -euo pipefail
set -a
. "${sandboxAgentDir}/public.env"
. "${sandboxAgentDir}/agent.env"
set +a
exec sandbox-agent server \
--host 127.0.0.1 \
--port "''${SANDBOX_AGENT_PORT}" \
--token "''${SANDBOX_AGENT_TOKEN}"
'';
sandboxCorsProxy = pkgs.writeText "sandbox-agent-cors-proxy.mjs" ''
import http from "node:http";
const listenHost = "127.0.0.1";
const listenPort = 2468;
const targetHost = "127.0.0.1";
const targetPort = 2470;
function setCorsHeaders(headers, req) {
headers["access-control-allow-origin"] = "*";
headers["access-control-allow-methods"] = "GET,POST,PUT,PATCH,DELETE,OPTIONS";
headers["access-control-allow-headers"] =
req.headers["access-control-request-headers"] || "authorization,content-type";
headers["access-control-max-age"] = "86400";
return headers;
}
const server = http.createServer((req, res) => {
if (req.method === "OPTIONS") {
res.writeHead(204, setCorsHeaders({}, req));
res.end();
return;
}
const proxyReq = http.request(
{
host: targetHost,
port: targetPort,
method: req.method,
path: req.url,
headers: {
...req.headers,
host: `''${targetHost}:''${targetPort}`,
},
},
(proxyRes) => {
res.writeHead(
proxyRes.statusCode || 502,
setCorsHeaders({ ...proxyRes.headers }, req),
);
proxyRes.pipe(res);
},
);
proxyReq.on("error", () => {
res.writeHead(502, setCorsHeaders({ "content-type": "text/plain" }, req));
res.end("Upstream request failed");
});
req.pipe(proxyReq);
});
server.listen(listenPort, listenHost);
'';
in in
{ {
imports = [ imports = [
@ -103,6 +186,7 @@ in
pkgs.bubblewrap pkgs.bubblewrap
pkgs.pnpm pkgs.pnpm
pkgs.nodejs pkgs.nodejs
sandboxAgentPackage
]; ];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
@ -122,13 +206,13 @@ in
recommendedTlsSettings = true; recommendedTlsSettings = true;
clientMaxBodySize = "512m"; clientMaxBodySize = "512m";
virtualHosts."sandbox.example.dev" = { virtualHosts.${sandboxDomain} = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
locations."/".proxyPass = "http://127.0.0.1:2470"; locations."/".proxyPass = "http://127.0.0.1:2470";
}; };
virtualHosts."git.example.dev" = { virtualHosts.${forgejoDomain} = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
locations."/".proxyPass = "http://127.0.0.1:3000"; locations."/".proxyPass = "http://127.0.0.1:3000";
@ -150,10 +234,10 @@ in
group = "git"; group = "git";
settings = { settings = {
server = { server = {
DOMAIN = "git.example.dev"; DOMAIN = forgejoDomain;
ROOT_URL = "https://git.example.dev/"; ROOT_URL = "https://${forgejoDomain}/";
HTTP_PORT = 3000; HTTP_PORT = 3000;
SSH_DOMAIN = "git.example.dev"; SSH_DOMAIN = forgejoDomain;
}; };
service.DISABLE_REGISTRATION = true; service.DISABLE_REGISTRATION = true;
session.COOKIE_SECURE = true; session.COOKIE_SECURE = true;
@ -177,53 +261,87 @@ in
pkgs.curl pkgs.curl
pkgs.jq pkgs.jq
pkgs.coreutils pkgs.coreutils
pkgs.gnused
]; ];
script = '' script = ''
set -euo pipefail set -euo pipefail
# Fetch all GitHub repos api_call() {
local response http_code body
response=$(curl -sS -w "\n%{http_code}" "$@")
http_code=$(printf '%s\n' "$response" | tail -n1)
body=$(printf '%s\n' "$response" | sed '$d')
if [ "$http_code" -ge 400 ]; then
printf '[forgejo-mirror-sync] HTTP %s\n' "$http_code" >&2
printf '%s\n' "$body" >&2
return 1
fi
printf '%s' "$body"
}
gh_user=$(api_call -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/user" | jq -r '.login')
repos_file=$(mktemp)
trap 'rm -f "$repos_file"' EXIT
page=1 page=1
repos=""
while true; do while true; do
batch=$(curl -sf -H "Authorization: token $GITHUB_TOKEN" \ batch=$(api_call -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/user/repos?per_page=100&page=$page&affiliation=owner") "https://api.github.com/user/repos?per_page=100&page=$page&visibility=all&affiliation=owner,organization_member")
count=$(echo "$batch" | jq length) count=$(printf '%s' "$batch" | jq length)
[ "$count" -eq 0 ] && break [ "$count" -eq 0 ] && break
repos="$repos$batch" printf '%s' "$batch" | jq -r '.[] | [.full_name, .clone_url] | @tsv' >> "$repos_file"
page=$((page + 1)) page=$((page + 1))
done done
echo "$repos" | jq -r '.[].clone_url' | while read -r clone_url; do sort -u "$repos_file" -o "$repos_file"
repo_name=$(basename "$clone_url" .git)
# Check if mirror already exists in Forgejo while IFS=$'\t' read -r full_name clone_url; do
status=$(curl -sf -o /dev/null -w '%{http_code}' \ repo_owner="''${full_name%%/*}"
repo_name="''${full_name#*/}"
if [ "$repo_owner" = "$gh_user" ]; then
forgejo_repo_name="$repo_name"
else
forgejo_repo_name="$repo_owner--$repo_name"
fi
status=$(curl -sS -o /dev/null -w '%{http_code}' \
-H "Authorization: token $FORGEJO_TOKEN" \ -H "Authorization: token $FORGEJO_TOKEN" \
"$FORGEJO_URL/api/v1/repos/$FORGEJO_OWNER/$repo_name") "${forgejoApiUrl}/api/v1/repos/$FORGEJO_OWNER/$forgejo_repo_name" || true)
if [ "$status" = "404" ]; then if [ "$status" = "404" ]; then
# Create mirror api_call -X POST \
curl -sf -X POST \
-H "Authorization: token $FORGEJO_TOKEN" \ -H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$FORGEJO_URL/api/v1/repos/migrate" \ "${forgejoApiUrl}/api/v1/repos/migrate" \
-d "{ -d "$(jq -n \
\"clone_addr\": \"$clone_url\", --arg addr "$clone_url" \
\"auth_token\": \"$GITHUB_TOKEN\", --arg name "$forgejo_repo_name" \
\"uid\": $(curl -sf -H "Authorization: token $FORGEJO_TOKEN" "$FORGEJO_URL/api/v1/user" | jq .id), --arg owner "$FORGEJO_OWNER" \
\"repo_name\": \"$repo_name\", --arg token "$GITHUB_TOKEN" \
\"mirror\": true, '{
\"service\": \"github\" clone_addr: $addr,
}" repo_name: $name,
echo "Created mirror: $repo_name" repo_owner: $owner,
mirror: true,
auth_token: $token,
service: "github"
}')" \
> /dev/null
echo "Created mirror: $full_name -> $FORGEJO_OWNER/$forgejo_repo_name"
else else
# Trigger sync on existing mirror if ! api_call -X POST \
curl -sf -X POST \
-H "Authorization: token $FORGEJO_TOKEN" \ -H "Authorization: token $FORGEJO_TOKEN" \
"$FORGEJO_URL/api/v1/repos/$FORGEJO_OWNER/$repo_name/mirror-sync" || true "${forgejoApiUrl}/api/v1/repos/$FORGEJO_OWNER/$forgejo_repo_name/mirror-sync" \
echo "Synced mirror: $repo_name" > /dev/null; then
echo "Failed mirror sync: $full_name -> $FORGEJO_OWNER/$forgejo_repo_name" >&2
continue
fi
echo "Synced mirror: $full_name -> $FORGEJO_OWNER/$forgejo_repo_name"
fi fi
done done < "$repos_file"
''; '';
}; };
@ -239,14 +357,17 @@ in
# --- Sandbox Agent (declarative systemd services) --- # --- Sandbox Agent (declarative systemd services) ---
systemd.services.sandbox-agent = { systemd.services.sandbox-agent = {
description = "Sandbox Agent"; description = "Sandbox Agent";
after = [ "network.target" ]; after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
path = sandboxAgentPath;
serviceConfig = { serviceConfig = {
Type = "simple"; Type = "simple";
User = username; User = username;
Group = "users"; Group = "users";
EnvironmentFile = "/home/${username}/.config/sandbox-agent/agent.env"; WorkingDirectory = "/home/${username}";
ExecStart = "/home/${username}/.local/bin/sandbox-agent"; ExecCondition = sandboxAgentEnvCheck;
ExecStart = sandboxAgentWrapper;
Restart = "on-failure"; Restart = "on-failure";
RestartSec = 5; RestartSec = 5;
}; };
@ -255,12 +376,15 @@ in
systemd.services.sandbox-cors-proxy = { systemd.services.sandbox-cors-proxy = {
description = "Sandbox CORS Proxy"; description = "Sandbox CORS Proxy";
after = [ "sandbox-agent.service" ]; after = [ "sandbox-agent.service" ];
requires = [ "sandbox-agent.service" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
Type = "simple"; Type = "simple";
User = username; User = username;
Group = "users"; Group = "users";
ExecStart = "${pkgs.nodejs}/bin/node /home/${username}/.config/sandbox-agent/cors-proxy.js"; WorkingDirectory = "/home/${username}";
ExecCondition = sandboxAgentEnvCheck;
ExecStart = "${pkgs.nodejs}/bin/node ${sandboxCorsProxy}";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = 5; RestartSec = 5;
}; };

17
leverage.md Normal file
View file

@ -0,0 +1,17 @@
# Nix Leveraging
[flake.nix](./flake.nix) is the root lever: one flake, one lockfile, one graph for both macOS and Linux. [modules/hosts/darwin.nix](./modules/hosts/darwin.nix) composes `nix-darwin`, `home-manager`, and `nix-homebrew`; [modules/hosts/netty.nix](./modules/hosts/netty.nix) composes `nixosSystem`, `disko`, and `home-manager`. The point is not “using Nix”; it is collapsing laptop state and VPS state into one reproducible interface.
[modules/nixpkgs.nix](./modules/nixpkgs.nix) and [lib/hosts.nix](./lib/hosts.nix) are the next leverage layer. They define the global `username`, per-host metadata, feature flags, and the `specialArgs` boundary. That removes random `isDarwin` checks from leaf modules and turns host differences into data.
[lib/package-sets.nix](./lib/package-sets.nix), [modules/base.nix](./modules/base.nix), [modules/packages.nix](./modules/packages.nix), and [modules/homebrew.nix](./modules/homebrew.nix) are the package policy. `core`, `extras`, and `fonts` give one place to reason about the machine surface; custom inputs like `googleworkspace-cli`, `claude-code-nix`, `OpenSpec`, `neovim-nightly`, `nix-homebrew`, and `disko` are pinned in [flake.nix](./flake.nix) instead of installed ad hoc.
[home/default.nix](./home/default.nix) and [home/common.nix](./home/common.nix) turn Home Manager into the userland control plane. [home/xdg.nix](./home/xdg.nix) pushes Rust, Go, Node, Python, AWS, Claude, npm, wget, psql, and sqlite into XDG paths; [home/security.nix](./home/security.nix) fixes SSH and GPG permissions on activation; [home/migration.nix](./home/migration.nix) cleans legacy `~/dots` links during the cutover instead of relying on manual cleanup.
[lib/theme.nix](./lib/theme.nix), [home/ghostty.nix](./home/ghostty.nix), [home/tmux.nix](./home/tmux.nix), [home/zsh.nix](./home/zsh.nix), and [home/scripts.nix](./home/scripts.nix) are the ergonomic leverage. One palette renders Ghostty, tmux, fzf, zsh highlights, bat, and delta. The generated `theme` script hot-swaps light/dark across those surfaces. tmux gets session restore, directory-based window names, and a generated session list; zsh gets vi mode, cursor-shape switching, XDG history, prompt theming, and deterministic PATH assembly.
[home/nvim.nix](./home/nvim.nix), [home/codex.nix](./home/codex.nix), [home/claude.nix](./home/claude.nix), and [home/skills.nix](./home/skills.nix) are the agent/editor layer. Neovim is pinned with the nightly overlay and seeded lockfile state; Codex and Claude configs are repo-owned; global skills are installed declaratively via `npx skills add -g` and hash-stamped so the activation only resyncs when the manifest changes.
[scripts/default.nix](./scripts/default.nix), [justfile](./justfile), [scripts/render-bw-shell-secrets.sh](./scripts/render-bw-shell-secrets.sh), and [scripts/restore-bw-files.sh](./scripts/restore-bw-files.sh) are the operational leverage. `writeShellApplication` turns local scripts into managed tools (`ga`, `ghpr`, `gpr`, `ni`, `theme`, `wt`, `wtc`); Bitwarden stays the secret source of truth; `just switch`, `just switch-netty`, and `nixos-anywhere` keep deployment small.
Finally, [hosts/netty/configuration.nix](./hosts/netty/configuration.nix) turns the VPS into a declarative service bundle: static networking, nginx + ACME, Forgejo with GitHub mirror sync, sandbox-agent plus its CORS proxy, bounded GC/journald retention, and a machine that can be rebuilt instead of repaired.

View file

@ -46,6 +46,7 @@ in
); );
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
nixpkgs.overlays = [ inputs.neovim-nightly.overlays.default ];
programs.zsh.enable = true; programs.zsh.enable = true;
environment.shells = [ pkgs.zsh ]; environment.shells = [ pkgs.zsh ];

View file

@ -11,19 +11,16 @@
taps = [ taps = [
"humanlayer/humanlayer" "humanlayer/humanlayer"
"gromgit/fuse"
"mutagen-io/mutagen" "mutagen-io/mutagen"
]; ];
brews = [ brews = [
"gromgit/fuse/sshfs-mac"
"mutagen-io/mutagen/mutagen" "mutagen-io/mutagen/mutagen"
]; ];
casks = [ casks = [
"cap" "cap"
"karabiner-elements" "karabiner-elements"
"macfuse"
"rectangle" "rectangle"
"raycast" "raycast"
"riptide-beta" "riptide-beta"

View file

@ -1,173 +0,0 @@
Nix Config - Architecture and Operations Guide
================================================
1. STATIC IP
----------------------------
DHCP on a VPS is dangerous. If the DHCP lease expires or the server
reboots while the DHCP server is unreachable, the machine loses its IP
and becomes inaccessible via SSH.
Static config in hosts/netty/configuration.nix:
- IP: 152.53.195.59/22
- Gateway: 152.53.192.1
- Interface: ens3
- DNS: 1.1.1.1, 8.8.8.8
Always verify the interface name with `ip link show` before changing
network config. Keep VNC console access available as a fallback.
2. HOST ABSTRACTION (hostConfig)
---------------------------------
lib/hosts.nix defines each machine with:
- isDarwin / isLinux / isNixOS booleans
- features map (rust, go, node, python, aws, claude, docker, tex)
modules/nixpkgs.nix passes hostConfig via specialArgs so all home-manager
modules can use it. This replaces scattered `pkgs.stdenv.isDarwin` checks.
To add a new host:
1. Add entry to lib/hosts.nix with all fields
2. Create hosts/<name>/configuration.nix (NixOS) or add darwin case
3. Add host output in modules/hosts/<name>.nix
4. home/default.nix auto-selects modules based on hostConfig flags
home/default.nix is the unified entry point - no separate per-host home
modules needed.
3. XDG COMPLIANCE
------------------
home/xdg.nix sets environment variables so tools respect XDG dirs:
CARGO_HOME -> $XDG_DATA_HOME/cargo
RUSTUP_HOME -> $XDG_DATA_HOME/rustup
GOPATH -> $XDG_DATA_HOME/go
GOMODCACHE -> $XDG_CACHE_HOME/go/mod
NPM_CONFIG_USERCONFIG -> $XDG_CONFIG_HOME/npm/npmrc
NODE_REPL_HISTORY -> $XDG_STATE_HOME/node_repl_history
PYTHON_HISTORY -> $XDG_STATE_HOME/python_history
AWS_CONFIG_FILE -> $XDG_CONFIG_HOME/aws/config
DOCKER_CONFIG -> $XDG_CONFIG_HOME/docker
CLAUDE_CONFIG_DIR -> $XDG_CONFIG_HOME/claude
PSQL_HISTORY -> $XDG_STATE_HOME/psql_history
SQLITE_HISTORY -> $XDG_STATE_HOME/sqlite_history
LESSHISTFILE -> "-" (disabled)
All gated by hostConfig.features so tools only get configured when
the feature flag is set for that host.
4. SECURITY MODULE
-------------------
home/security.nix runs activation scripts on every `home-manager switch`:
- ~/.ssh/ dir: 700, private keys: 600, pub/known_hosts/config: 644
- ~/.gnupg/ dirs: 700, files: 600
No manual chmod needed after restoring keys from Bitwarden.
5. THEME SYSTEM
----------------
lib/theme.nix is the single source of truth for colors.
Shared palette (gruvbox-inspired) used across:
- Ghostty terminal (renderGhostty)
- Tmux status bar (renderTmux)
- fzf color scheme (renderFzf)
- Zsh syntax highlighting (renderZshHighlights)
- Bat (batTheme)
- Git delta (deltaTheme)
Runtime toggle: `theme toggle` writes "light" or "dark" to
$XDG_STATE_HOME/theme/current, then updates Ghostty, tmux, fzf,
and Neovim (via RPC) live. Bat and delta are static at build time.
6. SHELL SETUP
---------------
Pure prompt with gruvbox-colored git integration. Async git status
(no blocking on large repos). Colors defined in lib/theme.nix via
renderPurePrompt - adapts to light/dark mode at runtime.
Vim mode via defaultKeymap = "viins" with cursor shape switching
(beam for insert, block for normal).
History: 50k entries, dedup, ignoreSpace, extended format, stored at
$XDG_STATE_HOME/zsh_history.
zoxide: declarative via programs.zoxide (no manual eval).
PATH: managed via home.sessionPath in xdg.nix + initContent block
in zsh.nix for entries that need conditional logic.
7. SERVER SERVICES (netty)
---------------------------
All in hosts/netty/configuration.nix:
Nginx reverse proxy with ACME SSL:
- sandbox.example.dev -> 127.0.0.1:2470 (sandbox agent)
- git.example.dev -> 127.0.0.1:3000 (forgejo)
Forgejo:
- Self-hosted git, registration disabled
- Runs as git user on port 3000
- GitHub mirror sync via hourly systemd timer
- Requires /etc/forgejo-mirror.env with GITHUB_TOKEN, FORGEJO_TOKEN,
FORGEJO_URL, FORGEJO_OWNER
Sandbox Agent:
- System-level systemd services (not user units)
- sandbox-agent on :2470, env from ~/.config/sandbox-agent/agent.env
- sandbox-cors-proxy on :2468 (Node.js)
- No cloudflared - nginx handles SSL termination
Garbage collection: 3-day retention (vs 14-day on darwin).
Disk guards: min-free 100MB, max-free 1GB.
Journald: 1-week retention.
8. DEPLOY COMMANDS
-------------------
Darwin (local):
just switch
Netty (from mac):
just switch-netty
First-time netty install:
nix run github:nix-community/nixos-anywhere -- \
--flake .#netty --target-host netty --build-on-remote
9. ROLLBACK
-------------
Each phase is a separate git commit.
NixOS rollback:
ssh netty "nixos-rebuild switch --rollback"
Or boot previous generation from GRUB (3 kept).
Darwin rollback:
git revert <commit> && just switch
Home Manager rollback:
home-manager generations # list
home-manager switch --flake .#<host> # after git revert
10. FEATURE FLAGS REFERENCE
-----------------------------
| Feature | darwin | netty |
|---------|--------|-------|
| rust | yes | yes |
| go | yes | yes |
| node | yes | yes |
| python | yes | yes |
| aws | yes | yes |
| claude | yes | yes |
| docker | yes | no |
| tex | yes | no |
Set in lib/hosts.nix, consumed by home/xdg.nix and lib/package-sets.nix.

3832
pkgs/sandbox-agent/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,38 @@
{
lib,
fetchFromGitHub,
rustPlatform,
}:
rustPlatform.buildRustPackage {
pname = "sandbox-agent";
version = "0.5.0-rc.1";
src = fetchFromGitHub {
owner = "rivet-dev";
repo = "sandbox-agent";
rev = "v0.5.0-rc.1";
hash = "sha256-oeOpWjaQlQZZzwQGts4yJgL3STDCd3Hz2qbOJ4N2HBM=";
};
cargoLock.lockFile = ./Cargo.lock;
prePatch = ''
cp ${./Cargo.lock} Cargo.lock
'';
cargoBuildFlags = [
"-p"
"sandbox-agent"
];
env.SANDBOX_AGENT_SKIP_INSPECTOR = "1";
doCheck = false;
meta = with lib; {
description = "Universal API for coding agents in sandboxes";
homepage = "https://sandboxagent.dev";
license = licenses.asl20;
mainProgram = "sandbox-agent";
platforms = platforms.unix;
};
}