Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions core.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GENERATED by dotfiles-core sync-core.sh — vendored Core provenance (B1).
# Regenerate after a manual 'git subtree pull' with: make core-lock
core_version=4.5.0
core_sha=bea73f6080b04704ffd206bd935073e38781aec9
core_branch=bea73f6080b04704ffd206bd935073e38781aec9
core_tag=v4.5.0
core_version=4.6.0
core_sha=f1780a40031ef75368d49e866d6274a34f4f3a75
core_branch=f1780a40031ef75368d49e866d6274a34f4f3a75
core_tag=v4.6.0
6 changes: 3 additions & 3 deletions core/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
exclude: '\.md$'
Expand All @@ -34,7 +34,7 @@ repos:
- id: check-shebang-scripts-are-executable

- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
rev: v0.11.0
hooks:
- id: shellcheck
args: ["-x"]
Expand All @@ -44,7 +44,7 @@ repos:
# shellcheck/zsh -n/toml-yaml never inspect — so lint it at author time too.
# Shares .markdownlint.jsonc with the audit's markdown section (one rule config).
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.22.1
rev: v0.23.2
hooks:
- id: markdownlint-cli2

Expand Down
60 changes: 60 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,66 @@ commit (`git tag -a vX.Y.Z -m vX.Y.Z`).

## [Unreleased]

## [v4.6.0] - 2026-07-30

### Added

- **`:checkhealth gerrrt` now reports LSP / formatter / linter readiness.** The built-in
`:checkhealth vim.lsp` only lists clients _attached to the live session_, so run from the
dashboard it reads "No active clients" and says nothing about whether your configured tools
are installed. Three new sections in `nvim/lua/gerrrt/health.lua` report per-tool state:
- **LSP servers** — every wanted server (from `nvim/lua/gerrrt/servers/init.lua`) as
attached / enabled-idle / pending-enable / binary-missing / override-failed, via a new
read-only `M.status()` export that reuses the module's own wanted-list and
`binary_available()`. It tracks _registered_ (our override loaded) and _enabled_ (we called
`vim.lsp.enable`) as **distinct** facts — not inferred from `vim.lsp.config[name]`, which
also resolves upstream lspconfig defaults — so a failed override or an installed-but-not-yet-
enabled binary is reported accurately.
- **Formatters (conform)** — rendered from conform's own `list_all_formatters()` availability.
- **Linters (nvim-lint)** — from `linters_by_ft` + the SAST `semgrep`, checking each linter's
real builtin `cmd`.

All three are **side-effect-free**: they observe `package.loaded` and never `require()` a plugin
(which would force-load it and, for the LSP stack, register/enable servers before nvim-lspconfig's
defaults are on the runtimepath), so from the dashboard they say "open a file, then re-run" rather
than mutating the session. Missing binaries are info (not warnings) on `DOTFILES_OFFLINE` boxes.
Covered by new assertions in `scripts/test-core.sh` (the D4 registry probe exercises `status()`;
the checkhealth probe asserts all four sections render).
(`nvim/lua/gerrrt/health.lua`, `nvim/lua/gerrrt/servers/init.lua`, `scripts/test-core.sh`)

### Changed

- **Dev toolchain and plugin pins rolled forward.** Routine freshness sweep. The gate
toolchain was bumped in `scripts/tool-versions.env` (the single source — `ci.yml` and
`make setup` read it, and the audit's consistency gate enforces the `.pre-commit-config.yaml`
revs match it): **shellcheck** `0.10.0 → 0.11.0` (with its `SHELLCHECK_SHA256` recomputed
via `scripts/update-tool-checksums.sh`), **markdownlint-cli2** `0.22.1 → 0.23.2`, and
**pre-commit-hooks** `v5.0.0 → v6.0.0`. Plugin pins were rolled to upstream: the zsh
`zsh-transient-prompt` pin (`zsh/45-plugins.zsh`) and four Neovim plugins in
`nvim/lazy-lock.json` (`nvim-lspconfig`, `package-info.nvim`, `rainbow-delimiters.nvim`,
`schemastore.nvim`). (`scripts/tool-versions.env`, `.pre-commit-config.yaml`,
`zsh/45-plugins.zsh`, `nvim/lazy-lock.json`)

### Fixed

