Skip to content

curtisault/agentflow.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AgentFlow

A Neovim plugin for orchestrating multi-agent AI workflows. Claude acts as a coordinator — decomposing tasks into a dependency graph, routing subtasks to specialized agents, running them in parallel, and synthesizing results back into your editor.

Requirements

Installation

vimpack (native Neovim 0.12+)

Add to your vimpack.lua (or wherever you call vim.pack.add):

vim.pack.add({
  "https://github.com/curtisault/agentflow.nvim",
})

Then call setup in your config (e.g. lua/curtis/init.lua):

require("agentflow").setup()

lazy.nvim

{
  "curtisault/agentflow.nvim",
  config = function()
    require("agentflow").setup()
  end,
}

Quick Start

:AgentFlow refactor the selected function to use early returns

Or via keymap: <leader>ap → type your prompt → <Enter>


Configuration

require("agentflow").setup({
  backend = {
    primary     = "cli",              -- "cli" | "http"
    cli_path    = "claude",           -- path to claude binary
    api_key_env = "ANTHROPIC_API_KEY",
  },

  orchestrator = {
    model     = "claude-sonnet-4-20250514",
    max_turns = 20,
  },

  agents = {
    { name = "sonnet", model = "claude-sonnet-4-20250514", backend = "cli", role = "subagent" },
    -- Add more agents for routing, e.g. a local model for simple tasks:
    -- { name = "local", model = "qwen2.5-coder:7b", backend = "ollama" },
  },

  routing = {
    rules = {
      -- Route analysis tasks to a cheaper/faster agent:
      -- { match = { task_type = "analysis" }, agent = "local", priority = 1 },
      { match = { fallback = true }, agent = "sonnet", priority = 99 },
    },
  },

  context = {
    max_tokens_per_agent = 12000,
    include_buffers      = true,
    include_treesitter   = true,
    include_git_diff     = "staged", -- "staged" | "all" | false
    include_lsp_symbols  = true,
    include_file_tree    = true,
  },

  ui = {
    approve_mode = "manual", -- "manual" | "auto" | "auto-safe"
    picker       = nil,      -- nil = auto-detect (mini.pick > telescope > fzf-lua > vim.ui.select)
  },

  concurrency = {
    max_parallel_agents    = 4,
    max_depth              = 5,
    max_total_agents       = 200,
    max_children_per_agent = 20,
    timeout_ms             = 30000,
  },

  keymaps = { enabled = true },
  log     = { level = "info", file = nil },
})

Project-level override

Place a .agentflow.json file in your project root to override any settings for that project:

{
  "concurrency": { "max_parallel_agents": 2 },
  "context": { "include_git_diff": "all" }
}

You can also add project-specific agents here. They are merged with (not replace) your global agent list:

{
  "agents": [
    { "name": "rust-expert", "model": "claude-opus-4-6", "backend": "cli", "role": "subagent" }
  ]
}

Project-local agents

For a cleaner setup, place individual agent definition files under .agentflow/agents/ in your project root. AgentFlow automatically discovers and registers them at startup — no changes to your Neovim config needed.

my-project/
├── .agentflow/
│   └── agents/
│       ├── rust-expert.json
│       └── test-runner.json
└── ...

Each file defines one agent:

{
  "name": "rust-expert",
  "model": "claude-opus-4-6",
  "backend": "cli",
  "role": "subagent",
  "max_tokens": 8192
}

Supported fields mirror the agents entries in setup(). Project-local files are registered last and win on name conflict, so they can override globally configured agents for a specific project.


Commands

Command Description
:AgentFlow [prompt] Open hub (no args) or start a workflow
:AgentChat Open chat pane
:AgentTree Open agent tree view
:AgentRoster Open agent roster
:AgentDash Open dashboard view
:AgentGrid Open grid (heat-map) view
:AgentReview Open review panel
:AgentLog [name] Show log buffer (optionally filter by agent name)
:AgentKill {name} Terminate an agent and its subtree
:AgentAdd {name} {model} Register a new agent at runtime
:AgentPick Picker to select and inspect an agent
:AgentSessions Browse and resume saved sessions
:AgentSave Save current session to disk

Keybindings

Global (normal mode)

Key Action
<leader>af Open hub
<leader>ap Prompt → start workflow
<leader>as Open agent tree
<leader>ac Open config panel
<leader>ar Open review panel

Disable with keymaps = { enabled = false } in setup.


Hub

Key Action
t Tree view
d Dashboard
g Grid view
r Roster
s Sessions browser
l Log buffer
? Help
p Prompt → start workflow
q / <Esc> Close

Review Panel

