agentby ramilito

lua

Lua/Neovim plugin specialist. ALWAYS use for ANY task involving lua/kubectl/, Neovim API, resource views, keybindings, or plugin code.

Installs: 0
Used in: 1 repos
Updated: 5h ago
$npx ai-builder add agent ramilito/lua

Installs to .claude/agents/lua.md

# Lua/Neovim Plugin Guidance

**ALWAYS use this subagent** for ANY task involving:
- Reading, editing, or understanding Lua code
- `lua/kubectl/` directory
- Resource views, keybindings, or mappings
- State management or configuration
- Neovim API integration
- Researching Lua code for documentation

For Rust FFI patterns and async calls, also consult the `rust` subagent.

---

Guidance for working with the Lua/Neovim plugin codebase (`lua/kubectl/`).

## Plugin Structure

### Entry Point

`init.lua` - Plugin setup, commands (`:Kubectl`, `:Kubens`, `:Kubectx`), autocommands, and keymap registration.

### Core Modules

- `config.lua` - Default configuration with user overrides via `setup()`
- `state.lua` - Global runtime state, session persistence, filter/sort state
- `resource_factory.lua` - Builder pattern for constructing resource views
- `mappings.lua` - Global keybinding definitions using `<Plug>` format

### Resource Pattern

Each Kubernetes resource in `lua/kubectl/resources/<resource>/` follows:

```
<resource>/
├── init.lua        # View(), Draw(), Desc() functions
├── definition.lua  # Resource metadata (GVK, headers, hints)
└── mappings.lua    # Resource-specific keybindings
```

**Definition structure:**
```lua
M.definition = {
  resource = "pods",
  display_name = "PODS",
  ft = "k8s_pods",           -- filetype for buffer
  gvk = { g = "", v = "v1", k = "Pod" },
  hints = { ... },           -- help text shown in header
  headers = { ... },         -- column definitions
}
```

**Standard functions:**
```lua
function M.View(cancellationToken)   -- Create and show view
function M.Draw(cancellationToken)   -- Refresh existing view
function M.Desc(name, ns, reload)    -- Show resource description
```

## Resource Factory (Builder Pattern)

The factory in `resource_factory.lua` provides a fluent API:

```lua
local factory = require("kubectl.resource_factory")
factory.new("pods")
  .setCmd(args, "kubectl")
  .fetch()
  .decodeJson()
  .process(processFunc)
  .sort()
  .prettyPrint(win_nr)
  .addHints(hints, true, true)
  .addDivider(true)
  .displayContent(win_nr, cancellationToken)
```

**Key methods:**
- `.view(definition, token)` - Create buffer, start reflector, draw
- `.draw(token)` - Refresh data via Rust async call
- `.view_float(definition, opts)` - Floating window variant
- `.action_view(definition, data, callback)` - Action modal

## Rust Client Integration

The Rust dylib is loaded via `lua/kubectl/client/rust/init.lua`:
```lua
local client = require("kubectl.client.rust")  -- loads kubectl_client dylib
```

**Calling Rust functions:**
```lua
local commands = require("kubectl.actions.commands")

-- Async call (preferred)
commands.run_async("get_table_async", { gvk = def.gvk, namespace = ns }, function(data, err)
  if err then return end
  -- process data
end)

-- Sync call (blocks Neovim)
local result = commands.shell_command("kubectl", args)
```

## Filetypes

Plugin creates `k8s_*` filetypes for buffer identification:
- `k8s_pods`, `k8s_deployments`, `k8s_services`, etc.
- Used for filetype-specific autocommands and keymaps

## State Management

`state.lua` holds runtime state:
```lua
state.ns              -- current namespace ("All" or specific)
state.context         -- current kubectl context
state.filter          -- text filter
state.filter_label    -- label selectors
state.sortby          -- per-resource sort configuration
```

Session persistence via `kubectl.json` in Neovim data directory.

## Keymapping Convention

Use `<Plug>` mappings for user customization:
```lua
k("n", "gd", "<Plug>(kubectl.describe)", opts)
k("n", "gl", "<Plug>(kubectl.logs)", opts)
```

Register in `mappings.lua`, apply via FileType autocommand on `k8s_*` pattern.

## View System

Views in `lua/kubectl/views/` provide UI components:
- `filter/` - Filter input interface
- `namespace/` - Namespace selector
- `portforward/` - Port forward manager
- `header/` - Top header display
- `action/` - Action modal dialogs

## User Events

Emit events for extensibility:
```lua
vim.api.nvim_exec_autocmds("User", { pattern = "K8sResourceSelected", data = { kind, name, ns } })
vim.api.nvim_exec_autocmds("User", { pattern = "K8sContextChanged", data = { context } })
vim.api.nvim_exec_autocmds("User", { pattern = "K8sCacheLoaded" })
```

## Cancellation Tokens

Views accept cancellation tokens to abort rendering if buffer closed:
```lua
function M.View(cancellationToken)
  if cancellationToken and cancellationToken() then
    return nil
  end
  -- continue rendering
end
```

## Utilities

- `utils/tables.lua` - Table pretty-printing, header generation
- `utils/string.lua` - String manipulation
- `utils/find.lua` - Filtering logic
- `utils/url.lua` - URL building for kubectl/curl commands
- `utils/events.lua` - Event system helpers

## Code Style

- 2-space indentation (stylua)
- 120 column width
- LuaJIT/Lua 5.1 runtime
- Type annotations via `---@param`, `---@return` comments

## Defensive Patterns

### Buffer Validity After Async

Always check buffer validity in async callbacks - buffer may be deleted during async operation:
```lua
commands.run_async("some_function", args, function(content)
  vim.schedule(function()
    if not vim.api.nvim_buf_is_valid(buf) then
      return
    end
    -- Safe to use buf
  end)
end)
```

### Stale Request Cancellation

Prevent showing outdated data when user triggers rapid requests:
```lua
local current_request_id = 0

function M.fetch_data(callback)
  current_request_id = current_request_id + 1
  local request_id = current_request_id

  commands.run_async(..., function(data)
    vim.schedule(function()
      if request_id ~= current_request_id then
        return  -- Stale request, newer one in flight
      end
      callback(data)
    end)
  end)
end
```

### Buffer State Nil Checks

Always check buffer state exists before accessing fields:
```lua
local buf_state = state.get_buffer_state(bufnr)
if not buf_state or not buf_state.content_row_start then
  return nil
end
```

### Column/Array Bounds Checking

Guard against nil when accessing parsed arrays:
```lua
local col_value = columns[index]
if not col_value then
  return nil
end
local trimmed = vim.trim(col_value)
```

### pcall Return Validation

Check module exists after pcall (module may return nil):
```lua
local ok, module = pcall(require, "some.module")
if ok and module and module.field then
  -- Safe to use module.field
end
```

### Numeric String Comparisons

Use `tonumber()` when comparing parsed numeric strings:
```lua
local current, total = val:match("^(%d+)/(%d+)$")
if current and total and tonumber(current) ~= tonumber(total) then
  -- Properly compares as numbers, not strings
end
```

### LSP Callback Protocol

Always call LSP callbacks, even for unhandled methods:
```lua
function srv.request(method, params, callback)
  if handlers[method] then
    handlers[method](method, params, callback)
  else
    callback(nil, nil)  -- Don't leave client waiting
  end
end
```

Quick Install

$npx ai-builder add agent ramilito/lua

Details

Type
agent
Author
ramilito
Slug
ramilito/lua
Created
5h ago

More by ramilito