- **`:checkhealth gerrrt` clipboard false alarm on native Windows.** On the Windows host
(where `dotfiles-Windows` vendors only `nvim/` and never runs Core's bootstrap), the
clipboard section warned "Core's cross-OS clipboard scripts are not on PATH (clip: found,
clip-paste: missing)" — misleading, because `clip` only "found" as Windows' built-in
`clip.exe` and the Unix/WSL `clip`/`clip-paste` ladder simply does not apply there:
`config/clipboard.lua` wires an OS-appropriate provider instead (the `clip-windows`
provider — `clip.exe` copy + PowerShell paste — when `clip.exe` is present, else the OSC52
fallback). `nvim/lua/gerrrt/health.lua` now detects native Windows (`has("win32")`, false
under WSL), records that the Unix probe is inapplicable, and defers to `:checkhealth
vim.provider` for the live backend rather than running the ladder or re-deriving the
provider itself. (`nvim/lua/gerrrt/health.lua`)
- **noice.nvim cmdline regex highlighting.** Added the `regex` Tree-sitter parser to
`ensure_installed` in `nvim/lua/gerrrt/plugins/nvim-treesitter.lua`. noice runs a
floating command line (`cmdline_popup`), which uses the `regex` parser to syntax-
highlight the pattern in `:s/…/` substitutions and searches; without it, `:checkhealth
noice` warned "`regex` parser is not installed. Highlighting of the cmdline for `regex`
might be broken." It installs on next launch via the existing `ensure_installed` diff.

## [v4.5.0] - 2026-07-28

### Added
Expand Down
2 changes: 1 addition & 1 deletion core/core.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.5.0
4.6.0
8 changes: 4 additions & 4 deletions core/nvim/lazy-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,22 @@
"nvim-dap": { "branch": "master", "commit": "9e848e09a697ee95302a3ef2dd43fd6eb709e570" },
"nvim-dap-python": { "branch": "master", "commit": "1808458eba2b18f178f990e01376941a42c7f93b" },
"nvim-lint": { "branch": "master", "commit": "a219b2c9e5b4765e5c845aba119dad55806fcaf1" },
"nvim-lspconfig": { "branch": "master", "commit": "7ab79bb1b57160a537f427ac8a6026102f12b701" },
"nvim-lspconfig": { "branch": "master", "commit": "b7b920947f21339ee41fbb38c79d6445e12900aa" },
"nvim-tree.lua": { "branch": "master", "commit": "4213bd6eabac38b16dd6615002b6243b23cf3bf6" },
"nvim-treesitter": { "branch": "main", "commit": "61df84986b4b4ec469ee745a182e433d49f8c27e" },
"nvim-treesitter-context": { "branch": "master", "commit": "b311b30818951d01f7b4bf650521b868b3fece16" },
"nvim-treesitter-textobjects": { "branch": "main", "commit": "898ee307df58f854d11cd7edd06472574d48014e" },
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
"nvim-web-devicons": { "branch": "master", "commit": "2ae6958df7ced50baac5035cec0c15799eedfbf7" },
"oil.nvim": { "branch": "master", "commit": "b73018b75affd13fa38e2fc94ef753b465f770d7" },
"package-info.nvim": { "branch": "master", "commit": "24099acd58f8aab06795c92ddea59f5ba5cd6eeb" },
"package-info.nvim": { "branch": "master", "commit": "9e40ba35e1f91df3686f4b4b6619dc2ef92561fa" },
"persistence.nvim": { "branch": "main", "commit": "b20b2a7887bd39c1a356980b45e03250f3dce49c" },
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
"promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "a798325b7f36acc62741d1029930a7b96d4dd4bf" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "012f1480cd9a5fc99fce7678e0a536421a53fc46" },
"render-markdown.nvim": { "branch": "main", "commit": "f422cb5c6855f150e2ddcfaf44e7157b98b34f6a" },
"rustaceanvim": { "branch": "main", "commit": "f69c85a955b9a83d495e3dc8d43adb6364a0b765" },
"schemastore.nvim": { "branch": "main", "commit": "3d2c297f7152208daf4a382ea7123f9e4ebf9da1" },
"schemastore.nvim": { "branch": "main", "commit": "19e40cef03a380bfb578793a08222826118664dd" },
"todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" },
"tokyonight.nvim": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" },
"treesj": { "branch": "main", "commit": "79aedb401bbdc7e4202f7881eab5f6feb2105b0a" },
Expand Down
213 changes: 207 additions & 6 deletions core/nvim/lua/gerrrt/health.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
-- nvim/lua/gerrrt/health.lua
-- ─────────────────────────────────────────────────────────────────────────────
-- `:checkhealth gerrrt` — a precise, actionable report for Core's Neovim bits that
-- otherwise fail with a generic message. The clipboard is the prime case: when no
-- backend exists, `"+y` / `"+p` surface only Neovim's opaque "clipboard provider"
-- error. Here we run the SAME `clip` / `clip-paste` ladder the provider uses and say
-- exactly what's missing and how to fix it, instead of leaving the user guessing.
-- otherwise fail with a generic message. Two sections:
-- • clipboard — when no backend exists, `"+y` / `"+p` surface only Neovim's opaque
-- "clipboard provider" error. Here we run the SAME `clip` / `clip-paste` ladder the
-- provider uses and say exactly what's missing and how to fix it.
-- • LSP servers — the built-in `:checkhealth vim.lsp` only lists clients ATTACHED to the
-- current session, so from the dashboard it reads "No active clients" and tells you
-- nothing about whether your configured servers are installed. This section reports
-- every WANTED server (from gerrrt/servers) as installed/enabled/attached instead.
--
-- This module is loaded ONLY by :checkhealth (Neovim discovers lua/**/health.lua) —
-- it is never required at startup, so it adds nothing to load time.
-- ─────────────────────────────────────────────────────────────────────────────
local M = {}

