agent-fleet.nvim

agent
Security Audit
Fail
Health Warn
  • No license — Repository has no license file
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Fail
  • rm -rf — Recursive force deletion command in tests/run.sh
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

Run a fleet of agent coding agents from Neovim

README.md

agent-fleet.nvim

Run a fleet of coding agents from Neovim — pi, Claude Code, or
any agent CLI — each in its own native terminal, listed and switchable from a
clean board.

agent-fleet.nvim — launch real pi agents from the board, watch them run in native terminals, then rename, mark done and archive across the fleet

Inspired by claude agents, but with one hard rule:

The agent runs as its real CLI, in a native nvim terminal. Nothing
reimplements the agent's UI.

So the whole conversation lives in a buffer you can scroll, search and yank with
your own keybindings.

Features

  • Launch agents into a native terminal, with an optional initial prompt.
  • Persist & resume — a roster on disk; reopen a past agent (focus it if
    live, else relaunch it via its CLI's resume flag in its original cwd).
  • List & switch the agents of the current directory, live ones merged with
    on-disk sessions (pi and Claude) and deduped.
  • Live board (:AgentsBoard) — a dedicated buffer grouping agents into
    running / idle / done / archived sections, with colored state and relative
    last-activity times, refreshing on a timer.
  • Lifecycle — mark done, archive (soft, never deletes a session), rename,
    stop; bulk actions over a visual selection.
  • Background auto-naming (opt-in) — name an agent from its launch prompt,
    optionally via a one-shot LLM namer.

See ROADMAP.md for what's shipped and what's planned. Today pi
and Claude Code are supported end-to-end; other CLIs launch but aren't yet
persisted/resumed.

Requirements

  • Neovim 0.9+
  • At least one agent CLI on your PATH (pi, claude, …)

Installation

With lazy.nvim:

{
  "MateoGreil/agent-fleet.nvim",
  keys = {
    { "<leader>aa", "<cmd>Agent<cr>", desc = "Agent Fleet: launch agent" },
    { "<leader>al", "<cmd>Agents<cr>", desc = "Agent Fleet: list & switch" },
    { "<leader>ab", "<cmd>AgentsBoard<cr>", desc = "Agent Fleet: board" },
    { "<leader>ad", "<cmd>AgentDone<cr>", desc = "Agent Fleet: done" },
    { "<leader>ax", "<cmd>AgentArchive<cr>", desc = "Agent Fleet: archive" },
    { "<leader>as", "<cmd>AgentSend<cr>", desc = "Agent Fleet: send selection to agent" },
    { "<leader>as", ":AgentSend<cr>", mode = "x", desc = "Agent Fleet: send selection to agent" },
  },
  config = function()
    require("agent-fleet").setup({
      agents = {
        pi = {},
        claude = {},
      },
      -- optional: background auto-naming via LLM
      auto_name = {
        enabled = true,
        model = "anthropic/claude-haiku-4-5",
      },
    })
  end,
}

With packer.nvim:

use({
  "MateoGreil/agent-fleet.nvim",
  config = function()
    require("agent-fleet").setup({
      agents = {
        pi = {},
        claude = {},
      },
      -- optional: background auto-naming via LLM
      auto_name = {
        enabled = true,
        model = "anthropic/claude-haiku-4-5",
      },
    })

    local map = vim.keymap.set
    map("n", "<leader>aa", "<cmd>Agent<cr>", { desc = "Agent Fleet: launch agent" })
    map("n", "<leader>al", "<cmd>Agents<cr>", { desc = "Agent Fleet: list & switch" })
    map("n", "<leader>ab", "<cmd>AgentsBoard<cr>", { desc = "Agent Fleet: board" })
    map("n", "<leader>ad", "<cmd>AgentDone<cr>", { desc = "Agent Fleet: done" })
    map("n", "<leader>ax", "<cmd>AgentArchive<cr>", { desc = "Agent Fleet: archive" })
    map("n", "<leader>as", "<cmd>AgentSend<cr>", { desc = "Agent Fleet: send selection to agent" })
    map("x", "<leader>as", ":AgentSend<cr>", { desc = "Agent Fleet: send selection to agent" })
  end,
})

setup() is required, and you must declare at least one agent — there are
no default agents. pi and claude are recognized keys that auto-fill their
command and session settings, so pi = {} / claude = {} is enough; pass extra
fields to override. See Configuration.

Usage

:Agent           " launch the default agent, prompting for an initial message (like the board's i)
:Agent fix auth  " launch the default agent with "fix auth" as the initial prompt
:AgentResume     " reopen a past agent of this directory (focus if live, else resume)
:Agents          " list & switch agents of this directory (focus if live, else resume)
:AgentsBoard     " open the live board buffer (sections, colors, per-row keymaps)
:AgentDone       " toggle an agent done / not done (✓)
:AgentArchive    " archive / unarchive an agent (hidden from :Agents by default)
:AgentRename foo " rename the current agent (or pick one) to "foo"
:AgentRename     " rename via a prompt (current agent, or pick one)
:AgentSend       " send the current line / visual selection to an agent as a file:line ref

:AgentSend doesn't send file content — it sends a path:line (or
path:line1-line2 in visual mode) reference into the current or
last-focused agent's terminal input, without moving focus or pressing Enter
for you. That means you can fire it from a few different spots to stack up
several references in the agent's input, then switch over yourself, add your
question, and press Enter.