Key Action
<CR> Accept artifact (apply diff / write file)
x Reject
e Edit artifact in buffer
r Retry with agent
]r Next artifact
[r Previous artifact
1 Diff tab
2 Raw output tab
3 Agent log tab
4 Context tab
<Tab> Cycle tabs
q / <Esc> Close

Tree View

Key Action
zo Expand node
zc Collapse node
zR Expand all
zM Collapse all
<CR> Open dashboard for agent
f Filter (picker)
d Set depth limit
<C-k> Kill agent + subtree
<Tab> Go to hub
q / <Esc> Close

Dashboard

Key Action
<CR> Drill into child agent
<BS> Go up to parent
f Pick agent (jump)
<Tab> Go to hub
q / <Esc> Close

Grid View

Key Action
h/j/k/l or arrows Navigate cells
<CR> Open dashboard for selected agent
/ Search / filter agents
<Tab> Go to hub
q / <Esc> Close

Roster

Key Action
<CR> Show agent details
d Deregister agent
q / <Esc> Close

Chat Pane

Key Mode Action
<CR> normal / insert Send message
<C-c> normal / insert Close
<C-l> normal / insert Clear chat
q / <Esc> normal Close

Backends

Backend Config value Notes
Claude CLI "cli" Requires claude binary. Falls back to HTTP if unavailable.
Anthropic HTTP "http" Requires ANTHROPIC_API_KEY (or api_key_env).
Ollama "ollama" Local. Default endpoint: http://localhost:11434.
LM Studio "lmstudio" Local. Default endpoint: http://localhost:1234/v1.
OpenAI-compat "openai_compat" Any OpenAI-compatible API. Requires endpoint.

Custom backend

require("agentflow.extensions").register_backend("my-backend", function(agent_config)
  return {
    complete = function(self, messages, opts)
      -- return result_string, nil   (or nil, error_string)
    end,
  }
end)

Extensibility

local ext = require("agentflow.extensions")

-- Custom context source (appended to every agent's context)
ext.register_context_provider("jira", function(task, config)
  return "Current Jira ticket: " .. get_current_ticket()
end)

-- Custom result parser (tried before built-in parser)
ext.register_result_parser("my-format", function(content, task)
  -- return artifacts[] or nil to fall through
end)

-- Inject a routing rule at runtime
ext.add_routing_rule({ match = { task_type = "test" }, agent = "local", priority = 1 })

-- Subscribe to events
ext.on("agent:completed", function(data)
  print("Agent done: " .. data.agent.name)
end)

Neovim autocommands

AgentFlow fires User autocommands for all major events:

autocmd User AgentFlowAgentCompleted lua print(vim.g.agentflow_event_data)
Autocommand Fired when
AgentFlowPlanCreated Orchestrator produces a task plan
AgentFlowAgentStarted An agent begins execution
AgentFlowAgentCompleted An agent finishes successfully
AgentFlowAgentFailed An agent errors out
AgentFlowAgentRetrying An agent is retrying after failure
AgentFlowSynthesized Orchestrator synthesizes final result
AgentFlowReviewAccepted User accepts an artifact
AgentFlowReviewRejected User rejects an artifact
AgentFlowReviewRetry User requests a retry from review

Routing

The orchestrator decomposes each request into tasks and routes them to registered agents. Two mechanisms control which agent handles which task.

Routing rules

Rules are evaluated in priority order (lower number = higher priority). The first matching rule wins.

routing = {
  rules = {
    { match = { file_pattern = "%.lua$" },  agent = "lua-expert", priority = 1 },
    { match = { task_type = "analysis" },   agent = "reviewer",   priority = 2 },
    { match = { task_type = "planning" },   agent = "planner",    priority = 3 },
    { match = { fallback = true },          agent = "sonnet",     priority = 99 },
  },
}

Match conditions (all optional; omitted conditions always match):

Condition Type Matches when
fallback boolean Always — use as catch-all
task_type string Task type equals value ("analysis", "implementation", "test", etc.)
filetype string Current buffer filetype equals value
file_pattern string Any file in the task's required files matches the Lua pattern
agent_hint boolean Orchestrator set an explicit agent hint on the task

Task types are assigned by the orchestrator's planner when it decomposes the request.

Agent hints

The orchestrator can assign a specific agent to a task directly via agent_hint in the plan JSON. This takes precedence over all routing rules. To enable this, the orchestrator needs to know what agents are available — inject them at runtime with add_routing_rule or by ensuring the agent names are mentioned in your orchestrator system prompt override.

You can also inject a rule at runtime without restarting:

require("agentflow.extensions").add_routing_rule({
  match = { task_type = "test" },
  agent = "reviewer",
  priority = 1,
})

Orchestrator visibility

The orchestrator is surfaced as a root agent node in the tree and dashboard. Its internal phases map to agent states so you can see exactly what is happening during a run:

State Phase
running Planning — decomposing the user request
assigned Plan ready — tasks queued for delegation
running Delegating — task agents executing in parallel
running Synthesizing — combining results into a final response
completed Workflow finished
failed An unrecoverable error occurred

Open :AgentTree or :AgentDash at any point during a workflow to see the orchestrator and its child task agents live.


Statusline

Add the current agent status to your statusline:

-- lualine example
require("lualine").setup({
  sections = {
    lualine_x = { _G.AgentFlowStatus },
  },
})

AgentFlowStatus() returns a spinner + elapsed time while agents are running, empty string otherwise.


Sessions

Workflows are auto-saved to .agentflow/sessions/ in your project root after synthesis. Each session stores:

  • conversation.json — full orchestrator message history
  • plan.json — task plan and execution state
  • meta.json — model, cost, timestamps
  • logs/ — per-agent log files

Browse and resume sessions with :AgentSessions or s in the hub.


Health Check

:checkhealth agentflow

Checks: Neovim version, Claude CLI, API key, curl, Ollama, LM Studio, treesitter parsers, picker plugins, git, and config validation.


Running Tests

Requires plenary.nvim.

# All tests
make -C lua/agentflow test

# Single file
make -C lua/agentflow test-file FILE=tests/test_planner.lua

About

View and manage the agentic orchestration from neovim.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors