From 6db6d131be4fa61c1ac5b01ce17e069912781396 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 05:04:54 +0000 Subject: [PATCH 01/13] Bump actions/checkout from 4 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/check.yml | 2 +- .github/workflows/flakehub-publish-rolling.yml | 2 +- .github/workflows/update.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index fcf1db9..8c670c4 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -29,7 +29,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: wimpysworld/nothing-but-nix@main if: runner.os == 'Linux' with: diff --git a/.github/workflows/flakehub-publish-rolling.yml b/.github/workflows/flakehub-publish-rolling.yml index 57250d0..50ccb47 100644 --- a/.github/workflows/flakehub-publish-rolling.yml +++ b/.github/workflows/flakehub-publish-rolling.yml @@ -10,7 +10,7 @@ jobs: id-token: "write" contents: "read" steps: - - uses: "actions/checkout@v5" + - uses: "actions/checkout@v7" with: persist-credentials: false - uses: "DeterminateSystems/determinate-nix-action@v3" diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index a65fbdc..bed5d7c 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - uses: wimpysworld/nothing-but-nix@main with: hatchet-protocol: 'carve' From 60b23e42d0a637b442a0d0cc36acc797acdbf05a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 05:04:58 +0000 Subject: [PATCH 02/13] Bump cachix/cachix-action from 14 to 17 Bumps [cachix/cachix-action](https://github.com/cachix/cachix-action) from 14 to 17. - [Release notes](https://github.com/cachix/cachix-action/releases) - [Changelog](https://github.com/cachix/cachix-action/blob/master/RELEASE.md) - [Commits](https://github.com/cachix/cachix-action/compare/v14...v17) --- updated-dependencies: - dependency-name: cachix/cachix-action dependency-version: '17' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/check.yml | 2 +- .github/workflows/update.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index fcf1db9..a571bb2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -37,7 +37,7 @@ jobs: - uses: cachix/install-nix-action@v31 with: github_access_token: ${{ secrets.GH_TOKEN }} - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v17 with: name: rde authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index a65fbdc..26e57c3 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -16,7 +16,7 @@ jobs: hatchet-protocol: 'carve' - name: Install Determinate Nix uses: DeterminateSystems/determinate-nix-action@v3 - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v17 with: name: rde authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' From d9a8c31bc622aabd138bafa1367ef524421868ad Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Mon, 27 Jul 2026 10:40:14 +1000 Subject: [PATCH 03/13] always add vimtex --- modules/module/specs/plugins.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/module/specs/plugins.nix b/modules/module/specs/plugins.nix index 7630632..2b92ef4 100644 --- a/modules/module/specs/plugins.nix +++ b/modules/module/specs/plugins.nix @@ -78,6 +78,7 @@ in { lze lzextras plenary-nvim + vimtex neogit { data = mini-nvim; @@ -140,7 +141,6 @@ in { data = with pkgs.vimPlugins; [ quarto-nvim render-markdown-nvim - vimtex { data = otter-nvim; pname = "otter"; From 6b6f5b3e57e10832fb840c9a44a2b5a6d0350cc8 Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Mon, 27 Jul 2026 11:25:49 +1000 Subject: [PATCH 04/13] vimtex is not lua --- plugin/28_latex.lua | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/plugin/28_latex.lua b/plugin/28_latex.lua index b77b201..3b24f9e 100644 --- a/plugin/28_latex.lua +++ b/plugin/28_latex.lua @@ -11,24 +11,19 @@ later(function() return end + -- vimtex is a VimL plugin: it does not expose a Lua module. Configure it + -- via globals before loading so the plugin picks them up on startup. + vim.g.vimtex_compiler_method = "latexmk" + vim.g.vimtex_compiler_latexmk = { + -- Keep conservative defaults: no continuous background compilation and no + -- callback chatter. Manual :VimtexCompile still works on demand. + continuous = 0, + callback = 0, + } + -- Let vimtex choose the first available viewer (zathura, Skim, Evince, ...). + Config.add("vimtex") - local ok, vimtex = pcall(require, "vimtex") - if not ok then - vim.notify("vimtex not available", vim.log.levels.WARN) - return - end - - -- Keep conservative defaults: latexmk continuous compilation off, single - -- viewer (zathura falls back to Evince on most setups). - vimtex.setup({ - enabled = true, - compile_on_save = false, - compiler = "latexmk", - -- Avoid hooking spell/formatting into our global group; - -- vimtex stashes its own mappings automatically. - }) - -- Filetype detection is normally on, but force it explicitly so .tex files -- opened outside Quarto still pick up the LSP + viewer hooks. vim.api.nvim_create_autocmd("BufReadPost", { From b8f72cb15045e2615bbac25d3cbcc3cabdb1b520 Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Thu, 30 Jul 2026 13:46:58 +1000 Subject: [PATCH 05/13] Small fixes --- flake.nix | 6 + plugin/22_languages.lua | 27 +++-- plugin/23_editor.lua | 8 +- tests/smoke.lua | 260 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 288 insertions(+), 13 deletions(-) create mode 100644 tests/smoke.lua diff --git a/flake.nix b/flake.nix index db14f58..2087556 100644 --- a/flake.nix +++ b/flake.nix @@ -230,6 +230,12 @@ nvim --headless -u NONE -c "set runtimepath+=${./.}" -l ${./tests/init.lua} touch $out ''; + + smoke-test = pkgs.runCommand "smoke-test" {} '' + BINARY_PATH="${defaultNvimPkg}/bin/vv" + "$BINARY_PATH" --headless -c "luafile ${./tests/smoke.lua}" -c "qa!" + touch $out + ''; } ); diff --git a/plugin/22_languages.lua b/plugin/22_languages.lua index cd5ebed..9eac58b 100644 --- a/plugin/22_languages.lua +++ b/plugin/22_languages.lua @@ -55,21 +55,24 @@ later(function() ignore_exitcode = true, parser = function(output, bufnr, linter_cwd) local diagnostics = {} - -- Pattern: /path/file.R:10:5: style: Some message + -- Expected format: /path/file.R:10:5: style: Some message + local severity_map = { + style = vim.diagnostic.severity.INFO, + warning = vim.diagnostic.severity.WARN, + error = vim.diagnostic.severity.ERROR, + } for line in output:gmatch("[^\r\n]+") do - local path, lnum, col, severity, message = line:match("^[^:]+:(%d+):(%d+):%s*(%w+):%s*(.+)$") - if path then - local severity_map = { - style = vim.diagnostic.severity.INFO, - warning = vim.diagnostic.severity.WARN, - error = vim.diagnostic.severity.ERROR, - } + -- Capture the file path as well so lnum/col line up with the numbers. + local path, lnum, col, severity, message = line:match("^(.-):(%d+):(%d+):%s*(%w+):%s*(.+)$") + if path and lnum and col and severity then + local line_num = tonumber(lnum) + local col_num = tonumber(col) table.insert(diagnostics, { bufnr = bufnr, - lnum = math.max(0, tonumber(lnum) - 1), - col = math.max(0, tonumber(col) - 1), - end_lnum = tonumber(lnum) - 1, - end_col = tonumber(col), + lnum = math.max(0, line_num - 1), + col = math.max(0, col_num - 1), + end_lnum = line_num - 1, + end_col = col_num, severity = severity_map[severity:lower()] or vim.diagnostic.severity.WARN, message = message or "lintr issue", source = "lintr", diff --git a/plugin/23_editor.lua b/plugin/23_editor.lua index 4345e2d..af4c96d 100644 --- a/plugin/23_editor.lua +++ b/plugin/23_editor.lua @@ -37,7 +37,13 @@ later(function() end) later(function() - require("mini.align").setup() + -- Keep `ga` free for CodeCompanion's "accept change" mapping. + require("mini.align").setup({ + mappings = { + start = "gA", + start_with_preview = "g", + }, + }) end) later(function() diff --git a/tests/smoke.lua b/tests/smoke.lua new file mode 100644 index 0000000..e4f0aaf --- /dev/null +++ b/tests/smoke.lua @@ -0,0 +1,260 @@ +-- Comprehensive CI smoke tests for the Neovim configuration. +-- Run inside the wrapped Neovim binary, e.g.: +-- vv --headless -c "luafile tests/smoke.lua" -c "qa!" +-- +-- The script loads the full config (plugin/*.lua files are sourced by Neovim on +-- startup), waits for mini.deps deferred work, then exercises key functionality +-- without user interaction. + +local M = {} + +-- --------------------------------------------------------------------------- +-- Tiny test harness +-- --------------------------------------------------------------------------- +local failures = {} +local passed = 0 + +local function fail(msg) + table.insert(failures, msg) + print(" FAIL: " .. msg) +end + +local function pass(msg) + passed = passed + 1 + print(" PASS: " .. msg) +end + +local function assert_eq(a, b, msg) + if a == b then + pass(msg) + else + fail(string.format("%s (expected %s, got %s)", msg, vim.inspect(b), vim.inspect(a))) + end +end + +local function assert_true(cond, msg) + if cond then + pass(msg) + else + fail(msg) + end +end + +local function assert_loaded(mod, msg) + assert_true(package.loaded[mod] ~= nil, msg or ("module '" .. mod .. "' loaded")) +end + +-- --------------------------------------------------------------------------- +-- Wait for deferred mini.deps/later work. +-- We patch MiniDeps.later to count pending callbacks and then drain the event +-- loop until the counter returns to zero (or we time out). +-- --------------------------------------------------------------------------- +local function wait_for_deferred(timeout_ms) + timeout_ms = timeout_ms or 10000 + + -- Fire VimEnter so startup autocmds run, then drain the event loop so + -- mini.deps' deferred setup closures have a chance to execute before we + -- assert anything. + vim.cmd('doautocmd VimEnter') + vim.wait(timeout_ms, function() return false end, 50) +end + +-- --------------------------------------------------------------------------- +-- Helper: check whether a keymap is registered for a given lhs in normal mode. +-- --------------------------------------------------------------------------- +local function has_normal_map(lhs) + -- is stored as the literal leader key, so test both the raw + -- symbolic form and the expanded form (e.g. "ff" and " ff"). + local expanded = lhs:gsub('^', vim.g.mapleader or '\\') + + if vim.keymap and vim.keymap.get then + local maps = vim.keymap.get('n') + for _, map in ipairs(maps) do + if map.lhs == lhs or map.lhs == expanded then + return true + end + end + end + + return vim.fn.maparg(lhs, 'n') ~= '' or vim.fn.maparg(expanded, 'n') ~= '' +end + +-- --------------------------------------------------------------------------- +-- Helper: check whether a Treesitter parser is available. +-- --------------------------------------------------------------------------- +local function has_parser(lang) + return #vim.api.nvim_get_runtime_file("parser/" .. lang .. ".*", false) > 0 +end + +-- --------------------------------------------------------------------------- +-- Helper: check whether an LSP config was registered (Neovim 0.11+ native API). +-- --------------------------------------------------------------------------- +local function has_lsp_config(name) + if vim.lsp and vim.lsp.config then + -- vim.lsp.config(name) returns the merged config or {} if none registered. + local ok, cfg = pcall(vim.lsp.config, name) + if ok and cfg and next(cfg) ~= nil then + return true + end + end + -- Fallback: inspect lspconfig internal table. + local ok, configs = pcall(require, 'lspconfig.configs') + if ok and configs and configs[name] then + return true + end + return false +end + +-- --------------------------------------------------------------------------- +-- Test suites +-- --------------------------------------------------------------------------- +function M.test_core_config() + print("\n=== Core configuration ===") + assert_eq(vim.g.mapleader, ' ', "leader is space") + assert_eq(vim.g.maplocalleader, ',', "localleader is comma") + assert_true(vim.o.backup, "backup option enabled") + assert_true(vim.o.undofile, "undofile enabled") + assert_eq(vim.o.mouse, 'a', "mouse enabled") +end + +function M.test_config_module() + print("\n=== Config module ===") + assert_true(_G.Config ~= nil, "global Config table exists") + assert_true(type(Config.edit) == 'function', "Config.edit helper exists") + assert_true(type(Config.terminal) == 'table', "Config.terminal namespace exists") + assert_true(type(Config.treesitter_helpers) == 'table', "Config.treesitter_helpers exists") +end + +function M.test_mini_modules() + print("\n=== mini.nvim modules ===") + assert_loaded('mini.basics', 'mini.basics loaded') + assert_loaded('mini.statusline', 'mini.statusline loaded') + assert_loaded('mini.tabline', 'mini.tabline loaded') + assert_loaded('mini.clue', 'mini.clue loaded') + assert_loaded('mini.pick', 'mini.pick loaded') + assert_loaded('mini.notify', 'mini.notify loaded') +end + +function M.test_keymaps() + print("\n=== Keymaps ===") + assert_true(has_normal_map('ff'), "leader ff -> pick files") + assert_true(has_normal_map('fg'), "leader fg -> live grep") + assert_true(has_normal_map('bb'), "leader bb -> alternate buffer") + assert_true(has_normal_map('ed'), "leader ed -> mini.files open") + assert_true(has_normal_map(''), "Esc clears search highlight") +end + +function M.test_treesitter() + print("\n=== Treesitter parsers ===") + local expected = { 'lua', 'python', 'nix', 'markdown', 'latex', 'r', 'julia' } + for _, lang in ipairs(expected) do + assert_true(has_parser(lang), "parser available: " .. lang) + end +end + +function M.test_filetype_detection() + print("\n=== Filetype detection ===") + local test_buf = vim.api.nvim_create_buf(false, true) + local orig_buf = vim.api.nvim_get_current_buf() + vim.api.nvim_set_current_buf(test_buf) + + -- Test by manually triggering filetype detection for a couple of languages. + vim.api.nvim_buf_set_name(test_buf, 'test.py') + vim.api.nvim_set_option_value('filetype', 'python', { buf = test_buf }) + assert_eq(vim.bo.filetype, 'python', "python filetype set") + + vim.api.nvim_buf_set_name(test_buf, 'test.lua') + vim.api.nvim_set_option_value('filetype', 'lua', { buf = test_buf }) + assert_eq(vim.bo.filetype, 'lua', "lua filetype set") + + vim.api.nvim_set_current_buf(orig_buf) + vim.api.nvim_buf_delete(test_buf, { force = true }) +end + +function M.test_lsp_config() + print("\n=== LSP server registration ===") + local servers = { 'lua_ls', 'basedpyright', 'ruff', 'nil_ls', 'texlab', 'marksman', 'harper_ls' } + for _, name in ipairs(servers) do + assert_true(has_lsp_config(name), "LSP config registered: " .. name) + end +end + +function M.test_plugin_configs() + print("\n=== Plugin-specific configuration ===") + -- vimtex globals should be set when the markdown cat is on. + if vim.g.vimtex_compiler_method then + assert_eq(vim.g.vimtex_compiler_method, 'latexmk', "vimtex compiler is latexmk") + else + pass("vimtex not configured (markdown cat disabled)") + end + + -- conform formatters should be registered. + local ok, conform = pcall(require, 'conform') + if ok and conform then + local formatters = require('conform').formatters + assert_true(formatters ~= nil, "conform formatters table exists") + else + pass("conform not loaded (expected if utils cat disabled)") + end + + -- blink.cmp keymap preset should be available. + local blink_ok, blink = pcall(require, 'blink.cmp') + if blink_ok and blink then + pass("blink.cmp loaded") + else + pass("blink.cmp not loaded (expected if utils cat disabled)") + end +end + +function M.test_nix_cats_helper() + print("\n=== nixCats helper ===") + local ok, nix = pcall(require, 'config.nix') + assert_true(ok and nix ~= nil, "config.nix can be required") + if ok and nix then + local cat = nix.get_cat('general', true) + assert_true(type(cat) == 'boolean', "get_cat returns boolean default") + end +end + +-- --------------------------------------------------------------------------- +-- Entry point +-- --------------------------------------------------------------------------- +function M.run() + print("Neovim config CI smoke tests") + local v = vim.version() + print(string.format("Neovim version: %d.%d.%d", v.major, v.minor, v.patch)) + + -- Drain deferred plugin setup before asserting. + wait_for_deferred() + + M.test_core_config() + M.test_config_module() + M.test_mini_modules() + M.test_keymaps() + M.test_treesitter() + M.test_filetype_detection() + M.test_lsp_config() + M.test_plugin_configs() + M.test_nix_cats_helper() + + print("\n=== Summary ===") + print(string.format("Passed: %d", passed)) + print(string.format("Failed: %d", #failures)) + + if #failures > 0 then + print("\nFailed tests:") + for _, f in ipairs(failures) do + print(" - " .. f) + end + vim.cmd('cquit 1') + else + print("\nAll smoke tests passed!") + vim.cmd('cquit 0') + end +end + +local ok, err = pcall(M.run) +if not ok then + print("CRASH: " .. tostring(err)) + vim.cmd('cquit 1') +end From 92729cff562101edbfb0edd6a208628ce2fc0333 Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Fri, 7 Aug 2026 12:48:40 +1000 Subject: [PATCH 06/13] added bloocky and dooing --- .letta/settings.local.json | 17 ++ flake.lock | 46 ++++- flake.nix | 10 + lua/keymap/leader.lua | 73 +++++--- modules/module/specs/plugins.nix | 8 +- nvim.log | 13 ++ overlays/plugins.nix | 16 -- overlays/r.nix | 2 +- plugin/20_startup.lua | 4 +- plugin/22_languages.lua | 2 +- plugin/23_editor.lua | 3 +- plugin/24_completion.lua | 312 +++---------------------------- plugin/25_lsp.lua | 2 +- plugin/29_bloocky.lua | 26 +++ plugin/30_dooing.lua | 28 +++ tests/smoke.lua | 12 +- 16 files changed, 234 insertions(+), 340 deletions(-) create mode 100644 .letta/settings.local.json create mode 100644 nvim.log create mode 100644 plugin/29_bloocky.lua create mode 100644 plugin/30_dooing.lua diff --git a/.letta/settings.local.json b/.letta/settings.local.json new file mode 100644 index 0000000..7656883 --- /dev/null +++ b/.letta/settings.local.json @@ -0,0 +1,17 @@ +{ + "lastAgent": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf", + "sessionsByServer": { + "api.letta.com": { + "agentId": "agent-21b21fd6-49e4-4d5c-a1b7-1c6dcd8dacc9", + "conversationId": "default" + }, + "local:/Users/daniel/.letta/lc-local-backend": { + "agentId": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf", + "conversationId": "default" + } + }, + "lastSession": { + "agentId": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf", + "conversationId": "default" + } +} \ No newline at end of file diff --git a/flake.lock b/flake.lock index 17fe244..195ab40 100644 --- a/flake.lock +++ b/flake.lock @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1784796856, - "narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=", + "lastModified": 1785967620, + "narHash": "sha256-IItrdb7Puk05RqOBWZYFC5X6Wl1sJmCfh5MWVHw5iMM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e2587caef70cea85dd97d7daab492899902dbf5d", + "rev": "b7c2ada94fe99c15b0dbcf4d11fd7850b957a436", "type": "github" }, "original": { @@ -36,6 +36,22 @@ "type": "github" } }, + "plugins-bloocky": { + "flake": false, + "locked": { + "lastModified": 1786042339, + "narHash": "sha256-8QcoC1bS8pRcwBkW3mqD2vgP9Mu0aCdIy6umZO6mUGM=", + "owner": "atiladefreitas", + "repo": "bloocky", + "rev": "a052c3b1a8126e04b194bb3db3c4c6ca641c0e4e", + "type": "github" + }, + "original": { + "owner": "atiladefreitas", + "repo": "bloocky", + "type": "github" + } + }, "plugins-cmp-pandoc-references": { "flake": false, "locked": { @@ -52,6 +68,22 @@ "type": "github" } }, + "plugins-dooing": { + "flake": false, + "locked": { + "lastModified": 1785971574, + "narHash": "sha256-GtEC5kWcH0DmiYXkzxzNjjfYLfyEb7zpTLQWdi7n3zs=", + "owner": "atiladefreitas", + "repo": "dooing", + "rev": "6748316bc6b4601797fb952a83694cc0a5ef6da2", + "type": "github" + }, + "original": { + "owner": "atiladefreitas", + "repo": "dooing", + "type": "github" + } + }, "r-nvim-nix": { "inputs": { "nixpkgs": [ @@ -76,11 +108,11 @@ }, "rixpkgs": { "locked": { - "lastModified": 1782576256, - "narHash": "sha256-KOvpL9DJJmShb64mX9QTjhxHaxE8MizQIqNUAniuJ2E=", + "lastModified": 1785776890, + "narHash": "sha256-CzzgB1teVbroR/QhGAnsQToUYCDXEZZtFTcISAooASM=", "owner": "dwinkler1", "repo": "rixpkgs", - "rev": "815afc01bc0cc9a2eba80906645bd08f976a4401", + "rev": "a54080a58b57d5785dc85901850b817bef2aaf83", "type": "github" }, "original": { @@ -111,7 +143,9 @@ "inputs": { "fran": "fran", "nixpkgs": "nixpkgs", + "plugins-bloocky": "plugins-bloocky", "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references", + "plugins-dooing": "plugins-dooing", "r-nvim-nix": "r-nvim-nix", "rixpkgs": "rixpkgs", "wrappers": "wrappers" diff --git a/flake.nix b/flake.nix index 2087556..53117d5 100644 --- a/flake.nix +++ b/flake.nix @@ -29,6 +29,16 @@ url = "github:jmbuhr/cmp-pandoc-references"; flake = false; }; + + "plugins-bloocky" = { + url = "github:atiladefreitas/bloocky"; + flake = false; + }; + + "plugins-dooing" = { + url = "github:atiladefreitas/dooing"; + flake = false; + }; }; outputs = { diff --git a/lua/keymap/leader.lua b/lua/keymap/leader.lua index 795079f..e741c81 100644 --- a/lua/keymap/leader.lua +++ b/lua/keymap/leader.lua @@ -11,28 +11,39 @@ nmap_leader('', 'bnext', 'Next buffer') nmap_leader('', 'bprev', 'Prev buffer') -- a is for 'AI' -nmap_leader("aa", "CodeCompanion /agent", "Agent chat (@{agent} tools)") -nmap_leader("ac", "CodeCompanionChat Toggle", "Chat Toggle") -nmap_leader("aC", function() - local chat = require("codecompanion").last_chat() - if not chat then - return vim.notify("No CodeCompanion chat to compact", vim.log.levels.WARN) - end - require("codecompanion.interactions.chat.context_management.compaction").compact(chat, { min_token_savings = 0 }) -end, "Compact chat") -nmap_leader("ag", "CodeCompanion /commit", "Generate commit message") -nmap_leader("ai", "CodeCompanionActions", "Chat Action") -nmap_leader("al", "CodeCompanion /lsp", "Explain LSP Diagnostics") -nmap_leader("an", "CodeCompanionChat Add", "Chat New") -nmap_leader("as", "CodeCompanion /suggest", "Suggest Improvements") -nmap_leader("aw", "CodeCompanion /tdd", "Workflow: plan, implement, test") -nmap_leader("ax", "CodeCompanion /fixer", "Code Fixer") -xmap_leader("aa", "CodeCompanion /agent", "Agent on selection") -xmap_leader("ae", "CodeCompanion /explain", "Explain Code") -xmap_leader("af", "CodeCompanion /fix", "Fix Code") -xmap_leader("ap", "CodeCompanion /expert", "Code Expert") -xmap_leader("as", "CodeCompanion /suggest", "Suggest Improvements") -nmap_leader("ak", "CodeCompanionChat adapter=codex", "Chat with Codex") +nmap_leader("aa", function() + require("opencode").ask("@this: ") +end, "Ask OpenCode about this") +nmap_leader("ab", function() + require("opencode").prompt("Analyse @buffer") +end, "Analyse buffer") +nmap_leader("aB", function() + require("opencode").prompt("Analyse @buffers") +end, "Analyse open buffers") +nmap_leader("ad", function() + require("opencode").prompt("Explain @diagnostics and fix the underlying issue") +end, "Explain/fix diagnostics") +nmap_leader("an", function() + require("opencode").command("session.new") +end, "New OpenCode session") +nmap_leader("ap", function() + require("opencode").ask("@") +end, "Ask OpenCode with context") +nmap_leader("as", function() + require("opencode").select() +end, "Select OpenCode action") +xmap_leader("aa", function() + require("opencode").ask("@this: ") +end, "Ask OpenCode about selection") +xmap_leader("af", function() + require("opencode").prompt("Fix the selected code and preserve its surrounding API:\n@this") +end, "Fix selection") +xmap_leader("ap", function() + require("opencode").ask("@") +end, "Ask OpenCode with context") +xmap_leader("as", function() + require("opencode").select() +end, "Select OpenCode action") -- b is for 'buffer' nmap_leader('bb', 'b#', 'Alternate') @@ -214,6 +225,24 @@ nmap_leader("wh", "h", "Go to Left Window", { remap = true }) nmap_leader("wj", "j", "Go to Lower Window", { remap = true }) nmap_leader("wk", "k", "Go to Upper Window", { remap = true }) nmap_leader("wl", "l", "Go to Right Window", { remap = true }) +nmap_leader("w>", "vertical resize +5", "Increase Window Width") +nmap_leader("w", "vertical resize -5", "Decrease Window Width") + +local function set_window_width_percent(percent) + return function() + local target_width = math.max(1, math.floor(vim.o.columns * percent / 100 + 0.5)) + vim.api.nvim_win_set_width(0, target_width) + end +end + +for i = 1, 9 do + local percent = i * 10 + nmap_leader( + "w" .. i, + set_window_width_percent(percent), + string.format("Set Window Width to %d%%", percent) + ) +end nmap_leader("_", "s", "Split Window Below", { remap = true }) nmap_leader("|", "v", "Split Window Right", { remap = true }) diff --git a/modules/module/specs/plugins.nix b/modules/module/specs/plugins.nix index 2b92ef4..babb1ba 100644 --- a/modules/module/specs/plugins.nix +++ b/modules/module/specs/plugins.nix @@ -80,6 +80,8 @@ in { plenary-nvim vimtex neogit + config.nvim-lib.neovimPlugins.bloocky + config.nvim-lib.neovimPlugins.dooing { data = mini-nvim; pname = "mini.nvim"; @@ -163,10 +165,8 @@ in { pname = "nvim-treesitter"; } { - data = pkgs.codecompanion-nvim.overrideAttrs (old: { - doCheck = false; - }); - pname = "codecompanion"; + data = pkgs.vimPlugins.opencode-nvim; + pname = "opencode.nvim"; } ] ++ builtins.attrValues pkgs.vimPlugins.nvim-treesitter.queries; }; diff --git a/nvim.log b/nvim.log new file mode 100644 index 0000000..4eb4ddf --- /dev/null +++ b/nvim.log @@ -0,0 +1,13 @@ +WRN 2026-07-31T16:36:01.037 ?.50508 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/TN25Kv/nvim.50508.0 +WRN 2026-07-31T16:36:38.634 ?.50644 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/6jT8As/nvim.50644.0 +WRN 2026-07-31T16:46:12.752 ?.54395 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/zY36Oi/nvim.54395.0 +WRN 2026-07-31T17:01:43.138 ?.58656 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/nDisRA/nvim.58656.0 +WRN 2026-07-31T17:01:50.641 ?.58696 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/okDyqb/nvim.58696.0 +WRN 2026-07-31T17:02:04.773 ?.58741 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/5wxfRu/nvim.58741.0 +WRN 2026-07-31T17:02:29.644 ?.58854 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/OMTfpB/nvim.58854.0 +WRN 2026-07-31T17:02:29.644 ?.58852 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/QZvLy2/nvim.58852.0 +WRN 2026-07-31T17:14:31.289 ?.64329 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/zh9tLm/nvim.64329.0 +WRN 2026-07-31T17:14:40.017 ?.64399 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/pGaDez/nvim.64399.0 +WRN 2026-07-31T17:17:48.228 ?.64921 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/ryMpX0/nvim.64921.0 +WRN 2026-07-31T17:17:52.664 ?.64953 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/X7RmXh/nvim.64953.0 +WRN 2026-07-31T17:18:00.691 ?.64989 server_start:197: Failed to start server: operation not permitted: /tmp/nvim.daniel/C6njjq/nvim.64989.0 diff --git a/overlays/plugins.nix b/overlays/plugins.nix index 91c01aa..17d7c5f 100644 --- a/overlays/plugins.nix +++ b/overlays/plugins.nix @@ -1,22 +1,6 @@ { ... }: final: prev: { - codecompanion-nvim = prev.vimPlugins.codecompanion-nvim.overrideAttrs { - checkInputs = with prev.vimPlugins; [ - blink-cmp - mini-nvim - ]; - dependencies = [ prev.vimPlugins.plenary-nvim ]; - nvimSkipModules = [ - "codecompanion.actions.static" - "codecompanion.actions.init" - "minimal" - "codecompanion.providers.actions.fzf_lua" - "codecompanion.providers.completion.cmp.setup" - "codecompanion.providers.actions.telescope" - "codecompanion.providers.actions.snacks" - ]; - }; zk-nvim = prev.vimPlugins.zk-nvim.overrideAttrs { nvimSkipModules = [ "zk.pickers.fzf_lua" diff --git a/overlays/r.nix b/overlays/r.nix index e736b77..f03d07e 100644 --- a/overlays/r.nix +++ b/overlays/r.nix @@ -5,7 +5,7 @@ }; in { inherit rpkgs; - baseRPackages = [rpkgs.nvimcom]; + baseRPackages = [rpkgs.nvimcom rpkgs.rPackages.btw]; rWrapper = rpkgs.rWrapper.override {packages = [];}; quarto = rpkgs.quarto.override {extraRPackages = [];}; } diff --git a/plugin/20_startup.lua b/plugin/20_startup.lua index 36bab13..f5cc9e8 100644 --- a/plugin/20_startup.lua +++ b/plugin/20_startup.lua @@ -191,7 +191,7 @@ end) -- Treesitter now_if_args(function() - vim.treesitter.language.register("markdown", { "markdown", "codecompanion", "rmd", "quarto" }) + vim.treesitter.language.register("markdown", { "markdown", "rmd", "quarto" }) require 'treesitter-context'.setup { enable = true, @@ -216,7 +216,7 @@ now_if_args(function() "julia", "rnoweb", "latex", "gitcommit", "gitignore", "git_config", "git_rebase", "diff", "dockerfile", "make", "xml", "zig", "regex", "csv", "bash", - "markdown_inline", "quarto", "rmd", "codecompanion", + "markdown_inline", "quarto", "rmd", } local function start_treesitter(buf, filetype) local lang = vim.treesitter.language.get_lang(filetype) or filetype diff --git a/plugin/22_languages.lua b/plugin/22_languages.lua index 9eac58b..3a6282e 100644 --- a/plugin/22_languages.lua +++ b/plugin/22_languages.lua @@ -102,7 +102,7 @@ now_if_args(function() add("render-markdown.nvim") require('render-markdown').setup({ -- completions = { blink = { enabled = true } }, - file_types = { 'markdown', 'codecompanion', }, + file_types = { 'markdown', }, link = { wiki = { body = function(ctx) diff --git a/plugin/23_editor.lua b/plugin/23_editor.lua index af4c96d..362333d 100644 --- a/plugin/23_editor.lua +++ b/plugin/23_editor.lua @@ -37,7 +37,6 @@ later(function() end) later(function() - -- Keep `ga` free for CodeCompanion's "accept change" mapping. require("mini.align").setup({ mappings = { start = "gA", @@ -219,6 +218,8 @@ later(function() local minikeymap = require("mini.keymap") minikeymap.setup() local map_multistep = minikeymap.map_multistep + -- blink_next/blink_prev/blink_accept are blink.cmp actions. + -- Direct blink keymaps (C-space/C-l) are set in 24_completion.lua. local tab_steps = { "blink_next", "pmenu_next", diff --git a/plugin/24_completion.lua b/plugin/24_completion.lua index c2291c7..38e57f2 100644 --- a/plugin/24_completion.lua +++ b/plugin/24_completion.lua @@ -4,9 +4,6 @@ local later = MiniDeps.later local now = MiniDeps.now local now_if_args = Config.now_if_args --- Constants -local BLINK_VERSION = "v1.10.2" - -- Plugin sources configuration local PLUGIN_SOURCES = { "hrsh7th/cmp-cmdline", @@ -14,7 +11,7 @@ local PLUGIN_SOURCES = { "zbirenbaum/copilot.lua", "jmbuhr/cmp-pandoc-references", "fang2hou/blink-copilot", - "olimorris/codecompanion.nvim" + "nickjvandyke/opencode.nvim", } local PLUGIN_ADDS = { @@ -24,34 +21,17 @@ local PLUGIN_ADDS = { "cmp-pandoc-references", } --- Helper functions -local function create_system_prompt(role_description) - return function(context) - local lang = context.filetype or "programmer" - return "I want you to act as a senior " .. lang .. " developer. " .. role_description - end -end - -local function get_code_block(context) - local text = require("codecompanion.helpers.code").get_code(context.start_line, context.end_line) - return "```" .. context.filetype .. "\n" .. text .. "\n```" -end - local function get_mini_icons_highlight(ctx) local _, hl, _ = require("mini.icons").get("lsp", ctx.kind) return hl end local function get_blink_fuzzy_setting() - local setting = { - sorts = { "exact", "score", "sort_text" } + return { + sorts = { "exact", "score", "sort_text" }, + use_frecency = true, + use_proximity = true, } - - if not Config.isNixCats then - setting.prebuilt_binaries = { force_version = BLINK_VERSION } - end - - return setting end -- Plugin loading @@ -62,7 +42,6 @@ if not Config.isNixCats then add({ source = "saghen/blink.cmp", depends = { "rafamadriz/friendly-snippets" }, - checkout = BLINK_VERSION, }) end) @@ -73,241 +52,6 @@ if not Config.isNixCats then end) end -local function get_codecompanion_config() - return { - adapters = { - acp = { - -- Codex = heavy agent lane (ChatGPT Edu login via `codex login`; ~/.codex/auth.json). - -- Requires `codex-acp` on PATH (~/.nix-profile/bin). ACP-only slash commands in the - -- chat buffer: /resume (restore a past session, fresh chat only), /mode (switch agent - -- mode), /command, /acp_session_options (e.g. model per session); `\` triggers ACP - -- command completion (1-5s delay after chat open). - codex = function() - return require("codecompanion.adapters").extend("codex", { - defaults = { - auth_method = "chatgpt", - }, - }) - end, - }, - }, - interactions = { - chat = { - adapter = { - name = "copilot", - model = "claude-sonnet-5", - }, - slash_commands = { - ["share"] = { - opts = { - token = os.getenv("GITHUB_GIST_TOKEN"), - }, - }, - }, - opts = { - completion_provider = "blink", - context_management = { - editing = { - trigger = 0.65, - keep_cycles = 3, - exclude_tools = { "memory" }, - }, - compaction = { - trigger = 0.85, - min_token_savings = 10000, - }, - }, - }, - }, - inline = { - adapter = { - name = "copilot", - model = "gpt-5-mini", - }, - }, - shared = { - keymaps = { - accept_change = { - modes = { n = "ga" }, - description = "Accept the suggested change", - }, - reject_change = { - modes = { n = "gr" }, - opts = { nowait = true }, - description = "Reject the suggested change", - }, - }, - }, - background = { - adapter = { - name = "copilot", - model = "gpt-5-mini", - }, - chat = { - callbacks = { - ["on_ready"] = { - actions = { "interactions.background.builtin.chat_make_title" }, - enabled = true, - }, - }, - opts = { - enabled = true, - }, - }, - }, - }, - display = { - chat = { - show_settings = false, - window = { - layout = "horizontal", - position = "bottom", - height = 0.33, - }, - }, - diff = { - enabled = true, - threshold_for_chat = 6, - }, - }, - rules = { - default = { - description = "Collection of common files for all projects", - files = { - ".clinerules", - ".cursorrules", - ".rules", - ".github/copilot-instructions.md", - "AGENT.md", - "AGENTS.md", - { path = "CLAUDE.md", parser = "claude" }, - { path = "CLAUDE.local.md", parser = "claude" }, - { path = "~/.claude/CLAUDE.md", parser = "claude" }, - }, - }, - opts = { - chat = { - autoload = "default", - enabled = true, - }, - }, - }, - prompt_library = { - ["expert"] = { - interaction = "chat", - description = "Get expert advice from an LLM", - opts = { alias = "expert" }, - prompts = { - { - role = "system", - content = create_system_prompt( - "I will ask you specific questions and I want you to return concise explanations and codeblock examples." - ), - }, - { - role = "user", - content = function(context) - return "I have the following code:\n\n" .. get_code_block(context) .. "\n\n" - end, - opts = { contains_code = true }, - }, - }, - }, - ["fixer"] = { - interaction = "chat", - description = "Fix code errors with expert guidance", - opts = { alias = "fixer" }, - prompts = { - { - role = "system", - content = create_system_prompt( - "I have a block of code that is not working and will give you a hint about the error. I want you to return the corrected code and a concise explanation of the corrections." - ), - }, - { - role = "user", - content = function(context) - return "The following code has an error:\n\n" .. get_code_block(context) .. "\n\nThe error is:" - end, - opts = { contains_code = true }, - }, - }, - }, - ["suggest"] = { - interaction = "chat", - description = "Suggest improvements to the buffer", - opts = { alias = "suggest" }, - prompts = { - { - role = "system", - content = create_system_prompt( - "When asked to improve code, follow these steps:\n" .. - "1. Identify the programming language.\n" .. - "2. Think separately for each function or significant block of code and think about possible improvements (e.g., for better readability or speed) in the context of the language.\n" .. - "3. Think about the whole document and think about possible improvements.\n" .. - "4. Provide the improved code.\n" .. - "5. Provide a concise explanation of the improvements." - ), - }, - { - role = "user", - content = function(context) - return "Please improve the following code:\n\n" .. get_code_block(context) - end, - opts = { contains_code = true }, - }, - }, - }, - ["agent"] = { - interaction = "chat", - description = "Agentic coding with the @{agent} tool group (read/edit/grep/run)", - opts = { alias = "agent" }, - prompts = { - { - role = "user", - content = function(context) - return "@{agent} Work on the following code:\n\n" .. get_code_block(context) .. "\n\n" - end, - opts = { contains_code = true }, - }, - }, - }, - ["tdd"] = { - interaction = "chat", - description = "Workflow: plan the buffer change, implement it, run the tests", - opts = { alias = "tdd", is_workflow = true }, - prompts = { - { - { - role = "user", - content = function(context) - return "Let's work test-driven. First, study #buffer and the relevant parts of the codebase, then propose a concise implementation plan (no code yet).\n\nThe code under discussion:\n\n" - .. get_code_block(context) - .. "\n\nThe task: " - end, - opts = { contains_code = true }, - }, - }, - { - { - role = "user", - content = "Implement the plan now, writing or updating tests alongside the code. @{agent}", - opts = { auto_submit = true }, - }, - }, - { - { - role = "user", - content = "Run the project's test suite with @{run_command} and fix any failures until it passes.", - opts = { auto_submit = true }, - }, - }, - }, - }, - }, - } -end - -- Batch add simple plugins later(function() for _, plugin in ipairs(PLUGIN_ADDS) do @@ -359,26 +103,33 @@ later(function() }) end) - --- CodeCompanion habit notes (chat buffer unless stated): --- /compact compact history, keep summary /fork fork the conversation --- /symbols insert symbols for a file /share export chat to a GitHub gist --- gm toggle "btw" ephemeral message gty YOLO: approve all tool calls --- gba/gbd buffer sync add/drop gd debug window (adapter/tools info) --- Codex (ACP) lane: /resume (fresh chat only), /mode, /command, /acp_session_options, --- `\` ACP command completion. Prompt library: /expert /fixer /suggest /agent /tdd (workflow). later(function() - add("codecompanion.nvim") - - -- now use function - require("codecompanion").setup(get_codecompanion_config()) - vim.cmd([[cab cc CodeCompanion]]) + vim.g.opencode_opts = { + events = { + reload = true, + permissions = { + enabled = true, + edits = { enabled = true }, + }, + }, + server = { + start = function() + vim.cmd("vsplit term://opencode --port") + vim.cmd("vertical resize " .. math.floor(vim.o.columns * 0.4)) + vim.cmd("wincmd p") + end + }, + } + add("opencode.nvim") end) now_if_args(function() add("blink.cmp") require("blink.cmp").setup({ + -- Direct blink keymaps (C-space/C-l). + -- Tab/Enter/Up/Down are handled via multistep chains in 23_editor.lua, + -- which chain blink_next/blink_prev/blink_accept with other editor actions. keymap = { preset = "default", [""] = { "show", "select_next" }, @@ -415,6 +166,7 @@ now_if_args(function() }, completion = { menu = { + border = "rounded", draw = { treesitter = { "lsp" }, components = { @@ -434,14 +186,12 @@ now_if_args(function() list = { selection = { preselect = false, auto_insert = true } }, - documentation = { auto_show = true }, + ghost_text = { enabled = true, show_with_menu = true }, + documentation = { auto_show = true, window = { border = "rounded" } }, trigger = { show_in_snippet = false }, }, snippets = { preset = "mini_snippets" }, sources = { - per_filetype = { - codecompanion = { "codecompanion" }, - }, default = { "references", "lsp", "path", "snippets", "buffer", "omni", "copilot" }, providers = { path = { @@ -467,12 +217,6 @@ now_if_args(function() score_offset = 45, async = true, }, - codecompanion = { - name = "CodeCompanion", - module = "codecompanion.providers.completion.blink", - score_offset = 45, - async = true, - }, references = { name = "pandoc_references", module = "cmp-pandoc-references.blink", diff --git a/plugin/25_lsp.lua b/plugin/25_lsp.lua index 5250cdd..6a719ca 100644 --- a/plugin/25_lsp.lua +++ b/plugin/25_lsp.lua @@ -15,7 +15,7 @@ now_if_args(function() basedpyright = {}, ruff = {}, marksman = { - filetypes = { "markdown", "markdown_inline", "codecompanion" }, + filetypes = { "markdown", "markdown_inline" }, }, harper_ls = { cmd = { "harper-ls", "--stdio" }, diff --git a/plugin/29_bloocky.lua b/plugin/29_bloocky.lua new file mode 100644 index 0000000..ec15b5d --- /dev/null +++ b/plugin/29_bloocky.lua @@ -0,0 +1,26 @@ +-- Bloocky: timeblocking calendar (day/week/month views), persisted to JSON. +-- Loaded via the `general` cat spec (nix mode) or MiniDeps (non-nix). +-- Default global toggle is tb, which collides with the terminal map +-- (toggle bracketed paste), so it is moved to the calendar/tasks group c. +local Config = require('config') + +if not Config.isNixCats then + local later = MiniDeps.later + later(function() + MiniDeps.add({ source = 'atiladefreitas/bloocky' }) + end) +end + +local nix = require('config.nix') +local later = MiniDeps.later + +later(function() + if not nix.get_cat('general', false) then + return + end + require('bloocky').setup({ + keymaps = { + toggle = 'cb', + }, + }) +end) \ No newline at end of file diff --git a/plugin/30_dooing.lua b/plugin/30_dooing.lua new file mode 100644 index 0000000..0ec1a09 --- /dev/null +++ b/plugin/30_dooing.lua @@ -0,0 +1,28 @@ +-- Dooing: minimalist todo list manager with a floating window, persisted to JSON. +-- Loaded via the `general` cat spec (nix mode) or MiniDeps (non-nix). +-- Default globals td / tN occupy the terminal group, so the +-- todo toggles are moved to the calendar/tasks group c. +local Config = require('config') + +if not Config.isNixCats then + local later = MiniDeps.later + later(function() + MiniDeps.add({ source = 'atiladefreitas/dooing' }) + end) +end + +local nix = require('config.nix') +local later = MiniDeps.later + +later(function() + if not nix.get_cat('general', false) then + return + end + require('dooing').setup({ + keymaps = { + toggle_window = 'cd', + open_project_todo = 'cD', + show_due_notification = 'cN', + }, + }) +end) \ No newline at end of file diff --git a/tests/smoke.lua b/tests/smoke.lua index e4f0aaf..2e4112e 100644 --- a/tests/smoke.lua +++ b/tests/smoke.lua @@ -55,6 +55,10 @@ local function wait_for_deferred(timeout_ms) -- Fire VimEnter so startup autocmds run, then drain the event loop so -- mini.deps' deferred setup closures have a chance to execute before we -- assert anything. + -- The headless smoke run has no start screen to display. Disabling + -- mini.starter also prevents it from replacing the current buffer and + -- trying to create a swap file in the read-only Nix sandbox. + vim.g.ministarter_disable = true vim.cmd('doautocmd VimEnter') vim.wait(timeout_ms, function() return false end, 50) end @@ -91,8 +95,12 @@ end -- --------------------------------------------------------------------------- local function has_lsp_config(name) if vim.lsp and vim.lsp.config then - -- vim.lsp.config(name) returns the merged config or {} if none registered. - local ok, cfg = pcall(vim.lsp.config, name) + -- Neovim 0.12 exposes configs through the callable `vim.lsp.config` + -- table; older versions expose a function-like API. + local ok, cfg = pcall(function() return vim.lsp.config[name] end) + if not ok or type(cfg) ~= 'table' or next(cfg) == nil then + ok, cfg = pcall(vim.lsp.config, name) + end if ok and cfg and next(cfg) ~= nil then return true end From 25a88599c11f6d7a99b2906afddbada8d197e623 Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Fri, 7 Aug 2026 14:33:48 +1000 Subject: [PATCH 07/13] bloocky config and fixes --- flake.nix | 6 ++++++ plugin/03_terminal.lua | 2 +- plugin/24_completion.lua | 1 - plugin/29_bloocky.lua | 19 ++++++++++++++++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 53117d5..00ad477 100644 --- a/flake.nix +++ b/flake.nix @@ -242,6 +242,12 @@ ''; smoke-test = pkgs.runCommand "smoke-test" {} '' + # The Nix build sandbox has a read-only HOME; point XDG dirs at a + # writable location so vim.lsp/shaDa can write state headlessly. + export XDG_CONFIG_HOME=$TMPDIR/xdg-config + export XDG_STATE_HOME=$TMPDIR/xdg-state + export XDG_CACHE_HOME=$TMPDIR/xdg-cache + export XDG_DATA_HOME=$TMPDIR/xdg-data BINARY_PATH="${defaultNvimPkg}/bin/vv" "$BINARY_PATH" --headless -c "luafile ${./tests/smoke.lua}" -c "qa!" touch $out diff --git a/plugin/03_terminal.lua b/plugin/03_terminal.lua index 95d7a29..43a53a8 100644 --- a/plugin/03_terminal.lua +++ b/plugin/03_terminal.lua @@ -18,7 +18,7 @@ local defaults = { duckdb = "duckdb", julia = "julia", python = "ipython", - shell = "echo 'Hello " .. vim.env.USER .. "!'", + shell = "echo 'Hello " .. (vim.env.USER or "user") .. "!'", } -- Registry of terminal commands diff --git a/plugin/24_completion.lua b/plugin/24_completion.lua index 38e57f2..9e70b27 100644 --- a/plugin/24_completion.lua +++ b/plugin/24_completion.lua @@ -29,7 +29,6 @@ end local function get_blink_fuzzy_setting() return { sorts = { "exact", "score", "sort_text" }, - use_frecency = true, use_proximity = true, } end diff --git a/plugin/29_bloocky.lua b/plugin/29_bloocky.lua index ec15b5d..5e6a3ea 100644 --- a/plugin/29_bloocky.lua +++ b/plugin/29_bloocky.lua @@ -19,8 +19,25 @@ later(function() return end require('bloocky').setup({ + week_start = "monday", + window = { + -- Width per view: fraction of the editor width (or absolute columns if > 1). + -- A single number applies to every view. + width = { + month = 0.99, + week = 0.99, + day = 0.8, + }, + border = "rounded", + }, + integrations = { + dooing = { + enabled = true, -- show Dooing todos on their due date + show_done = false, -- also show completed todos + }, + }, keymaps = { toggle = 'cb', }, }) -end) \ No newline at end of file +end) From c7543c978abbda8028369d4414d853a2e465de92 Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Fri, 7 Aug 2026 15:00:50 +1000 Subject: [PATCH 08/13] OMP neovim server bridge --- .omp/tools/nvim_buffers.mjs | 185 ++++++++++++++++++++++++++++++++++++ lua/nvim_omp/init.lua | 55 +++++++++++ plugin/31_nvim_omp.lua | 8 ++ 3 files changed, 248 insertions(+) create mode 100644 .omp/tools/nvim_buffers.mjs create mode 100644 lua/nvim_omp/init.lua create mode 100644 plugin/31_nvim_omp.lua diff --git a/.omp/tools/nvim_buffers.mjs b/.omp/tools/nvim_buffers.mjs new file mode 100644 index 0000000..82e9ce6 --- /dev/null +++ b/.omp/tools/nvim_buffers.mjs @@ -0,0 +1,185 @@ +// omp custom tool: read this machine's running Neovim buffers over its RPC +// socket. Read-only and on-demand — the model calls it when it needs buffer +// content; nothing is injected into prompts automatically. +// +// Install options +// 1. Project scope (this project): keep the file in .omp/tools/ — omp picks it +// up when a session's cwd is inside this repository. +// 2. All projects: mkdir -p ~/.omp/agent/tools +// ln -s "$PWD/.omp/tools/nvim_buffers.mjs" ~/.omp/agent/tools/ +// then restart `omp` (custom tools are loaded at session bootstrap). +// +// Socket contract (must match lua/nvim_omp/init.lua): +// path = $NVIM_OMP_SOCKET or /nvim/omp.sock +// The nvim instance running this config starts the listener at startup. +// Only one instance can own the socket; a second instance never replaces a +// live owner. Remove a stale socket manually only after confirming no nvim +// process owns it. + +import os from "node:os"; +import path from "node:path"; + +// Safe default cap so a large buffer cannot flood the conversation. +const DEFAULT_MAX_LINES = 2000; + +export default function (pi) { + // Resolve socket path with the same rule as lua/nvim_omp/init.lua. + const socketPath = () => { + const env = process.env.NVIM_OMP_SOCKET; + if (env) return env; + const state = + process.env.XDG_STATE_HOME || + path.join(os.homedir(), ".local", "state"); + return path.join(state, "nvim", "omp.sock"); + }; + + // Evaluate expr in the remote nvim via `nvim --server`; returns parsed JSON. + const rpc = async (expr, signal) => { + const bin = process.env.NVIM_BIN || "nvim"; + const { code, stdout, stderr, killed } = await pi.exec( + bin, + ["--server", socketPath(), "--remote-expr", expr], + { signal }, + ); + if (killed) throw new Error("cancelled"); + if (code !== 0) { + const why = String(stderr || "").trim(); + throw new Error( + why.includes("E247") + ? `No Neovim RPC socket at ${socketPath()}. ` + + "Start nvim (or vv) first — the 31_nvim_omp.lua plugin binds the socket at startup." + : `nvim RPC failed (${code}): ${why}`, + ); + } + const text = String(stdout ?? "").trim(); + if (!text) throw new Error("empty response from nvim RPC"); + return JSON.parse(text); + }; + + // Resolve a buffer argument (number, string, or partial path) to a buffer + // number. Returns null when nothing matches. + const resolveBuf = async (buffer, signal) => { + if (buffer === undefined || buffer === null || buffer === "") { + return rpc("json_encode(nvim_get_current_buf())", signal); + } + if (typeof buffer === "number") return buffer; + const bufnum = Number(buffer); + if (Number.isInteger(bufnum) && bufnum > 0) return bufnum; + const list = await rpc( + "json_encode(map(nvim_list_bufs(), {i, v -> " + + "{'bufnr': v, 'name': nvim_buf_get_name(v)}}))", + signal, + ); + const q = String(buffer); + const hit = + list.find( + (b) => b.name === q || b.name.endsWith("/" + q) || b.name.endsWith(q), + ) || + list.find((b) => b.name.includes(q)); + return hit ? hit.bufnr : null; + }; + + const tools = []; + + // List open buffers ----------------------------------------------- + tools.push({ + name: "nvim_buffers", + label: "Neovim Buffers", + description: + "List the buffers currently open in the user's running Neovim " + + "(number, name, current/loaded state, modified). Use before " + + "nvim_buffer to pick a buffer.", + parameters: pi.arktype({}), + async execute(_id, _params, _onUpdate, _ctx, _signal) { + const list = await rpc( + "json_encode(map(nvim_list_bufs(), {i, v -> {'nr': v, " + + "'name': nvim_buf_get_name(v), " + + "'current': v == nvim_get_current_buf(), " + + "'loaded': nvim_buf_is_loaded(v), " + + "'modified': getbufvar(v, '&modified')}}))", + ); + if (!list.length) { + return { content: [{ type: "text", text: "No buffers open." }] }; + } + const lines = list.map( + (b) => + `${b.nr}\t${b.name || "[no name]"}\t` + + `${b.current ? "current " : ""}` + + `${b.loaded ? "" : "unloaded "}` + + `${b.modified ? "modified" : ""}`.trim(), + ); + return { + content: [ + { + type: "text", + text: `Open buffers (${list.length}):\n` + lines.join("\n"), + }, + ], + }; + }, + }); + + // Read a single buffer, line-capped ------------------------------- + tools.push({ + name: "nvim_buffer", + label: "Neovim Buffer", + description: + "Read lines of a Neovim buffer. `buffer` accepts the current buffer " + + "(default), a buffer number from nvim_buffers, or a file name/path " + + "open in Neovim. `maxLines` caps the returned lines (default 2000); " + + "pass a larger value explicitly to read more of a long buffer.", + parameters: pi.arktype({ + buffer: "string? | number?", + maxLines: "number?", + }), + async execute(_id, params, _onUpdate, _ctx, signal) { + const buf = await resolveBuf(params.buffer, signal); + if (buf === null) { + return { + content: [ + { + type: "text", + text: `No buffer matches '${params.buffer}'. List open buffers with nvim_buffers.`, + }, + ], + }; + } + const maxLines = + Number.isInteger(params.maxLines) && params.maxLines > 0 + ? Math.min(params.maxLines, 100000) + : DEFAULT_MAX_LINES; + + const expr = + `json_encode({'name': nvim_buf_get_name(${buf}), ` + + `'total': nvim_buf_line_count(${buf}), ` + + `'lines': nvim_buf_get_lines(${buf}, 0, ${maxLines}, 0)})`; + const { name, total, lines } = await rpc(expr, signal); + if (!total) { + return { + content: [ + { type: "text", text: `Buffer ${buf} (${name}) is empty.` }, + ], + }; + } + const truncated = + total > lines.length + ? `\n... ${total - lines.length} more lines ` + + `(raise maxLines to read them)` + : ""; + const numbered = lines.map((l, i) => `${i + 1}: ${l}`).join("\n"); + return { + content: [ + { + type: "text", + text: + `Buffer ${buf} (${name}), ${total} lines:\n` + + numbered + + truncated, + }, + ], + }; + }, + }); + + return tools; +} \ No newline at end of file diff --git a/lua/nvim_omp/init.lua b/lua/nvim_omp/init.lua new file mode 100644 index 0000000..4400d49 --- /dev/null +++ b/lua/nvim_omp/init.lua @@ -0,0 +1,55 @@ +-- omp bridge: expose an RPC socket so the omp harness can read this Neovim +-- instance's buffers on demand. Read-only on the agent side; this module only +-- owns the socket lifecycle. +-- +-- Contract +-- * Socket path: $NVIM_OMP_SOCKET if set, else /omp.sock +-- (~/.local/state/nvim/omp.sock on macOS, ~/.local/state/nvim/omp.sock +-- elsewhere). The omp-side tool in .omp/tools/nvim_buffers.mjs resolves the +-- *same* path, so the two sides agree without configuration. +-- * The agent reads buffers by evaluating read-only nvim API expressions over +-- the socket with `nvim --server --remote-expr 'json_encode(...)'`. +-- Nothing here writes buffers or executes model-supplied commands. +local M = {} + +local SOCKET_NAME = "omp.sock" + +-- Resolve the deterministic socket path. Copies the rule on the omp side; keep +-- the two files in sync when changing the fallback or env override. +function M.socket_path() + local env = vim.env.NVIM_OMP_SOCKET + if env and env ~= "" then + return env + end + return vim.fn.stdpath("state") .. "/" .. SOCKET_NAME +end + +-- Start the RPC listener. Returns the live socket path, or nil. +-- A second instance cannot bind the same address. This function deliberately +-- never removes an existing socket file: a failed liveness probe must not +-- disconnect another live Neovim instance. +-- If a crash leaves a stale socket, remove it manually only after confirming +-- that no Neovim process owns the path, then restart vv. +function M.start() + if vim.v.headless == 1 then + -- Headless runs (tests, CI) get no socket; nothing should depend on one. + return nil + end + + local path = M.socket_path() + local ok, res = pcall(vim.fn.serverstart, path) + if ok and type(res) == "string" and res ~= "" then + -- serverstart returns the bound address string (e.g. "/tmp/omp.sock"). + vim.notify("nvim_omp: RPC socket ready at " .. path, vim.log.levels.INFO) + return path + end + + vim.notify( + "nvim_omp: could not bind RPC socket at " .. path + .. "; another instance may own it or a stale socket needs manual cleanup.", + vim.log.levels.WARN + ) + return nil +end + +return M diff --git a/plugin/31_nvim_omp.lua b/plugin/31_nvim_omp.lua new file mode 100644 index 0000000..44076cf --- /dev/null +++ b/plugin/31_nvim_omp.lua @@ -0,0 +1,8 @@ +-- omp bridge entrypoint: start the RPC socket as early as possible so the omp +-- harness can read this instance's buffers on demand (see lua/nvim_omp/init.lua +-- and .omp/tools/nvim_buffers.mjs). Safe to fail silently — the socket is a +-- convenience, not a dependency of the editor. +local ok, nvim_omp = pcall(require, "nvim_omp") +if ok then + nvim_omp.start() +end \ No newline at end of file From b630932348d8b16e8f186584d1256f7a9c2002c9 Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Tue, 11 Aug 2026 12:33:49 +1000 Subject: [PATCH 09/13] Fixed dooing keymap --- plugin/30_dooing.lua | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/plugin/30_dooing.lua b/plugin/30_dooing.lua index 0ec1a09..f47ecc2 100644 --- a/plugin/30_dooing.lua +++ b/plugin/30_dooing.lua @@ -13,16 +13,32 @@ end local nix = require('config.nix') local later = MiniDeps.later - +local now = MiniDeps.now later(function() if not nix.get_cat('general', false) then return end - require('dooing').setup({ + local dooing = require('dooing') + require("dooing").setup({ keymaps = { - toggle_window = 'cd', - open_project_todo = 'cD', - show_due_notification = 'cN', + toggle_window = "cd", + open_project_todo = "cD", + show_due_notification = "cN", + create_nested_task = "cn", -- Create nested subtask under current todo + toggle_priority = "a", + }, + calendar = { + week_start_day = "monday", }, }) -end) \ No newline at end of file + -- Remove Dooing's old defaults. + vim.keymap.del("n", "td") + vim.keymap.del("n", "tD") + vim.keymap.del("n", "tN") + + -- Restore mappings overwritten by Dooing. + local helpers = require('keymap.helpers') + local nmap_leader = helpers.nmap_leader + -- t is for 'terminal' + nmap_leader("td", 'lua Config.terminal.open_duckdb();Config.terminal.toggle_bracket()', 'Open DuckDB') +end) From a4bae164c38e4309e02b9749abefe07ae9911b75 Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Wed, 12 Aug 2026 08:49:32 +1000 Subject: [PATCH 10/13] Moved pyarrow overlay to overlays, cleanup --- .../1784851829193-codecompanion-revamp.md | 85 -------- .../plans/1784858731550-nix-neovim-review.md | 191 ------------------ .letta/settings.local.json | 17 -- .omp/tools/nvim_buffers.mjs | 185 ----------------- flake.lock | 24 +-- flake.nix | 4 +- lua/keymap/leader.lua | 3 +- overlays/python.nix | 12 +- plugin/01_lib.lua | 66 ++++++ plugin/29_bloocky.lua | 43 ---- plugin/30_dooing.lua | 44 ---- plugin/31_nvim_omp.lua | 8 - plugin/32_checkbox.lua | 71 +++++++ result | 1 + 14 files changed, 165 insertions(+), 589 deletions(-) delete mode 100644 .kilo/plans/1784851829193-codecompanion-revamp.md delete mode 100644 .kilo/plans/1784858731550-nix-neovim-review.md delete mode 100644 .letta/settings.local.json delete mode 100644 .omp/tools/nvim_buffers.mjs delete mode 100644 plugin/29_bloocky.lua delete mode 100644 plugin/30_dooing.lua delete mode 100644 plugin/31_nvim_omp.lua create mode 100644 plugin/32_checkbox.lua create mode 120000 result diff --git a/.kilo/plans/1784851829193-codecompanion-revamp.md b/.kilo/plans/1784851829193-codecompanion-revamp.md deleted file mode 100644 index b5898e4..0000000 --- a/.kilo/plans/1784851829193-codecompanion-revamp.md +++ /dev/null @@ -1,85 +0,0 @@ -# CodeCompanion Revamp Plan - -## Context - -- Current: CodeCompanion **19.18.0** via nixpkgs `vimPlugins.codecompanion-nvim` (nixpkgs input lastModified ~2026-05-15). -- Latest: **19.20.0**. Key changes since 19.18.0: - - v19.19.0: `claude-sonnet-5` support, async/dynamic model fetching, copilot `top_p` fixes, inline orphaned-keymap fix, background command deregistration. - - v19.20.0: `gemini_interactions` adapter, PDF support for http adapters (`/file` on Anthropic/Copilot/OpenAI/OpenRouter), env vars from files, prompt-library items auto-receive default rule groups, copilot schema options removed. -- **Bug in current setup:** config sets `model = "claude-sonnet-5"` but 19.18.0 predates its support; the model name may not resolve. The nixpkgs bump fixes this. -- Files involved: - - `plugin/24_completion.lua` — `get_codecompanion_config()`, setup, blink integration. - - `plugin/10_keymap.lua` — `a*` keymaps (lines 69–84, contains duplicates/commented cruft). - - `overlays/plugins.nix` — codecompanion overlay (nvimSkipModules); likely unchanged. - - `modules/module/specs/plugins.nix` — plugin spec; unchanged. - -## Decisions (confirmed with user) - -1. **Version bump:** `nix flake update nixpkgs` (accepts wider plugin bump; do NOT override src). -2. **Adopt:** agent-mode keymaps, one workflow prompt, minor slash-command config (`/share` token). **No MCP.** -3. **Models:** `claude-sonnet-5` (copilot) for **chat only**; cheaper copilot model for **inline** and **background** (title generation) — use `gpt-5-mini` as placeholder; verify exact model id via `ga` model picker in a chat buffer after the bump and adjust. -4. **Codex (ChatGPT Edu) = heavy agent lane.** Copilot stays the default chat adapter; Codex ACP is launched explicitly for heavy autonomous tasks. Auth via ChatGPT login only (no API key) — user's existing `auth_method = "chatgpt"` is correct per the v19.20.0 adapter source (`"openai-api-key"|"codex-api-key"|"chatgpt"`; the docs page comment `"chat-gpt"` is stale). - -## Tasks - -1. **Bump nixpkgs** - - Run `nix flake update nixpkgs`. - - Verify: `nix eval --raw nixpkgs#vimPlugins.codecompanion-nvim.version` reports ≥ 19.20.0. - - Rebuild the wrapped Neovim per this repo's normal build (`nix build` / the repo's usual package target) and smoke-test that the editor starts. - -2. **`plugin/24_completion.lua` — `get_codecompanion_config()`** - - Keep `interactions.chat.adapter = { name = "copilot", model = "claude-sonnet-5" }`. - - Change `interactions.inline.adapter` to `{ name = "copilot", model = "gpt-5-mini" }` (cheap/fast). - - Add a background adapter so title generation doesn't use Sonnet: set the background chat adapter to copilot/`gpt-5-mini` (config lives under `interactions.background.chat`; confirm exact key against `:h codecompanion` / `:checkhealth codecompanion` after the bump — the docs page "Generating Titles" says an adapter must be configured for background interactions). - - Keep existing context_management, rules, display, shared keymaps unchanged. - - Add `interactions.chat.slash_commands["share"].opts.token = os.getenv("GITHUB_GIST_TOKEN")`. - - **Prompt library additions** (keep `expert`, `fixer`, `suggest`): - - `["agent"]`: interaction "chat", alias `agent`, first user prompt starts with `@{agent}` plus selected code block, so Copilot/Claude gets the file-editing tool group (read_file, insert_edit_into_file, grep_search, run_command, etc., with approvals). - - `["tdd"]` (name flexible): interaction "chat", `opts = { is_workflow = true, alias = "tdd" }` — 3-stage workflow: (1) plan/understand `#buffer`, (2) `@{agent}` implement, (3) `@{run_command}` run the test suite (leverages run_command's test-flag for agentic workflows). - -3. **`plugin/10_keymap.lua` — clean up + add** - - Remove duplicated/commented lines (70–71, 77, 81). - - Add: - - `nmap_leader("aa", "CodeCompanionChat", "Agent chat (use @{agent})")` or directly `CodeCompanion /agent`. - - `xmap_leader("aa", "CodeCompanion /agent", "Agent on selection")`. - - `nmap_leader("aw", "CodeCompanion /tdd", "Workflow: plan→implement→test")`. - - `nmap_leader("aC", "CodeCompanion /compact", "Compact chat")` (verify `aC` doesn't clash). - - Keep existing mappings unchanged. - -4. **No changes** to `overlays/plugins.nix` or `modules/module/specs/plugins.nix` unless the new version introduces new lazy-module load failures (re-run the build's check phase; `doCheck = false` is already set on the spec entry). - -5. **Codex ACP lane (ChatGPT Edu)** - - **Resolved prerequisite:** user has installed `codex-acp` at `~/.nix-profile/bin/codex-acp` (verified on PATH 2026-07-24). The preset adapter's default command (`codex-acp`) now works as-is — **no `commands` override in the Lua config**. Fallback only if the binary misbehaves: override with `commands = { default = { "codex", "acp" } }` (requires the codex CLI's built-in `acp` subcommand; verify with `codex acp --help`). - - **Config change (small):** none strictly required — the existing `extend("codex", { defaults = { auth_method = "chatgpt" } })` block in `plugin/24_completion.lua` is correct. Keep it. - - Auth: prereq is an active `codex login` session with the ChatGPT Edu account (`~/.codex/auth.json` exists — user confirms validity; if expired, re-run `codex login`, browser/ChatGPT-app flow, no API key). - - Keep codex on-demand (do NOT make it the default chat adapter): - - Keep `ak` (`:CodeCompanionChat adapter=codex`). - - Add comment documenting ACP-only slash commands for this lane: `/resume` (restore past codex session, fresh chat only), `/mode` (switch agent mode), `/command`, `/acp_session_options`, plus `\`-triggered ACP command completion in the chat buffer. - - Do not set a default codex model in config; pick per-session via `ga` / `/acp_session_options` (avoids hardcoding model ids that change with the Edu plan). - - PATH note: Neovim must inherit a PATH containing `~/.nix-profile/bin` so the spawned `codex-acp` resolves — true when nvim is launched from the user's normal shell; call it out if validation fails with "command not found". - -6. **Docs/habit notes** (add as comment block above the codecompanion setup, no separate docs file): `/compact`, `/fork`, `/symbols`, `/share`, `/resume`+`/mode` (codex ACP), `gm` (btw), `gty` (YOLO), `gba`/`gbd` (buffer sync), `gd` (debug window). - -## Validation - -- `nix flake update nixpkgs` then repo build succeeds. -- In Neovim: `:checkhealth codecompanion` clean. -- `:CodeCompanionChat`, press `ga` → copilot adapter lists `claude-sonnet-5`; send a trivial message and confirm response + auto title generation. -- In chat: type `@` → completion shows `agent`, `files`, `memory`, etc.; run `@{agent}` task on a scratch repo and confirm the approval prompt flow works. -- `:CodeCompanion /tdd` starts the workflow stages in order. -- Inline: visual-select code, `:CodeCompanion` prompt → confirm diff shows and `ga`/`gr` accept/reject still work on the cheap model. -- `:CodeCompanion /share` prompts/errors sensibly if `GITHUB_GIST_TOKEN` unset. -- **Codex lane:** `ak` opens a chat with the codex ACP adapter; the spawned `codex-acp` process initializes without auth errors (ChatGPT method), a trivial prompt gets a response, and `/mode` lists codex session modes. If it fails with command-not-found, check that Neovim inherited `~/.nix-profile/bin` in PATH (`:echo $PATH` inside nvim). - -## Risks - -- `nix flake update nixpkgs` bumps **all** vimPlugins and Neovim itself; other plugins may break. If the blast radius is too large, fall back to overriding codecompanion `src` to tag `v19.20.0` in `overlays/plugins.nix` (fetchFromGitHub, `doCheck = false`). -- Model ids (`claude-sonnet-5`, `gpt-5-mini`) must match the copilot adapter's choices post-bump; verify via `ga` picker and adjust literals. -- `gpt-5-mini` may not support tool use on copilot — fine, since inline/background don't use tools. -- `codex-acp` lives in the user's nix profile (outside this flake). If the profile is rebuilt/removed, the codex lane breaks — long-term consider adding `codex-acp` to this flake's runtime deps so it's pinned with the rest of the setup (optional follow-up, not required now). -- ChatGPT Edu accounts authenticate codex via browser/ChatGPT-app login; token expiry will surface as ACP auth errors → re-run `codex login`. - -## Out of scope - -- MCP server integration. -- Custom rules parsers, custom tools, extensions (mcphub/history/vectorcode). diff --git a/.kilo/plans/1784858731550-nix-neovim-review.md b/.kilo/plans/1784858731550-nix-neovim-review.md deleted file mode 100644 index d0f0c63..0000000 --- a/.kilo/plans/1784858731550-nix-neovim-review.md +++ /dev/null @@ -1,191 +0,0 @@ -# Neovim + Nix setup review - -Scope: `flake.nix`, `overlays/`, `modules/`, `plugin/*.lua`, `lua/*`, `ftplugin/*`, CI/workflows, and cross-check against the pinned versions in `flake.lock` (nixpkgs weekly `241313f4`). - -## Pinned package versions (from current lock) -- neovim `0.12.2` -- blink-cmp `1.10.2` -- codecompanion-nvim `19.13.0` -- nvim-treesitter `0.10.0-unstable-2026-04-03` (main-branch rewrite) -- nvim-treesitter-textobjects `0-unstable-2026-04-07` (main-branch rewrite) -- copilot-lua `2.0.3` -- render-markdown-nvim `8.12.0-unstable-2026-05-07` -- zk-nvim `0.4.7-unstable-2026-03-13` -- mini.nvim `0.17.0-unstable-2026-05-12` -- quarto-nvim `2.1.0` -- otter-nvim `2.14.5` -- lspconfig `2.9.0` - ---- - -## CRITICAL: Treesitter is currently non-functional - -### Finding -The nixpkgs `nvim-treesitter` package is the **main-branch rewrite** — there is no `lua/nvim-treesitter/configs.lua`. The whole module in `plugin/20_startup.lua` lines 212-294 is dead code: - -```lua -local ok_configs, configs = pcall(require, "nvim-treesitter.configs") -- fails -``` - -The working tree already removed the fallback `vim.treesitter.start()` FileType autocmd (the "fixed treesitter" commit deleted it). Net result: **no treesitter highlighting, indentexpr, foldexpr, or textobjects are configured**. - -### Impact -- Syntax highlighting falls back to Neovim's regex-only engine. -- All textobject keymaps (`]a`, `[a`, `]f`, `[f`, `]e`/`[e`, `x`/`X`, lsp_interop `lm`) are inert. -- `foldexpr`/`indentexpr` based folding does nothing. - -### Fix direction -Rewrite `plugin/20_startup.lua` for main-branch API: -1. FileType autocmd → `vim.treesitter.start()` (and optionally `indentexpr`/`foldexpr`). -2. `require("nvim-treesitter-textobjects").setup({ move = { set_jumps = true } })` + explicit keymaps using `move.goto_next_start(query, "textobjects")` and `swap.swap_next(...)`. -3. The non-Nix `ensure_installed` block should set `opts.ensure_installed` BEFORE the filter (line 278 reads it before line 286 defines it). - -Also remove duplicate `vim.treesitter.language.register("markdown", ...)` between `plugin/20_startup.lua` and `plugin/21_datascience.lua`. - ---- - -## CodeCompanion version mismatch - -### Finding -Config targets features absent in `19.13.0` (present only in `≥19.19.0`/main): - -| Config usage | Present in 19.13? | Present in main (`v19.20.0`) | -|---|---|---| -| `interactions.chat.opts.context_management.editing` | No (flat `trigger`/`enabled` only) | Yes | -| `interactions.chat.opts.context_management.compaction` | No | Yes | -| `aC` requiring `codecompanion.interactions.chat.context_management.compaction` | **RUNTIME ERROR** | Works | -| `slash_commands.share` (`opts.token = ...`) | No (absent from defaults) | Yes | -| `adapters.acp.codex.defaults.auth_method = "chatgpt"` | Yes | Yes | -| `interactions.chat.adapter = { name = ..., model = ... }` table form | Yes | Yes | - -### Risk -On today's lock (`19.13.0`): `/share` is inert, compaction-only keymap throws module-not-found, and `editing`/`compaction` tuning is silently ignored. On `≥19.19.0`/main: everything works. - -### Decision needed (BLOCKING) -Choose **exactly one**: - -- **A — Upgrade the plugin pin** (`overlays/plugins.nix` fetch to `v19.20.0` or `main`, bump the lock, and keep the current config). Riskiest change but matches what the config is written for. -- **B — Downgrade config call sites** to match `19.13.0`: remove `slash_commands.share`, remove `editing`/`compaction` keys (or collapse to flat `trigger: 0.75`), remove or gate `aC` on "newer" version. -- **C — Version-gate the config**: keep current code, read the installed version at startup and skip the new features when < 19.19. - -I recommend A (the config clearly intends to track upstream main-ish behavior; `nix flake update` already bumped it to `19.18.0` in a prior session and you only reverted because the nightly was still too old — `v19.20.0` is now available). - ---- - -## DEAD / DUPLICATE code - -- `plugin/20_startup.lua` — the entire `configs.setup(opts)` block is dead (see Treesitter finding above). -- `plugin/10_keymap.lua`: - - `nmap_leader('od', 'Neogen', ...)` — `neogen` plugin is **not shipped** in `specs/plugins.nix`. Mapping errors on press. - - `nmap_leader('fp', 'Pick projects', ...)` — `MiniExtra.pickers.projects` doesn't exist in mini.extra; would error. - - `nmap_leader('oS', 'lua Config.insert_section()', ...)` — `Config.insert_section` is never defined. - - `vim.lsp.buf.definition()` is bound to `grd`; Neovim 0.11+ defaults include `gr` aliases. Cosmetic, but `]d`/`[d` exist on modern LSP config and would be more idiomatic. -- `plugin/23_editor.lua`: - - `my_styler` formatter (calls `R -s -e styler::...`) is defined but never referenced — dead. -- `plugin/24_completion.lua`: - - `providers.cmp_r` is defined in the blink source list but never enabled in `default` or `per_filetype`; inert. - - `BLINK_VERSION = "v1.4.1"` — only consulted in the non-Nix install path; nixpkgs is `1.10.2`. Pin it to current or drop. - - `get_blink_fuzzy_setting().prebuilt_binary = { force_version = BLINK_VERSION }` — singular key is wrong; blink option is `fuzzy.prebuilt_binaries` (plural). Being skipped in-Nix anyway, but still wrong key. -- `modules/module/specs/plugins.nix` — `specs.utils-lazy` ships `nvim-dap*` and `nvim-lint` but there is zero config/tooling that references them in this repo. -- `ftplugin/quarto.lua`: - - Second top-level `require('quarto').setup()` with **no args** runs _after_ `21_datascience.lua`'s setup and **resets** it to defaults (no `lspFeatures`, no `codeRunner`). - - Top-level `require('quarto')` at FileType load also crashes if the quarto plugin isn't installed (e.g. cats off and a `.qmd`/`.quarto` file is opened): Neovim detects `quarto` ft natively in 0.10+. -- `plugin/00_options.lua`: - - Lines 146-163: two back-to-back FileType autocmds that both remove `r`/`o` from `formatoptions`. The second references an undefined `augroup` variable (nil → autocmd is global, happens to still work). - ---- - -## STALE / WRONG options - -- `plugin/10_keymap.lua` line 174: - ```lua - require("conform").format({ lsp_fallback = true }) - ``` - `lsp_fallback` is the **deprecated** boolean form of `conform.nvim`; should be `lsp_format = "fallback"` (the form already used in `plugin/23_editor.lua` and `ftplugin/python.lua`). In newer conform this may warn or error. -- `ftplugin/quarto.lua`: - - Sets `RDSendLine` and R-style keymaps on _all_ quarto buffers, including Python/Julia chunks. This collides with quarto-runner mappings set in `21_datascience.lua`. Should gate on `vim.bo.filetype == "r"` (the ftplugin already imports quarto.runner for python, but the R keys leak). -- `.github/dependapot.yml` — filename typo. GitHub expects `dependabot.yml`; Dependabot won't run. -- `.github/workflows/check.yml`: - - `nix develop` without a `-c` command just launches an interactive shell. In CI it does nothing useful (or hangs). Replace with `nix develop -c echo ok` or drop. - - Path filter `'modules'` only matches the root directory itself; should be `modules/**` (or `'modules/**'`). - - Changing `plugin/`, `lua/`, `overlays/`, `ftplugin/`, etc. does **not** trigger CI. -- `flake.nix` / `.envrc` shellHook: - ```sh - export R_LIBS_SITE=$(strings "$(command -v R)" | grep -oP '/nix/store/[^:]+/library' ...) - ``` - `grep -oP` (PCRE) is **not available in macOS BSD grep**. On `aarch64-darwin` this silently fails → `R_LIBS_SITE` is empty. Either depend on `ripgrep` regex (`strings ... | rg -o '...'`) or use `gsed` (GNU sed). -- `overlays/plugins.nix` — `zk-nvim` skip list contains `zk.pickers.fzf_lua`; current zk-nvim package likely doesn't load that regardless, but harmless. -- `modules/module/settings/core.nix` — `config.settings.nvim_lua_env` references `lp.tiktoken_core` but `tiktoken_core` is **not in `catPkgs.general` or anywhere else** in these files. If there's an extra Lua/tiktoken module, fine; otherwise this option is placeholder dead code. -- `modules/module/settings/hosts.nix` — host `m` (marimo) is defined with `enable = false` but also configured with `package`, `argv0`, `addFlag`. Dead block. - ---- - -## PORTABILITY / DARWIN issues - -- Shell hook `grep -oP` (above) fails on macOS. -- `mkdir -p "$R_LIBS_USER"` fine; but `command -v R` on macOS returns the wrapper; the wrapper path injection still works. - ---- - -## DEAD WEIGHT (shipped but unused) - -- `catPkgs.r` includes `pkgs.rnvimserver` — `rnvimserver` is needed by R.nvim only when using the _socket_ transport; `21_datascience.lua` only uses vim-slime. Acceptable, but `rnvimserver` adds to build time. Include if you actually use it? Currently not used. -- `specs.utils-lazy` ships `nvim-dap`, `nvim-dap-ui`, `nvim-dap-virtual-text`, `nvim-lint` — none of these are referenced anywhere in `plugin/` or `lua/`. Consider moving them to a devShell-only cat, or drop them. -- `.gitignore` `*.R` — prevents tracking any new `.R` files. `tests/test.R` is already committed so it isn't actively harmful, but it's surprising for a repo whose default cats include R. -- `.commandcode/` dir is listed in `.gitignore` but is in the worktree; fine, but worth cleaning up if it's an artifact. - ---- - -## IMPROVEMENTS given updated packages - -- `catPkgs.markdown` — add `marksman` (it's the LSP used by `render-markdown.nvim` wiki links and configured in LSP). Same binary is needed by `render-markdown` for wiki link ISP. -- `vim.lsp.enable` servers configured but binaries missing in PATH: - - `marksman` (LSP + render-markdown wiki integration). - - `r_ls` (new R language server — package name in nixpkgs is likely `r-languageserver` or `r_ls`; current `catPkgs.r` doesn't ship either). - - `clangd` — not required for this data-science setup; either drop or add to `external`. - - `julials` — require `LanguageServer.jl` in `settings.lang_packages.julia` for it to be useful. -- `blink.cmp` is `1.10.2` but config pins `BLINK_VERSION = "v1.4.1"` for the non-Nix path. Either drop the `BLINK_VERSION` constant (let MiniDeps track HEAD or the tag pinned in flake) or update it to `v1.10.2`. -- `conform.nvim` already uses `lsp_format = "fallback"` in `23_editor.lua` and `ftplugin/python.lua`, but `10_keymap.lua` still calls the deprecated `lsp_fallback = true`. Align to `lsp_format = "fallback"`. -- `.github/workflows/check.yml` — add path filters for `plugin/**`, `lua/**`, `overlays/**`, `ftplugin/**`, `modules/**`. -- `tests/init.lua` smoke-test and `tests/test.R` are present but **not wired into `nix flake check`**. Add a trivial check that runs `lua tests/init.lua` via `nix-shell -A ...`. - ---- - -## CONFLICTS - -- `up` (terminal bracketed paste toggle in `10_keymap.lua`) conflicts with `mini.basics.mappings.option_toggle_prefix = "u"` (paste toggle), which is set up later in `20_startup.lua` via `now()`. Result: the user's `up` mapping gets overwritten. Portable workaround: remap to `tp` (terminal namespace) and keep `tb` as alternative — both already mapped in 10_keymap. -- `ftplugin/quarto.lua` resets quarto-nvim config (kills `lspFeatures`/`codeRunner` set in `21_datascience.lua`) and injects R plug mappings into non-R quarto chunks. - ---- - -## CLEAN summary for implementer - -### Immediate (not version-dependent) -1. Restore treesitter highlighting + textobjects in `plugin/20_startup.lua` using main-branch API. -2. Remove duplicate `vim.treesitter.language.register` in `21_datascience.lua`. -3. Fix `plugin/10_keymap.lua`: remove/resolve `Neogen`, `Pick projects`, `Config.insert_section` dangling mappings. -4. Replace deprecated `lsp_fallback = true` with `lsp_format = "fallback"` in `plugin/10_keymap.lua`. -5. Gate `ftplugin/quarto.lua` to not reset config and to not leak R keys into non-R chunks; guard against missing `quarto` plugin. -6. Remove duplicate `formatoptions` autocmds (or at least dedupe). -7. Dedupe `RNVIM_COMPLDIR` / `TMPDIR` setup (do it in one place). -8. Rename `.github/dependapot.yml` to `.github/dependabot.yml`. -9. Remove or gate dead host `m` in `hosts.nix`. -10. Remove unused `rsplit`? no, irrelevant. -11. Add `marksman` to `catPkgs.markdown` and remove orphan LSP entries or ship the binaries. -12. Remove dead `conform` formatter `my_styler` and dead blink `cmp_r` provider, or wire them up. - -### Version-dependent (BLOCKED) -- **Reset the CodeCompanion version decision**: either pin to `v19.20.0` (keep config, fix `slash_commands.share`, drop the `aC` compatibility shim, keep `editing`/`compaction`) OR downgrade config to match `19.13.0` (remove `share`, collapse `context_management`). - -### CI / packaging -- Fix `nix develop` usage and path filters in `.github/workflows/check.yml`. -- Replace `grep -oP` in `flake.nix` shellHook with portable `rg -o` or add `gnused` to `catPkgs.always` and use `gsed`. -- Wire `tests/init.lua` into `flake check`. - -### Verification -- `nix flake check` on both `aarch64-darwin` and `x86_64-linux`. -- `nix build .#packages..default` succeeds (already does). -- Start nvim, verify :TSContext works, treesitter highlighting is on, and `]f`/`[f` textobjects move. -- Open a `.qmd` file with cats off → no ftplugin crash. -- Confirm `up` still toggles bracketed paste. -- Confirm `:Pick projects` and `:Neogen` and `Config.insert_section` no longer error (or are mapped to valid handlers). diff --git a/.letta/settings.local.json b/.letta/settings.local.json deleted file mode 100644 index 7656883..0000000 --- a/.letta/settings.local.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "lastAgent": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf", - "sessionsByServer": { - "api.letta.com": { - "agentId": "agent-21b21fd6-49e4-4d5c-a1b7-1c6dcd8dacc9", - "conversationId": "default" - }, - "local:/Users/daniel/.letta/lc-local-backend": { - "agentId": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf", - "conversationId": "default" - } - }, - "lastSession": { - "agentId": "agent-local-096f2785-09c8-4487-b71b-f8ac2b81aaaf", - "conversationId": "default" - } -} \ No newline at end of file diff --git a/.omp/tools/nvim_buffers.mjs b/.omp/tools/nvim_buffers.mjs deleted file mode 100644 index 82e9ce6..0000000 --- a/.omp/tools/nvim_buffers.mjs +++ /dev/null @@ -1,185 +0,0 @@ -// omp custom tool: read this machine's running Neovim buffers over its RPC -// socket. Read-only and on-demand — the model calls it when it needs buffer -// content; nothing is injected into prompts automatically. -// -// Install options -// 1. Project scope (this project): keep the file in .omp/tools/ — omp picks it -// up when a session's cwd is inside this repository. -// 2. All projects: mkdir -p ~/.omp/agent/tools -// ln -s "$PWD/.omp/tools/nvim_buffers.mjs" ~/.omp/agent/tools/ -// then restart `omp` (custom tools are loaded at session bootstrap). -// -// Socket contract (must match lua/nvim_omp/init.lua): -// path = $NVIM_OMP_SOCKET or /nvim/omp.sock -// The nvim instance running this config starts the listener at startup. -// Only one instance can own the socket; a second instance never replaces a -// live owner. Remove a stale socket manually only after confirming no nvim -// process owns it. - -import os from "node:os"; -import path from "node:path"; - -// Safe default cap so a large buffer cannot flood the conversation. -const DEFAULT_MAX_LINES = 2000; - -export default function (pi) { - // Resolve socket path with the same rule as lua/nvim_omp/init.lua. - const socketPath = () => { - const env = process.env.NVIM_OMP_SOCKET; - if (env) return env; - const state = - process.env.XDG_STATE_HOME || - path.join(os.homedir(), ".local", "state"); - return path.join(state, "nvim", "omp.sock"); - }; - - // Evaluate expr in the remote nvim via `nvim --server`; returns parsed JSON. - const rpc = async (expr, signal) => { - const bin = process.env.NVIM_BIN || "nvim"; - const { code, stdout, stderr, killed } = await pi.exec( - bin, - ["--server", socketPath(), "--remote-expr", expr], - { signal }, - ); - if (killed) throw new Error("cancelled"); - if (code !== 0) { - const why = String(stderr || "").trim(); - throw new Error( - why.includes("E247") - ? `No Neovim RPC socket at ${socketPath()}. ` + - "Start nvim (or vv) first — the 31_nvim_omp.lua plugin binds the socket at startup." - : `nvim RPC failed (${code}): ${why}`, - ); - } - const text = String(stdout ?? "").trim(); - if (!text) throw new Error("empty response from nvim RPC"); - return JSON.parse(text); - }; - - // Resolve a buffer argument (number, string, or partial path) to a buffer - // number. Returns null when nothing matches. - const resolveBuf = async (buffer, signal) => { - if (buffer === undefined || buffer === null || buffer === "") { - return rpc("json_encode(nvim_get_current_buf())", signal); - } - if (typeof buffer === "number") return buffer; - const bufnum = Number(buffer); - if (Number.isInteger(bufnum) && bufnum > 0) return bufnum; - const list = await rpc( - "json_encode(map(nvim_list_bufs(), {i, v -> " + - "{'bufnr': v, 'name': nvim_buf_get_name(v)}}))", - signal, - ); - const q = String(buffer); - const hit = - list.find( - (b) => b.name === q || b.name.endsWith("/" + q) || b.name.endsWith(q), - ) || - list.find((b) => b.name.includes(q)); - return hit ? hit.bufnr : null; - }; - - const tools = []; - - // List open buffers ----------------------------------------------- - tools.push({ - name: "nvim_buffers", - label: "Neovim Buffers", - description: - "List the buffers currently open in the user's running Neovim " + - "(number, name, current/loaded state, modified). Use before " + - "nvim_buffer to pick a buffer.", - parameters: pi.arktype({}), - async execute(_id, _params, _onUpdate, _ctx, _signal) { - const list = await rpc( - "json_encode(map(nvim_list_bufs(), {i, v -> {'nr': v, " + - "'name': nvim_buf_get_name(v), " + - "'current': v == nvim_get_current_buf(), " + - "'loaded': nvim_buf_is_loaded(v), " + - "'modified': getbufvar(v, '&modified')}}))", - ); - if (!list.length) { - return { content: [{ type: "text", text: "No buffers open." }] }; - } - const lines = list.map( - (b) => - `${b.nr}\t${b.name || "[no name]"}\t` + - `${b.current ? "current " : ""}` + - `${b.loaded ? "" : "unloaded "}` + - `${b.modified ? "modified" : ""}`.trim(), - ); - return { - content: [ - { - type: "text", - text: `Open buffers (${list.length}):\n` + lines.join("\n"), - }, - ], - }; - }, - }); - - // Read a single buffer, line-capped ------------------------------- - tools.push({ - name: "nvim_buffer", - label: "Neovim Buffer", - description: - "Read lines of a Neovim buffer. `buffer` accepts the current buffer " + - "(default), a buffer number from nvim_buffers, or a file name/path " + - "open in Neovim. `maxLines` caps the returned lines (default 2000); " + - "pass a larger value explicitly to read more of a long buffer.", - parameters: pi.arktype({ - buffer: "string? | number?", - maxLines: "number?", - }), - async execute(_id, params, _onUpdate, _ctx, signal) { - const buf = await resolveBuf(params.buffer, signal); - if (buf === null) { - return { - content: [ - { - type: "text", - text: `No buffer matches '${params.buffer}'. List open buffers with nvim_buffers.`, - }, - ], - }; - } - const maxLines = - Number.isInteger(params.maxLines) && params.maxLines > 0 - ? Math.min(params.maxLines, 100000) - : DEFAULT_MAX_LINES; - - const expr = - `json_encode({'name': nvim_buf_get_name(${buf}), ` + - `'total': nvim_buf_line_count(${buf}), ` + - `'lines': nvim_buf_get_lines(${buf}, 0, ${maxLines}, 0)})`; - const { name, total, lines } = await rpc(expr, signal); - if (!total) { - return { - content: [ - { type: "text", text: `Buffer ${buf} (${name}) is empty.` }, - ], - }; - } - const truncated = - total > lines.length - ? `\n... ${total - lines.length} more lines ` + - `(raise maxLines to read them)` - : ""; - const numbered = lines.map((l, i) => `${i + 1}: ${l}`).join("\n"); - return { - content: [ - { - type: "text", - text: - `Buffer ${buf} (${name}), ${total} lines:\n` + - numbered + - truncated, - }, - ], - }; - }, - }); - - return tools; -} \ No newline at end of file diff --git a/flake.lock b/flake.lock index 195ab40..57e58b8 100644 --- a/flake.lock +++ b/flake.lock @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1785967620, - "narHash": "sha256-IItrdb7Puk05RqOBWZYFC5X6Wl1sJmCfh5MWVHw5iMM=", + "lastModified": 1786384358, + "narHash": "sha256-RzPPiWeUtuvymnpuEWsdtzli5w4kjZs49FqEs3/1u+I=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b7c2ada94fe99c15b0dbcf4d11fd7850b957a436", + "rev": "2fcb964de67fcf60b43471c55d5d99e61a9ccb5a", "type": "github" }, "original": { @@ -39,11 +39,11 @@ "plugins-bloocky": { "flake": false, "locked": { - "lastModified": 1786042339, - "narHash": "sha256-8QcoC1bS8pRcwBkW3mqD2vgP9Mu0aCdIy6umZO6mUGM=", + "lastModified": 1786453931, + "narHash": "sha256-OpVm/ubj6rxjGFkjluQLhys/qGWqmN3E7dUMiFUYAZw=", "owner": "atiladefreitas", "repo": "bloocky", - "rev": "a052c3b1a8126e04b194bb3db3c4c6ca641c0e4e", + "rev": "64a6de6560c40b3351d29545528ddaed98cd9984", "type": "github" }, "original": { @@ -71,11 +71,11 @@ "plugins-dooing": { "flake": false, "locked": { - "lastModified": 1785971574, - "narHash": "sha256-GtEC5kWcH0DmiYXkzxzNjjfYLfyEb7zpTLQWdi7n3zs=", + "lastModified": 1786125118, + "narHash": "sha256-Iil8eCCz61T1Hz1y2cij71Hti+QGXbjH49eGCPMqTbA=", "owner": "atiladefreitas", "repo": "dooing", - "rev": "6748316bc6b4601797fb952a83694cc0a5ef6da2", + "rev": "2871aaf400c49187aa4216e7cf7abfa37cd32840", "type": "github" }, "original": { @@ -108,11 +108,11 @@ }, "rixpkgs": { "locked": { - "lastModified": 1785776890, - "narHash": "sha256-CzzgB1teVbroR/QhGAnsQToUYCDXEZZtFTcISAooASM=", + "lastModified": 1786421894, + "narHash": "sha256-BQLZ0kJYdZ3FmC2Vq7PJFmx4wnyifoCdz+5CIkdbDxI=", "owner": "dwinkler1", "repo": "rixpkgs", - "rev": "a54080a58b57d5785dc85901850b817bef2aaf83", + "rev": "9518d16a41413cf08eecde557b337b87f1422e91", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 00ad477..3d3543e 100644 --- a/flake.nix +++ b/flake.nix @@ -171,11 +171,11 @@ modules = [ { cats = { - r = false; + r = true; python = true; }; settings.lang_packages.python = nixpkgs.lib.mkForce (with pkgs.python3Packages; [ - pandas + polars ]); catPkgs.nix = nixpkgs.lib.mkForce [ pkgs.alejandra diff --git a/lua/keymap/leader.lua b/lua/keymap/leader.lua index e741c81..3c9870a 100644 --- a/lua/keymap/leader.lua +++ b/lua/keymap/leader.lua @@ -148,7 +148,8 @@ nmap_leader('LL', 'luafile %echo "Sourced lua"', 'Source buffe nmap_leader('Ls', 'lua Config.log_print()', 'Show log') nmap_leader('Lx', 'lua Config.execute_lua_line()', 'Execute `lua` line') --- m is free +-- m is for 'mark' (checkbox toggling) +nmap_leader('mc', 'ToggleCheckbox', 'Toggle checkbox') -- o is for 'other' local trailspace_toggle_command = 'lua vim.b.minitrailspace_disable = not vim.b.minitrailspace_disable' diff --git a/overlays/python.nix b/overlays/python.nix index bb17e94..29e4d12 100644 --- a/overlays/python.nix +++ b/overlays/python.nix @@ -9,4 +9,14 @@ in { basePythonPackages = reqPkgs; python = prev.python3.withPackages reqPkgs; -} + + # pyarrow's test_timezone_absent reads /usr/share/zoneinfo, which is + # blocked by the build sandbox (PermissionError: Operation not permitted). + python3Packages = prev.python3Packages // { + pyarrow = prev.python3Packages.pyarrow.overridePythonAttrs (old: { + disabledTests = (old.disabledTests or [ ]) ++ [ + "pyarrow/tests/test_orc.py::test_timezone_absent" + ]; + }); + }; +} \ No newline at end of file diff --git a/plugin/01_lib.lua b/plugin/01_lib.lua index 988905c..f117a77 100644 --- a/plugin/01_lib.lua +++ b/plugin/01_lib.lua @@ -119,3 +119,69 @@ Config.add = (function(pkg) end) Config.now_if_args = vim.fn.argc(-1) > 0 and MiniDeps.now or MiniDeps.later + +-- Checkbox +local checked_character = "x" + +local checked_checkbox = "%[" .. checked_character .. "%]" +local unchecked_checkbox = "%[ %]" + +local line_contains_unchecked = function(line) + return line:find(unchecked_checkbox) +end + +local line_contains_checked = function(line) + return line:find(checked_checkbox) +end + +local line_with_checkbox = function(line) + return line:find("^%s*- " .. checked_checkbox) + or line:find("^%s*- " .. unchecked_checkbox) + or line:find("^%s*%d%. " .. checked_checkbox) + or line:find("^%s*%d%. " .. unchecked_checkbox) +end + +local checkbox = { + check = function(line) + return line:gsub(unchecked_checkbox, checked_checkbox, 1) + end, + + uncheck = function(line) + return line:gsub(checked_checkbox, unchecked_checkbox, 1) + end, + + make_checkbox = function(line) + if not line:match("^%s*%-%s") and not line:match("^%s*%d%.%s") then + -- "xxx" -> "- [ ] xxx" + return line:gsub("(%S+)", "- [ ] %1", 1) + else + -- "- xxx" -> "- [ ] xxx", "3. xxx" -> "3. [ ] xxx" + return line:gsub("(%s*- )(.*)", "%1[ ] %2", 1):gsub("(%s*%d%. )(.*)", "%1[ ] %2", 1) + end + end, +} + + +Config.togglecb = function() + local bufnr = vim.api.nvim_get_current_buf() + local cursor = vim.api.nvim_win_get_cursor(0) + local start_line = cursor[1] - 1 + local current_line = vim.api.nvim_buf_get_lines(bufnr, start_line, start_line + 1, false)[1] or "" + + -- If the line contains a checked checkbox then uncheck it. + -- Otherwise, if it contains an unchecked checkbox, check it. + local new_line = "" + + if not line_with_checkbox(current_line) then + new_line = checkbox.make_checkbox(current_line) + elseif line_contains_unchecked(current_line) then + new_line = checkbox.check(current_line) + elseif line_contains_checked(current_line) then + new_line = checkbox.uncheck(current_line) + end + + vim.api.nvim_buf_set_lines(bufnr, start_line, start_line + 1, false, { new_line }) + vim.api.nvim_win_set_cursor(0, cursor) +end + +vim.api.nvim_create_user_command("ToggleCheckbox", Config.togglecb, {}) diff --git a/plugin/29_bloocky.lua b/plugin/29_bloocky.lua deleted file mode 100644 index 5e6a3ea..0000000 --- a/plugin/29_bloocky.lua +++ /dev/null @@ -1,43 +0,0 @@ --- Bloocky: timeblocking calendar (day/week/month views), persisted to JSON. --- Loaded via the `general` cat spec (nix mode) or MiniDeps (non-nix). --- Default global toggle is tb, which collides with the terminal map --- (toggle bracketed paste), so it is moved to the calendar/tasks group c. -local Config = require('config') - -if not Config.isNixCats then - local later = MiniDeps.later - later(function() - MiniDeps.add({ source = 'atiladefreitas/bloocky' }) - end) -end - -local nix = require('config.nix') -local later = MiniDeps.later - -later(function() - if not nix.get_cat('general', false) then - return - end - require('bloocky').setup({ - week_start = "monday", - window = { - -- Width per view: fraction of the editor width (or absolute columns if > 1). - -- A single number applies to every view. - width = { - month = 0.99, - week = 0.99, - day = 0.8, - }, - border = "rounded", - }, - integrations = { - dooing = { - enabled = true, -- show Dooing todos on their due date - show_done = false, -- also show completed todos - }, - }, - keymaps = { - toggle = 'cb', - }, - }) -end) diff --git a/plugin/30_dooing.lua b/plugin/30_dooing.lua deleted file mode 100644 index f47ecc2..0000000 --- a/plugin/30_dooing.lua +++ /dev/null @@ -1,44 +0,0 @@ --- Dooing: minimalist todo list manager with a floating window, persisted to JSON. --- Loaded via the `general` cat spec (nix mode) or MiniDeps (non-nix). --- Default globals td / tN occupy the terminal group, so the --- todo toggles are moved to the calendar/tasks group c. -local Config = require('config') - -if not Config.isNixCats then - local later = MiniDeps.later - later(function() - MiniDeps.add({ source = 'atiladefreitas/dooing' }) - end) -end - -local nix = require('config.nix') -local later = MiniDeps.later -local now = MiniDeps.now -later(function() - if not nix.get_cat('general', false) then - return - end - local dooing = require('dooing') - require("dooing").setup({ - keymaps = { - toggle_window = "cd", - open_project_todo = "cD", - show_due_notification = "cN", - create_nested_task = "cn", -- Create nested subtask under current todo - toggle_priority = "a", - }, - calendar = { - week_start_day = "monday", - }, - }) - -- Remove Dooing's old defaults. - vim.keymap.del("n", "td") - vim.keymap.del("n", "tD") - vim.keymap.del("n", "tN") - - -- Restore mappings overwritten by Dooing. - local helpers = require('keymap.helpers') - local nmap_leader = helpers.nmap_leader - -- t is for 'terminal' - nmap_leader("td", 'lua Config.terminal.open_duckdb();Config.terminal.toggle_bracket()', 'Open DuckDB') -end) diff --git a/plugin/31_nvim_omp.lua b/plugin/31_nvim_omp.lua deleted file mode 100644 index 44076cf..0000000 --- a/plugin/31_nvim_omp.lua +++ /dev/null @@ -1,8 +0,0 @@ --- omp bridge entrypoint: start the RPC socket as early as possible so the omp --- harness can read this instance's buffers on demand (see lua/nvim_omp/init.lua --- and .omp/tools/nvim_buffers.mjs). Safe to fail silently — the socket is a --- convenience, not a dependency of the editor. -local ok, nvim_omp = pcall(require, "nvim_omp") -if ok then - nvim_omp.start() -end \ No newline at end of file diff --git a/plugin/32_checkbox.lua b/plugin/32_checkbox.lua new file mode 100644 index 0000000..955a461 --- /dev/null +++ b/plugin/32_checkbox.lua @@ -0,0 +1,71 @@ +-- Minimal Markdown checkbox toggler: `:ToggleCheckbox` (mapped to mc). +-- Toggles `- [ ]`/`- [x]` (and numbered `N. [ ]`/`N. [x]`) on the current line; +-- creates a checkbox on lines without one. + +local checked_character = "x" + +local checked_checkbox = "%[" .. checked_character .. "%]" +local unchecked_checkbox = "%[ %]" + +local line_contains_unchecked = function(line) + return line:find(unchecked_checkbox) +end + +local line_contains_checked = function(line) + return line:find(checked_checkbox) +end + +local line_with_checkbox = function(line) + return line:find("^%s*- " .. checked_checkbox) + or line:find("^%s*- " .. unchecked_checkbox) + or line:find("^%s*%d%. " .. checked_checkbox) + or line:find("^%s*%d%. " .. unchecked_checkbox) +end + +local checkbox = { + check = function(line) + return line:gsub(unchecked_checkbox, checked_checkbox, 1) + end, + + uncheck = function(line) + return line:gsub(checked_checkbox, unchecked_checkbox, 1) + end, + + make_checkbox = function(line) + if not line:match("^%s*%-%s") and not line:match("^%s*%d%.%s") then + -- "xxx" -> "- [ ] xxx" + return line:gsub("(%S+)", "- [ ] %1", 1) + else + -- "- xxx" -> "- [ ] xxx", "3. xxx" -> "3. [ ] xxx" + return line:gsub("(%s*- )(.*)", "%1[ ] %2", 1):gsub("(%s*%d%. )(.*)", "%1[ ] %2", 1) + end + end, +} + +local M = {} + +M.toggle = function() + local bufnr = vim.api.nvim_get_current_buf() + local cursor = vim.api.nvim_win_get_cursor(0) + local start_line = cursor[1] - 1 + local current_line = vim.api.nvim_buf_get_lines(bufnr, start_line, start_line + 1, false)[1] or "" + + -- If the line contains a checked checkbox then uncheck it. + -- Otherwise, if it contains an unchecked checkbox, check it. + local new_line = "" + + if not line_with_checkbox(current_line) then + new_line = checkbox.make_checkbox(current_line) + elseif line_contains_unchecked(current_line) then + new_line = checkbox.check(current_line) + elseif line_contains_checked(current_line) then + new_line = checkbox.uncheck(current_line) + end + + vim.api.nvim_buf_set_lines(bufnr, start_line, start_line + 1, false, { new_line }) + vim.api.nvim_win_set_cursor(0, cursor) +end + +vim.api.nvim_create_user_command("ToggleCheckbox", M.toggle, {}) + +return M diff --git a/result b/result new file mode 120000 index 0000000..a99c79c --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/12g17cxiz4wkn5zy483xhbkybpqwc9k4-neovim-unwrapped-0.12.4 \ No newline at end of file From 9a6e20110e531c696354e12dc46bf23c5e29396f Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Tue, 25 Aug 2026 11:25:05 +1000 Subject: [PATCH 11/13] Make settings.lang_packages append instead of replace Downstream modules assign settings.lang_packages. plainly, which out-priorities mkOptionDefault and silently dropped the built-in defaults (e.g. lintr, breaking R linting in downstream wrappers like the project-template ed flake). Move the built-in defaults into a separate settings.langPackageDefaults option and compose defaults ++ lang_packages at the consumption sites (catPkgs r/python/julia, quarto extraRPackages, neovide R host). A new append-semantics check fails if defaults migrate back into lang_packages. Also fix pre-existing inverted assertions in downstream-overrides check: the override module sets cats.r = true, so assert presence, not absence. --- README.md | 8 +- flake.nix | 36 ++++- modules/module/settings/cat-packages.nix | 161 +++++++++++++--------- modules/module/settings/env.nix | 9 ++ modules/module/settings/hosts.nix | 5 +- modules/module/settings/lang-packages.nix | 102 ++++++++------ 6 files changed, 207 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 068d704..b6e18fb 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,8 @@ projectSettings = { ### Language packages (module defaults) -Add Python/R/Julia libraries that get appended to the module defaults. The built-in defaults are: +Add Python/R/Julia libraries. These are appended to the built-in defaults, which +always apply (see `settings.langPackageDefaults` in `modules/module/settings/lang-packages.nix`): - Python: `duckdb`, `polars` - R: `arrow`, `broom`, `data_table`, `janitor`, `styler` @@ -214,7 +215,8 @@ projectSettings = { }; ``` -Use `lib.mkForce` to replace rather than append: +Use `lib.mkForce` to replace other `lang_packages` assignments; the built-in defaults still apply. +To exclude defaults, override `catPkgs.` instead: ```nix projectSettings = { @@ -337,5 +339,5 @@ cats.nix ───────────────► config.cats │ - **Overlays** (`overlays/`) inject dependency package sets into nixpkgs (`rpkgs`, `baseRPackages`, `basePythonPackages`, plugins). Top-level `rWrapper` and `quarto` are compatibility conveniences, but `pkgs.rpkgs` is the canonical R surface for downstream configuration. - **cat-packages.nix** is the single source of truth for per-category packages. Each list is gated by its cat toggle. - **deps.nix** wires `catPkgs` into the wrapper's runtime PATH. -- **settings.lang_packages** holds the shared defaults used by local outputs and downstream module consumers. +- **settings.lang_packages** holds project-specific language libraries appended to the built-in `settings.langPackageDefaults`, used by local outputs and downstream module consumers. - **flake.nix** exports `lib.eval`, `lib.mkWrapper`, `lib.devShellPackages`, and `lib.shellHook` as the canonical downstream helpers, and builds `packages.default` and `devShells.default` from the same module config. diff --git a/flake.nix b/flake.nix index 3d3543e..7406da2 100644 --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,6 @@ url = "github:jmbuhr/cmp-pandoc-references"; flake = false; }; - "plugins-bloocky" = { url = "github:atiladefreitas/bloocky"; flake = false; @@ -39,6 +38,7 @@ url = "github:atiladefreitas/dooing"; flake = false; }; + }; outputs = { @@ -216,10 +216,10 @@ (builtins.length (self.lib.devShellPackages defaultConfig) > 0) ]; overrideAssertions = [ - (!(overrideConfig.cats.r or false)) + (overrideConfig.cats.r or false) (builtins.length overrideNix == 1) ((builtins.head overrideNix) == "alejandra") - (builtins.match ".*R RHOME.*" overrideShellHook == null) + (builtins.match ".*R RHOME.*" overrideShellHook != null) ]; in pkgs.runCommand "check-downstream-overrides" { @@ -241,6 +241,36 @@ touch $out ''; + # Downstream modules assign settings.lang_packages. plainly. The + # module system replaces same-option defaults, so built-in defaults must + # live in langPackageDefaults and be composed at the consumption sites. + # This check fails if someone moves defaults back into lang_packages: + # then a downstream assignment would silently drop lintr etc. + append-semantics = let + downstreamCfg = (self.lib.eval { + inherit pkgs; + modules = [ + { + cats.r = true; + settings.lang_packages.r = [ pkgs.rpkgs.rPackages.fixest ]; + } + ]; + }).config; + rWrapperPkg = builtins.head downstreamCfg.catPkgs.r; + in + pkgs.runCommand "check-lang-packages-append" { } '' + script=$(readlink -f "${rWrapperPkg}/bin/R") + grep -q "r-lintr-" "$script" || { + echo "default R packages were dropped by a downstream lang_packages assignment" >&2 + exit 1 + } + grep -q "r-fixest-" "$script" || { + echo "downstream lang_packages.r additions were not appended" >&2 + exit 1 + } + echo "lang_packages append semantics OK" > $out + ''; + smoke-test = pkgs.runCommand "smoke-test" {} '' # The Nix build sandbox has a read-only HOME; point XDG dirs at a # writable location so vim.lsp/shaDa can write state headlessly. diff --git a/modules/module/settings/cat-packages.nix b/modules/module/settings/cat-packages.nix index dbdba3f..a2a1400 100644 --- a/modules/module/settings/cat-packages.nix +++ b/modules/module/settings/cat-packages.nix @@ -9,14 +9,17 @@ let # NOTE: Package list expressions are lazily evaluated, and derivations are # not built until needed, so keep side-effecting expressions out of these # lists. - maybe = cat: pkgsList: - lib.optionals (config.cats.${cat} or false) pkgsList; - rPackages = (pkgs.baseRPackages or [ ]) ++ config.settings.lang_packages.r; + maybe = cat: pkgsList: lib.optionals (config.cats.${cat} or false) pkgsList; + rPackages = + (pkgs.baseRPackages or [ ]) + ++ config.settings.langPackageDefaults.r + ++ config.settings.lang_packages.r; rWrapperPackages = rPackages; quartoPkg = - if config.cats.r or false - then pkgs.rpkgs.quarto.override { extraRPackages = rPackages; } - else pkgs.quarto; + if config.cats.r or false then + pkgs.rpkgs.quarto.override { extraRPackages = rPackages; } + else + pkgs.quarto; in { options.catPkgs = lib.mkOption { @@ -25,83 +28,107 @@ in }; config.catPkgs = { - always = maybe "always" (with pkgs; [ - ripgrep - ]); + always = maybe "always" ( + with pkgs; + [ + ripgrep + ] + ); clickhouse = maybe "clickhouse" (with pkgs; [ clickhouse-lts ]); - external = maybe "external" (with pkgs; [ - nodejs - perl - ruby - shfmt - sqlfluff - tree-sitter - ]); + external = maybe "external" ( + with pkgs; + [ + nodejs + perl + ruby + shfmt + sqlfluff + tree-sitter + ] + ); julia = maybe "julia" [ - (pkgs.julia-bin.withPackages config.settings.lang_packages.julia) + (pkgs.julia-bin.withPackages ( + config.settings.langPackageDefaults.julia ++ config.settings.lang_packages.julia + )) ]; lua = maybe "lua" (with pkgs; [ lua-language-server ]); - markdown = maybe "markdown" (with pkgs; [ - python3Packages.pylatexenc - quartoPkg - zk - marksman - texlab - imagemagick - harper - ]); + markdown = maybe "markdown" ( + with pkgs; + [ + python3Packages.pylatexenc + quartoPkg + zk + marksman + texlab + imagemagick + harper + ] + ); - nix = maybe "nix" (with pkgs; [ - alejandra - nix-doc - nixd - ]); + nix = maybe "nix" ( + with pkgs; + [ + alejandra + nix-doc + nixd + ] + ); - optional = maybe "optional" (with pkgs; [ - bat - broot - devenv - dust - fd - fzf - gawk - gh - git - hunspell - hunspellDicts.de-at - hunspellDicts.en-us - ispell - jq - just - lazygit - man - ncdu - pigz - poppler - ripgrep - tokei - wget - yq - ]); + optional = maybe "optional" ( + with pkgs; + [ + bat + broot + devenv + dust + fd + fzf + gawk + gh + git + hunspell + hunspellDicts.de-at + hunspellDicts.en-us + ispell + jq + just + lazygit + man + ncdu + pigz + poppler + ripgrep + tokei + wget + yq + ] + ); - python = maybe "python" (let - python_packages_fn = - if pkgs ? basePythonPackages - then ps: pkgs.basePythonPackages ps ++ config.settings.lang_packages.python - else _: config.settings.lang_packages.python; - python_with_packages = pkgs.python3.withPackages python_packages_fn; - in - with pkgs; [ + python = maybe "python" ( + let + python_packages_fn = + if pkgs ? basePythonPackages then + ps: + pkgs.basePythonPackages ps + ++ config.settings.langPackageDefaults.python + ++ config.settings.lang_packages.python + else + _: config.settings.langPackageDefaults.python ++ config.settings.lang_packages.python; + python_with_packages = pkgs.python3.withPackages python_packages_fn; + in + with pkgs; + [ python_with_packages ruff basedpyright uv - ]); + ] + ); r = maybe "r" [ (pkgs.rpkgs.rWrapper.override { packages = rWrapperPackages; }) diff --git a/modules/module/settings/env.nix b/modules/module/settings/env.nix index 75ac273..0575cf0 100644 --- a/modules/module/settings/env.nix +++ b/modules/module/settings/env.nix @@ -14,6 +14,15 @@ # R.nvim v1.x owns its cache and temporary directories and exports # RNVIM_COMPLDIR/RNVIM_TMPDIR during setup. Do not inject literal `$PWD` # values into the wrapper environment. + # + # ponytail: with R_LIBS_USER unset-or-empty, loading compiled namespaces + # (S7 via btw) segfaults on macOS for bare-terminal launches outside the + # devShell hook. Any non-empty value avoids it; this store path exists, + # contains no libraries, and stays inert. The devShell hook overrides it + # with $PWD/.r-libs. Upgrade path: teach wlib to emit expandable defaults. + (lib.mkIf (config.cats.r or false) { + R_LIBS_USER = "${pkgs.rpkgs.rWrapper}"; + }) ]; # Environment variables with defaults (can be overridden by user) diff --git a/modules/module/settings/hosts.nix b/modules/module/settings/hosts.nix index 2db1515..4638e5e 100644 --- a/modules/module/settings/hosts.nix +++ b/modules/module/settings/hosts.nix @@ -5,7 +5,10 @@ ... }: let - rPackages = (pkgs.baseRPackages or [ ]) ++ config.settings.lang_packages.r; + rPackages = + (pkgs.baseRPackages or [ ]) + ++ config.settings.langPackageDefaults.r + ++ config.settings.lang_packages.r; rWrapperPkg = pkgs.rpkgs.rWrapper.override { packages = rPackages ++ [pkgs.nvimcom]; }; in { diff --git a/modules/module/settings/lang-packages.nix b/modules/module/settings/lang-packages.nix index 3be7a9a..c172f8c 100644 --- a/modules/module/settings/lang-packages.nix +++ b/modules/module/settings/lang-packages.nix @@ -5,53 +5,75 @@ ... }: { - options.settings.lang_packages = lib.mkOption { - type = lib.types.submodule { - options = { - python = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - description = "Additional Python-related packages appended to the python spec (overlay defaults remain)."; - }; - r = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - description = "Additional R-related packages appended to the r spec (overlay defaults remain)."; - }; - julia = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "Additional Julia packages (names) passed to julia-bin.withPackages."; + options.settings = { + # Built-in language libraries composed into every language spec. + # Consumers extend via settings.lang_packages, which is APPENDED to these. + langPackageDefaults = lib.mkOption { + type = lib.types.submodule { + options = { + python = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + r = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + julia = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; }; }; + default = { }; + }; + + lang_packages = lib.mkOption { + type = lib.types.submodule { + options = { + python = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = "Additional Python-related packages appended to the python spec (overlay defaults remain)."; + }; + r = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = "Additional R-related packages appended to the r spec (overlay defaults remain)."; + }; + julia = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "Additional Julia packages (names) passed to julia-bin.withPackages."; + }; + }; + }; + default = { }; + description = '' + Project-specific language libraries. Appended to settings.langPackageDefaults + in each language spec's runtime packages. + ''; }; - default = { }; - description = '' - Language-specific package defaults and downstream overrides appended to each - language spec's runtime packages. - ''; }; - config.settings.lang_packages = { - python = lib.mkDefault (with pkgs.python3Packages; [ + config.settings.langPackageDefaults = { + python = with pkgs.python3Packages; [ duckdb polars - ]); - r = lib.mkDefault ( - (with pkgs.rpkgs.rPackages; [ - arrow - broom - data_table - janitor - styler - # vscDebugger is not on CRAN/Bioconductor, so it is not available in - # pkgs.rpkgs.rPackages. Install it manually in your R library if you - # want to use the nvim-dap R adapter (see plugin/26_dap.lua). - # vscDebugger - lintr - ]) - ); - julia = lib.mkDefault [ + ]; + r = with pkgs.rpkgs.rPackages; [ + arrow + broom + data_table + janitor + styler + # vscDebugger is not on CRAN/Bioconductor, so it is not available in + # pkgs.rpkgs.rPackages. Install it manually in your R library if you + # want to use the nvim-dap R adapter (see plugin/26_dap.lua). + # vscDebugger + lintr + ]; + julia = [ "DataFramesMeta" "QuackIO" ]; From 99aca2b6e7b46a119954eb145a0987cc764e85fd Mon Sep 17 00:00:00 2001 From: Daniel Winkler Date: Tue, 25 Aug 2026 13:07:41 +1000 Subject: [PATCH 12/13] Cleanup --- flake.lock | 52 ++++++-------------------------- flake.nix | 10 ------ modules/module/settings/core.nix | 2 +- modules/module/specs/plugins.nix | 2 -- 4 files changed, 10 insertions(+), 56 deletions(-) diff --git a/flake.lock b/flake.lock index 57e58b8..0f12e11 100644 --- a/flake.lock +++ b/flake.lock @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1786384358, - "narHash": "sha256-RzPPiWeUtuvymnpuEWsdtzli5w4kjZs49FqEs3/1u+I=", + "lastModified": 1787498568, + "narHash": "sha256-9i/VTdusq/+NM/tz+J1Re+ojkMB8MBf0QshnYfzHz30=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2fcb964de67fcf60b43471c55d5d99e61a9ccb5a", + "rev": "56c02bc00adcf003215cc4bd996d6efaf4cff188", "type": "github" }, "original": { @@ -36,22 +36,6 @@ "type": "github" } }, - "plugins-bloocky": { - "flake": false, - "locked": { - "lastModified": 1786453931, - "narHash": "sha256-OpVm/ubj6rxjGFkjluQLhys/qGWqmN3E7dUMiFUYAZw=", - "owner": "atiladefreitas", - "repo": "bloocky", - "rev": "64a6de6560c40b3351d29545528ddaed98cd9984", - "type": "github" - }, - "original": { - "owner": "atiladefreitas", - "repo": "bloocky", - "type": "github" - } - }, "plugins-cmp-pandoc-references": { "flake": false, "locked": { @@ -68,22 +52,6 @@ "type": "github" } }, - "plugins-dooing": { - "flake": false, - "locked": { - "lastModified": 1786125118, - "narHash": "sha256-Iil8eCCz61T1Hz1y2cij71Hti+QGXbjH49eGCPMqTbA=", - "owner": "atiladefreitas", - "repo": "dooing", - "rev": "2871aaf400c49187aa4216e7cf7abfa37cd32840", - "type": "github" - }, - "original": { - "owner": "atiladefreitas", - "repo": "dooing", - "type": "github" - } - }, "r-nvim-nix": { "inputs": { "nixpkgs": [ @@ -108,11 +76,11 @@ }, "rixpkgs": { "locked": { - "lastModified": 1786421894, - "narHash": "sha256-BQLZ0kJYdZ3FmC2Vq7PJFmx4wnyifoCdz+5CIkdbDxI=", + "lastModified": 1787585894, + "narHash": "sha256-tp/kigWpoz0DUyDmbqVHLjZova3n98vn7R7HTRtjeWg=", "owner": "dwinkler1", "repo": "rixpkgs", - "rev": "9518d16a41413cf08eecde557b337b87f1422e91", + "rev": "5a76f5802a7728c62feff1399c7302b53276ed8f", "type": "github" }, "original": { @@ -143,9 +111,7 @@ "inputs": { "fran": "fran", "nixpkgs": "nixpkgs", - "plugins-bloocky": "plugins-bloocky", "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references", - "plugins-dooing": "plugins-dooing", "r-nvim-nix": "r-nvim-nix", "rixpkgs": "rixpkgs", "wrappers": "wrappers" @@ -158,11 +124,11 @@ ] }, "locked": { - "lastModified": 1782135443, - "narHash": "sha256-vAmbArdCyjqpVW+37aCy/PMBOLIqukUXLQuEKLwUhA4=", + "lastModified": 1787579952, + "narHash": "sha256-Dn6IPaLyltx8hBTyC4hgNWDb7T7WHRYUG6wWh6KBHXA=", "owner": "BirdeeHub", "repo": "nix-wrapper-modules", - "rev": "6e7f66fa2cdf4d63162580b438f7fcf87c28a46f", + "rev": "b56e973dd2bd62244b44d6542fb52182bc3a3cda", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 7406da2..b92139f 100644 --- a/flake.nix +++ b/flake.nix @@ -29,16 +29,6 @@ url = "github:jmbuhr/cmp-pandoc-references"; flake = false; }; - "plugins-bloocky" = { - url = "github:atiladefreitas/bloocky"; - flake = false; - }; - - "plugins-dooing" = { - url = "github:atiladefreitas/dooing"; - flake = false; - }; - }; outputs = { diff --git a/modules/module/settings/core.nix b/modules/module/settings/core.nix index bebc487..52c9413 100644 --- a/modules/module/settings/core.nix +++ b/modules/module/settings/core.nix @@ -8,7 +8,7 @@ config.settings.config_directory = ../../..; # Default colorscheme and background - config.settings.colorscheme = lib.mkDefault "cyberdream"; + config.settings.colorscheme = lib.mkDefault "kanagawa"; config.settings.background = lib.mkDefault "dark"; # Enable RC wrapping (allows neovim to find the config) diff --git a/modules/module/specs/plugins.nix b/modules/module/specs/plugins.nix index babb1ba..34ac406 100644 --- a/modules/module/specs/plugins.nix +++ b/modules/module/specs/plugins.nix @@ -80,8 +80,6 @@ in { plenary-nvim vimtex neogit - config.nvim-lib.neovimPlugins.bloocky - config.nvim-lib.neovimPlugins.dooing { data = mini-nvim; pname = "mini.nvim"; From 0a1f6535d434553b975d6ab3aad761495efb60a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Sep 2026 08:28:11 +0000 Subject: [PATCH 13/13] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'fran': 'github:dwinkler1/fran/30ab141' (2026-06-13) → 'github:dwinkler1/fran/b77635a' (2026-08-26) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/56c02bc' (2026-08-23) → 'github:nixos/nixpkgs/34ab990' (2026-08-31) • Updated input 'rixpkgs': 'github:dwinkler1/rixpkgs/5a76f58' (2026-08-24) → 'github:dwinkler1/rixpkgs/f3d00cb' (2026-08-31) • Updated input 'wrappers': 'github:BirdeeHub/nix-wrapper-modules/b56e973' (2026-08-24) → 'github:BirdeeHub/nix-wrapper-modules/04ef216' (2026-08-26) --- flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/flake.lock b/flake.lock index 0f12e11..9c8afb8 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1781337902, - "narHash": "sha256-QjXxUvpOBbnlpSskBOZJ9nUbpYyFRD4cBo4s1aHniGs=", + "lastModified": 1787729487, + "narHash": "sha256-3A5rKxnceQ+inWqeQDUrxoVPXr67JMrdIIfbLStx06s=", "owner": "dwinkler1", "repo": "fran", - "rev": "30ab141871ede23b245a90afea58e076ef83b515", + "rev": "b77635aeb1e70fffd09e5823dde5b76abb3df991", "type": "github" }, "original": { @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1787498568, - "narHash": "sha256-9i/VTdusq/+NM/tz+J1Re+ojkMB8MBf0QshnYfzHz30=", + "lastModified": 1788179007, + "narHash": "sha256-hn1oU2rue2SYK8dAr8+WNZWtbsz1S2W5mnHlSEuh3bo=", "owner": "nixos", "repo": "nixpkgs", - "rev": "56c02bc00adcf003215cc4bd996d6efaf4cff188", + "rev": "34ab99075ac4f7e40cf037eef32cb1c360bb85e9", "type": "github" }, "original": { @@ -76,11 +76,11 @@ }, "rixpkgs": { "locked": { - "lastModified": 1787585894, - "narHash": "sha256-tp/kigWpoz0DUyDmbqVHLjZova3n98vn7R7HTRtjeWg=", + "lastModified": 1788208780, + "narHash": "sha256-ouq3GjmTOXtWVufLOhMqLAEJXxrlkhDYtZJt3YfN5oU=", "owner": "dwinkler1", "repo": "rixpkgs", - "rev": "5a76f5802a7728c62feff1399c7302b53276ed8f", + "rev": "f3d00cb8f39262e6ca15c2046bd29ca8a52aae31", "type": "github" }, "original": { @@ -124,11 +124,11 @@ ] }, "locked": { - "lastModified": 1787579952, - "narHash": "sha256-Dn6IPaLyltx8hBTyC4hgNWDb7T7WHRYUG6wWh6KBHXA=", + "lastModified": 1787722691, + "narHash": "sha256-bWBt7v2FhgG6WTLgSi8nYGi1zXaTdA+vTdCRdvR2vcw=", "owner": "BirdeeHub", "repo": "nix-wrapper-modules", - "rev": "b56e973dd2bd62244b44d6542fb52182bc3a3cda", + "rev": "04ef216559b18214853879df862f493a9e6bb8cf", "type": "github" }, "original": {