The visual-mode mapping must be :AgentSend<cr> (not <cmd>AgentSend<cr>): a
<Cmd> mapping does not pass the visual selection's line range to the command,
so it would only send the cursor line. The leading : lets Neovim insert the
'<,'> range for the selection.

Inside an agent terminal: <C-\><C-n> to enter Normal mode, then move / scroll /
yank with your usual nvim keys. i / a to type to the agent again.

The board (:AgentsBoard)

A dedicated, non-terminal buffer that opens in the current window and lists
this directory's agents in lifecycle sections — running, idle, done,
and (when toggled) archived — each row showing a live/dead marker, the
derived state, the name, and a relative last-activity time. It re-renders on a
timer while visible and reacts to agents exiting. Move with your normal nvim
keys (j/k///gg); the per-row actions are:

Key Action
<CR> switch to the agent under the cursor (focus its terminal if live, else resume it via its CLI)
d toggle done (✓)
x archive / unarchive
r rename (prompt)
s stop — kill the live terminal without marking it done (still resumable)
a launch a new agent
i type a prompt, then launch a new agent started with it
A toggle the archived section
R / gr refresh now

Switching, a and i hand the board's window to the agent (the board buffer
is wiped; reopen with :AgentsBoard). Action keys are no-ops on section
headers. d, x and s also work over a visual line selection (V): select a
span of rows and the action applies to every agent in it at once. There is
intentionally no q binding — leave the board with your usual buffer navigation.

The board renders a muted key legend at the bottom, so these shortcuts stay discoverable without leaving the buffer.

Configuration

require("agent-fleet").setup({
  default_agent = "pi", -- which agent `:Agent` launches with no argument
  agents = {            -- registry of agents: key -> { cmd = "<command>" }
    pi = {},
    claude = {},
  },
  window = "enew",      -- where the agent terminal opens (see below)
  start_insert = true,  -- drop straight into terminal insert mode
  follow_output = true, -- keep unfocused agent terminals scrolled to the bottom
  board = {             -- the :AgentsBoard buffer
    refresh_ms = 2000,  -- how often the open board re-renders
  },
  auto_name = {         -- background auto-naming (off by default; see below)
    enabled = false,
    model = nil,        -- e.g. "openai/gpt-4o-mini"; required to do anything
  },
})
Option Default Description
default_agent (required when multiple agents declared) Agent launched by :Agent with no argument. Implicit when exactly one agent is declared; required when two or more are declared; with zero agents declared the plugin notifies an error and commands no-op.
agents (required — none by default) Registry of declared agents (key -> { cmd = … }). You must declare at least one; pi and claude are recognized keys that auto-fill their presets ({} suffices).
window "enew" Ex command that opens the agent window.
start_insert true Enter terminal insert mode after launching.
follow_output true Auto-scroll an agent's terminal to the bottom on new output even when its window is not focused.
board.refresh_ms 2000 How often (ms) the open :AgentsBoard re-renders.

