lua-projects
Skill1 starsChanged 8 months agoMITView on GitHub
Guides idiomatic Lua 5.4 programming, module design, and project maintenance for Neovim plugin/config ecosystems (LazyVim, lazy.nvim) and macOS bar tools (SketchyBar/SbarLua). Covers style, tooling (StyLua, Selene, LuaLS), module patterns, and testing. Use when writing Lua code, configuring Neovim plugins, working with SketchyBar Lua configs, debugging Lua require/module errors, or when the user mentions Lua, .lua files, LazyVim, lazy.nvim, SbarLua, SketchyBar, luarocks, LuaLS, StyLua, or Selene.
- Installs packages
In this repository33 other files it gives its agents.
- argocdSkill
- c-cpp-compilersSkill
- coding-guidelinesSkill
- devops-iac-engineerSkill
- dag-flowdSkill
- flowd-rules-authorSkill
- frontend-designSkill
- ghSkill
- grafana-dashboardsSkill
- helmSkill
- kustomizeSkill
- legalizerSkill
- markdown-documentationSkill
- mermaid-diagramsSkill
- meta-cognition-parallelSkill
- podmasterSkill
- practical-haskellSkill
- rcaSkill
- researcherSkill
- security-auditSkill
- solidity-securitySkill
- svg-genSkill
- tdd-red-green-refactorSkill
- tmux-masterySkill
- tokenomics-designSkill
- ultimate-dbSkill
- ultimate-nixosSkill
- ultimate-rustSkill
- ultimate-web3Skill
- web3-frontendSkill
- web3-testingSkill
- web3-whitepaperSkill
- ziglangSkill
The file
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 1 is distinctive, and 10 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 29
Use 2 spaces (common in Neovim/LazyVim ecosystem) or tabs (SketchyBar default) -- be consistent within a project
- Very specific: line 32
No spaces inside {}, (), or [].
- Very specific: line 52
Run: stylua . or integrate as a pre-commit hook / editor format-on-save.
- Very specific: line 127
Every module file should return a table (or a single value).
- Very specific: line 198
Use opts table merging over config functions when possible
- Very specific: line 242
Always call sbar.event_loop() at the end of init.lua
- Very specific: line 243
Use sbar.exec() instead of os.execute() to avoid blocking the event handler
- Very specific: line 246
Keep color/icon/settings as pure data modules that return a table
- Very specific: line 314
Run: busted or busted spec/
- Very specific: line 318
Use plenary.nvim's test harness for Neovim-specific tests:
- Very specific: line 329
Prefer ipairs over pairs when iterating sequential arrays (faster and order-guaranteed)
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Lua Projects
Scope: Lua 5.4 (with LuaJIT notes where relevant). Targets config-driven projects like Neovim distributions, SketchyBar setups, and general Lua modules.
Style and Formatting
Naming
snake_casefor variables, functions, and file namesPascalCaseonly for class-like constructor tables (rare)UPPER_SNAKEfor true constants- Prefix unused variables with
_(e.g.,for _, v in ipairs(t))
Indentation and Layout
- Use 2 spaces (common in Neovim/LazyVim ecosystem) or tabs (SketchyBar default) -- be consistent within a project
- Wrap lines at 100 characters; hard limit 120
- Trailing commas in multi-line tables are encouraged
- No spaces inside
{},(), or[]; spaces after commas and around operators
Formatter: StyLua
StyLua (v2.4+) is the standard Lua formatter. Configure via stylua.toml at the project root:
column_width = 100
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"For SketchyBar configs using tabs:
indent_type = "Tabs"Run: stylua . or integrate as a pre-commit hook / editor format-on-save.
Linting
Selene (recommended)
Modern Lua linter written in Rust. Actively maintained (v0.30+). Provides rich diagnostics with named lint rules.
Config file selene.toml:
std = "lua54"For Neovim configs, use the vim standard library definition so globals like vim are recognized:
std = "lua54+vim"Run: selene .
Luacheck (legacy)
Still detects some things Selene does not (uninitialized vars, unreachable code), but has been unmaintained since 2018. Use as a secondary pass if needed.
Config file .luacheckrc:
std = "lua54"
globals = { "vim" }Language Server: LuaLS
lua-language-server (LuaLS) provides diagnostics, completion, hover, and type checking.
Settings file .luarc.json at project root:
{
"runtime": { "version": "Lua 5.4" },
"diagnostics": { "globals": ["vim"] },
"workspace": {
"library": [],
"checkThirdParty": false
}
}For SketchyBar projects, add the SbarLua install path to workspace.library so LuaLS resolves the sketchybar module:
"workspace": {
"library": ["~/.local/share/sketchybar_lua"]
}Type Annotations
LuaLS supports EmmyLua-style annotations. Use them for public API surfaces:
---@param name string
---@param opts? { padding?: number, icon?: string }
---@return table item
local function add_item(name, opts)
-- ...
endModule Patterns
The Return-Table Pattern
Every module file should return a table (or a single value). Avoid polluting globals.
local M = {}
function M.greet(name)
return "hello " .. name
end
return MInit Modules
A directory with an init.lua is requireable by its directory name. Use init.lua to re-export or orchestrate sub-modules:
items/
init.lua -- require("items") loads this
apple.lua
calendar.lua-- items/init.lua
require("items.apple")
require("items.calendar")Avoid Globals
-- BAD: implicit global
sbar = require("sketchybar")
-- GOOD: local binding (use upvalues or pass explicitly)
local sbar = require("sketchybar")Exception: SketchyBar's example config sets sbar as a global in init.lua because sub-modules reference it without an explicit require path. If following that convention, document it clearly and configure your linter to allow it:
# selene.toml
[lints]
global_usage = "allow"Project Structure Patterns
Neovim Config (LazyVim / lazy.nvim)
~/.config/nvim/
init.lua -- minimal bootstrap: vim.loader.enable(), require("config.lazy")
lua/
config/
lazy.lua -- lazy.nvim bootstrap and setup
options.lua
keymaps.lua
autocmds.lua
plugins/
lsp.lua
treesitter.lua
ui.lua
editor.luaKey conventions:
init.luastays minimal -- callvim.loader.enable()then require config- One plugin spec per file (or group related specs) in
lua/plugins/ - lazy.nvim auto-loads everything in
lua/plugins/ - Use
optstable merging overconfigfunctions when possible
Plugin spec pattern:
return {
"author/plugin.nvim",
event = "VeryLazy",
dependencies = { "nvim-lua/plenary.nvim" },
opts = {
setting = true,
},
keys = {
{ "<leader>x", "<cmd>PluginAction<cr>", desc = "Do thing" },
},
}SketchyBar Config (SbarLua)
~/.config/sketchybar/
sketchybarrc -- shell entry: sketchybar --config init.lua (or similar)
init.lua -- requires sbar, wraps in begin_config/end_config, runs event_loop
bar.lua -- bar-level properties
default.lua -- default item properties
colors.lua -- color palette table
icons.lua -- icon constants (SF Symbols / Nerd Font)
settings.lua -- shared settings (paddings, fonts)
items/
init.lua -- requires each item module
spaces.lua
front_app.lua
media.lua
...
helpers/
init.lua
app_icons.lua
default_font.lua
event_providers/ -- native helpers (C compiled)Key conventions:
- Wrap all setup between
sbar.begin_config()/sbar.end_config()for batching - Always call
sbar.event_loop()at the end ofinit.lua - Use
sbar.exec()instead ofos.execute()to avoid blocking the event handler - Properties use sub-tables instead of dot notation:
icon = { y_offset = 10 }noticon.y_offset - Color values are
0xAARRGGBBhex integers - Keep color/icon/settings as pure data modules that return a table
Common Patterns
Safe Require
local ok, mod = pcall(require, "optional_module")
if not ok then
return
endMetatables for OOP-ish Tables
local Item = {}
Item.__index = Item
function Item.new(name)
return setmetatable({ name = name }, Item)
end
function Item:display()
return self.name
endConfig Merging
local defaults = { padding = 4, color = 0xffffffff }
local function apply(user_opts)
local cfg = {}
for k, v in pairs(defaults) do cfg[k] = v end
for k, v in pairs(user_opts or {}) do cfg[k] = v end
return cfg
endFor deep merging in Neovim: vim.tbl_deep_extend("force", defaults, user_opts).
Testing
Busted
The standard Lua test framework. Install via luarocks:
luarocks install busted-- spec/greet_spec.lua
describe("greet", function()
local mod = require("mymod")
it("returns greeting", function()
assert.are.equal("hello world", mod.greet("world"))
end)
it("handles nil", function()
assert.has_error(function() mod.greet(nil) end)
end)
end)Run: busted or busted spec/
Neovim Plugin Testing
Use plenary.nvim's test harness for Neovim-specific tests:
nvim --headless -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua'}"Performance Notes
locallookups are register-based; global lookups go through_ENVhash. Always localize hot-path references.- Pre-size tables with known lengths:
local t = table.create and table.create(n) or {} - String concatenation in loops: accumulate in a table and
table.concat()at the end - Prefer
ipairsoverpairswhen iterating sequential arrays (faster and order-guaranteed) - In LuaJIT (Neovim): avoid NYI (Not Yet Implemented) operations in tight loops -- check https://wiki.luajit.org/NYI
Lua 5.4 Specifics
Features available in 5.4 that older references may not cover:
- Integer subtype: integers and floats are distinct;
type(1)is"number"butmath.type(1)is"integer" - Bitwise operators:
&,|,~(xor),~(unary not),<<,>>-- no need forbit32orbitlibraries - Integer for-loop:
for i = 1, nuses native integers - Generational GC:
collectgarbage("generational")for lower-latency collection <const>and<close>: local attributes for immutability and deterministic cleanup
local path <const> = "/tmp/data"
local f <close> = io.open(path, "r")Note: Neovim uses LuaJIT (Lua 5.1 compatible), so 5.4 features are not available in Neovim configs. Use 5.4 features only in standalone Lua or SketchyBar (if built against Lua 5.4).
Debugging
print(vim.inspect(t))in Neovim for table inspectionprint(require("inspect")(t))in standalone Lua (install via luarocks)- SketchyBar logs to
~/.local/share/sketchybar/-- check there for Lua errors - LuaLS diagnostics surface most type/require errors before runtime
Tooling Summary
| Tool | Purpose | Config File | Install |
| StyLua | Formatter | stylua.toml | cargo install stylua or npm |
| Selene | Linter | selene.toml | cargo install selene |
| LuaLS | Language server | .luarc.json | via Mason or package manager |
| Busted | Test framework | .busted | luarocks install busted |
| Luarocks | Package manager | *.rockspec | system package manager |
Reference Projects
| Project | URL | Focus |
| LazyVim | https://github.com/LazyVim/LazyVim | Neovim distribution, plugin orchestration |
| lazy.nvim | https://github.com/folke/lazy.nvim | Plugin manager, spec system |
| SbarLua | https://github.com/FelixKratz/SbarLua | SketchyBar Lua bindings |
| SketchyBar | https://github.com/FelixKratz/SketchyBar | macOS bar, config examples |
| nvim-lspconfig | https://github.com/neovim/nvim-lspconfig | LSP client configs |
| plenary.nvim | https://github.com/nvim-lua/plenary.nvim | Neovim Lua utilities and test harness |
| telescope.nvim | https://github.com/nvim-telescope/telescope.nvim | Fuzzy finder, well-structured plugin |
Additional Resources
- Lua 5.4 Reference Manual: https://www.lua.org/manual/5.4/
- Programming in Lua (4th ed): https://www.lua.org/pil/
- LuaRocks style guide: https://github.com/luarocks/lua-style-guide
- Neovim Lua guide: https://neovim.io/doc/user/lua-guide.html
- StyLua: https://github.com/JohnnyMorganz/StyLua
- Selene: https://github.com/Kampfkarren/selene
- LuaLS wiki: https://luals.github.io/
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