function M.check()
local h = vim.health
local function check_clipboard(h)
h.start("dotfiles-core: clipboard")

-- Native Windows (NOT WSL — there has("win32") is false, it's Linux) never runs Core's
-- bootstrap and has no clip/clip-paste scripts: dotfiles-Windows vendors only nvim/, so the
-- unified Unix/WSL provider simply does not apply here. Worse, `clip` spuriously resolves to
-- Windows' built-in clip.exe, so the generic ladder below reports "clip: found, clip-paste:
-- missing" and warns about a probe the host was never meant to run. clipboard.lua instead wires
-- an OS-appropriate provider on this host (the clip-windows provider — clip.exe copy + PowerShell
-- paste — when clip.exe is present, else the OSC52 fallback). We deliberately do NOT re-derive
-- which of those is live here: :checkhealth vim.provider is the single authority on the active
-- backend. So just record that the Unix probe is inapplicable and defer to it.
if vim.fn.has("win32") == 1 then
h.ok("native Windows — Core's Unix/WSL clip/clip-paste probe does not apply here")
h.info(
"clipboard.lua wires the host provider instead (clip-windows via clip.exe + PowerShell, "
.. "else OSC52). See :checkhealth vim.provider for the live clipboard backend."
)
return
end

local have_clip = vim.fn.executable("clip") == 1
local have_paste = vim.fn.executable("clip-paste") == 1

Expand Down Expand Up @@ -55,4 +76,184 @@ function M.check()
end
end

local function check_lsp(h)
h.start("dotfiles-core: LSP servers")

-- READ-ONLY by construction: we peek `package.loaded` rather than require()-ing the registry.
-- Requiring it here would REGISTER server configs and call vim.lsp.enable() as a side effect of
-- running a health check — and, on the dashboard, it would do so BEFORE nvim-lspconfig's
-- lsp/*.lua defaults are on the runtimepath (that plugin loads on `User FilePost`), caching an
-- incomplete config for the rest of the session. So we only report once a code file has loaded
-- the stack the normal way (correct order), and otherwise tell the user to open one.
local servers = package.loaded["gerrrt.servers"]
if type(servers) ~= "table" or type(servers.status) ~= "function" then
h.info("LSP stack not loaded yet — open any code file (that loads it in the right order), then re-run :checkhealth gerrrt")
return
end

local offline = vim.g.dotfiles_offline
local attached, idle, pending, missing, broken = 0, 0, 0, 0, 0

for _, s in ipairs(servers.status()) do
if not s.registered then
-- Our servers/<name>.lua override failed to load. The server may still run on nvim-lspconfig's
-- upstream defaults, but our deliberate local overrides are silently lost — flag it.
broken = broken + 1
h.error(("%s — config override failed to load (running on lspconfig defaults, if any)"):format(s.name), {
"See :messages for the gerrrt.servers error.",
})
elseif s.clients > 0 then
attached = attached + 1
h.ok(("%s — attached (%d client%s)"):format(s.name, s.clients, s.clients == 1 and "" or "s"))
elseif s.enabled then
idle = idle + 1
h.ok(("%s — enabled (no client attached right now)"):format(s.name))
elseif s.available then
-- Binary is present but the enable pass never ran for it (installed after startup without
-- firing the Mason re-enable hook, e.g. `uv tool install`). It can't attach until enabled.
pending = pending + 1
h.warn(("%s — binary present but not enabled yet"):format(s.name), {
"Restart Neovim (or re-run the Mason install) to enable it; it won't attach until then.",
})
elseif offline then
-- DOTFILES_OFFLINE boxes intentionally omit tooling; a missing binary is expected, not a
-- defect — keep the section green (mirrors the startup-notify suppression in servers/init.lua).
missing = missing + 1
h.info(("%s — binary not found (DOTFILES_OFFLINE: expected)"):format(s.name))
else
missing = missing + 1
h.warn(("%s — binary not found, server not enabled"):format(s.name), {
"Install via :Mason (except ruff/ty via `uv tool install`, rust via rustup).",
})
end
end

h.info(
("%d attached · %d enabled-idle · %d pending-enable · %d missing · %d broken"):format(
attached,
idle,
pending,
missing,
broken
)
.. "\n'attached' is a snapshot of THIS moment — servers attach per-filetype, so open a file of that"
.. " language to watch its server start."
)
end

local function sorted_keys(set)
local t = {}
for k in pairs(set) do
t[#t + 1] = k
end
table.sort(t)
return t
end

local function check_formatters(h)
h.start("dotfiles-core: formatters (conform)")

-- READ-ONLY: peek package.loaded, never require() (that would force-load conform from a health
-- check). list_all_formatters() is conform's own public API — the exact one :ConformInfo renders
-- — so there's no second formatter list here to drift from conform.lua.
local conform = package.loaded["conform"]
if type(conform) ~= "table" or type(conform.list_all_formatters) ~= "function" then
h.info("conform.nvim not loaded yet — save a file or run :ConformInfo, then re-run :checkhealth gerrrt")
return
end

local list = conform.list_all_formatters()
table.sort(list, function(a, b)
return a.name < b.name
end)

local offline = vim.g.dotfiles_offline
local installed, missing = 0, 0
for _, f in ipairs(list) do
if f.available then
installed = installed + 1
h.ok(("%s — installed"):format(f.name))
else
missing = missing + 1
local why = (f.available_msg and f.available_msg ~= "") and (" (" .. f.available_msg .. ")") or ""
if offline then
h.info(("%s — not found%s (DOTFILES_OFFLINE: expected)"):format(f.name, why))
else
h.warn(("%s — binary not found%s"):format(f.name, why), {
"Install via :Mason; ruff via `uv tool install ruff`; forge_fmt/zigfmt/terraform_fmt need foundry/zig/terraform on PATH.",
})
end
end
end

h.info(("%d installed · %d missing"):format(installed, missing)
.. "\nconform silently skips a formatter whose binary is missing (that filetype just falls back to"
.. " LSP formatting, or none) — a miss here is degraded formatting, never an error.")
end

local function check_linters(h)
h.start("dotfiles-core: linters (nvim-lint)")

-- READ-ONLY: peek package.loaded, never require() (that would force-load nvim-lint, whose config
-- also runs an initial lint pass — a side effect a health check must not trigger).
local lint = package.loaded["lint"]
if type(lint) ~= "table" then
h.info("nvim-lint not loaded yet — open any code file, then re-run :checkhealth gerrrt")
return
end

-- Union of every linter wired per-filetype, plus semgrep (attached via the SAST filetype set in
-- nvim-lint.lua, not linters_by_ft). We read the plugin's own registries, so the wanted set can't
-- drift from the config. Each linter's REAL binary is its builtin `.cmd` (e.g. golangcilint→
-- "golangci-lint"), which is more accurate than guessing from the map key.
local set = { semgrep = true }
for _, names in pairs(lint.linters_by_ft or {}) do
for _, n in ipairs(names) do
set[n] = true
end
end

local offline = vim.g.dotfiles_offline
local installed, missing, dynamic = 0, 0, 0
for _, name in ipairs(sorted_keys(set)) do
local okd, def = pcall(function()
return lint.linters[name]
end)
local cmd = okd and type(def) == "table" and def.cmd or nil

if type(cmd) == "function" then
-- A few builtins compute cmd at runtime; probing it here would mean calling it out of context.
dynamic = dynamic + 1
h.info(("%s — dynamic command, not probed"):format(name))
elseif type(cmd) == "string" then
if vim.fn.executable(cmd) == 1 then
installed = installed + 1
h.ok(("%s — installed (%s)"):format(name, cmd))
elseif offline then
missing = missing + 1
h.info(("%s — %s not found (DOTFILES_OFFLINE: expected)"):format(name, cmd))
else
missing = missing + 1
h.warn(("%s — `%s` not found on PATH"):format(name, cmd), {
"Install via :Mason; some (checkstyle/phpstan/rubocop/…) also need their language runtime present.",
})
end
else
h.info(("%s — could not resolve its command"):format(name))
end
end

h.info(("%d installed · %d missing · %d dynamic"):format(installed, missing, dynamic)
.. "\nseveral linters are config-gated — installed here, but they only run in a project that ships"
.. " their config (see plugins/nvim-lint.lua). Python lint is ruff's LSP, not listed here.")
end

function M.check()
local h = vim.health
check_clipboard(h)
check_lsp(h)
check_formatters(h)
check_linters(h)
end

return M
1 change: 1 addition & 0 deletions core/nvim/lua/gerrrt/plugins/nvim-treesitter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ return {
"diff", -- diffview.nvim + git diff buffers
"gitcommit", -- commit message buffers (you write these via fugitive/lazygit)
"vimdoc", -- :help and plugin docs
"regex", -- noice.nvim floating cmdline: highlights the regex in :s/… and searches
-- ── added-language parsers (LSP + formatter + linter wired in the other manifests) ──
"ruby",
"java",
Expand Down
Loading
Loading