Registering agents

The agents registry maps a key to a command. pi and Claude Code are
fully supported end-to-end (persist, resume, board listing with live state).
Other CLIs will launch in a terminal but not appear on the board or be
resumable unless they provide a session backend.

agents = {
  pi = { cmd = "pi" },
  claude = { cmd = "claude" },
}

default_agent chooses which of these :Agent launches; the agent type is not
a command argument (all of :Agent's arguments become the new agent's name).

cmd is split on spaces into an argv list and executed directly without a
shell
, so each token becomes a separate argument — no quoting, pipes, or
VAR=val env prefixes. If you need shell features, point cmd at a wrapper
script.

Declaring pi = {} or claude = {} pulls in that agent's built-in preset
(command, session flags, and on-disk session location); any field you set
overrides the preset. A key with no matching preset (e.g. aider = { cmd = "aider" }) launches and is tracked while live, but is only persisted/resumed if
you also give it a session = { id_flag, name_flag, resume_flag } block.

window — where the agent opens

window is run as a plain Ex command right before the buffer becomes a
terminal, so any window-opening command works. Common choices:

Value Result
"enew" current window (default)
"botright vnew" new vertical split on the right
"topleft vnew" new vertical split on the left
"topleft new" new horizontal split on top
"botright new" new horizontal split on the bottom
"tabnew" new tab

Power users can pass any Ex command, e.g. window = "botright 80vnew" for a
fixed-width split.

auto_name — background auto-naming

When an agent (pi or Claude) is launched with an initial prompt but without
a name (board i, or :Agent <prompt>), the plugin can rename it from that
prompt: it runs a lightweight one-shot namer for that agent's backend (pi with
tools/session/extensions off, or claude -p with --tools "") on the prompt
text, sanitizes the reply to a short name, and applies it. There is no
polling — the prompt we launched with is used directly. The agent stays
machine-named, so a manual :AgentRename always takes precedence and is never
overwritten.

Independently of this LLM namer, an agent launched with a prompt but no name
already gets a sensible default: the first line of the prompt (char-aware
truncated to 40 chars), instead of the numbered <kind>-<n> name. An agent
launched without a prompt still falls back to <kind>-<n>.

The LLM namer is OFF by default and does nothing unless you both set
auto_name.enabled = true and provide a model.

auto_name = {
  enabled = false,        -- master switch
  model = nil,            -- model the namer runs with (required, e.g. "openai/gpt-4o-mini")
  thinking = "off",       -- pi --thinking value for the namer
  namer_timeout_ms = 30000,  -- kill the namer subprocess after this long
  max_chars = 2000,          -- cap the prompt text sent to the namer
}
Option Default Description
enabled false Master switch for background auto-naming.
model nil Model the one-shot namer runs with. Required.
thinking "off" pi --thinking value for the namer.
namer_timeout_ms 30000 Kill the namer subprocess after this long.
max_chars 2000 Cap on the prompt text sent to the namer.

The namer is the only subprocess agent-fleet spawns itself; it runs via
jobstart with an argv list (no shell), and it never passes --name or touches
the session file.

Board highlight groups

The board defines these highlight groups, each linked to a standard group by
default so it follows your colourscheme; override any with
vim.api.nvim_set_hl(0, "<group>", { … }):

Group Default link Used for
AgentFleetWorking DiagnosticInfo a working agent's state
AgentFleetIdle Normal an idle agent's state
AgentFleetStopped Comment a stopped agent's state
AgentFleetError DiagnosticError an errored agent's state
AgentFleetNew DiagnosticHint a brand-new agent's state
AgentFleetUnknown NonText state older than the tail read
AgentFleetArchived Comment archived rows (dimmed)
AgentFleetHeader Title section headers and the empty-state title
AgentFleetTime Comment the relative last-activity column

Reviews (0)

No results found