Group staged changes semantically for review, inspection, and selective commits.
ai-split-commit.nvim is a review-oriented plugin. It analyzes your staged diff using AI, proposes semantic groups (e.g., "Fix auth refresh", "Refactor helpers", "Update docs"), assigns a criticality level to each group, and presents everything in a 4-pane review UI. You can move hunks between groups, rename/merge/reorder groups, generate commit messages per group (via ai-commit.nvim), and commit groups individually or all at once β without leaving the UI.
It is not just a commit tool β it is primarily a change-inspection workflow that helps you create clean, focused commits from a messy staging area.
- AI-powered semantic grouping of staged changes
- Criticality levels per group (
high,medium,low) - 4-pane review UI: groups / changes / diff preview / commit message
- Two view modes:
split(detailed) andgroup_diff(wide group diff) - Move hunks between groups
- Create, rename, merge, delete, reorder groups
- Re-group everything with AI at any time
- Generate commit messages per group through
ai-commit.nvim - Batch-generate one commit message per group automatically
- Commit one group or all prepared groups at once
- Stage one group for manual commit
- Binary-safe handling: binary file content is omitted from preview and AI input, while staging/committing still works
- delta support for rich diff rendering
- Blink.nvim-style keymap customization
- Backup ref created before multi-group commits
- Neovim >= 0.8.0
- Git
- plenary.nvim
- ai-provider.nvim
- ai-commit.nvim (optional β needed for
gc/gacommit message generation) - delta (optional β for rich diff rendering)
Using lazy.nvim:
{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim", -- optional, for commit message generation
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
},
}require("ai-split-commit").setup(opts)| Parameter | Type | Default | Description |
|---|---|---|---|
provider |
string |
"openrouter" |
AI provider to use. One of "openrouter", "github-copilot", "anthropic", "google", "openai", "xai", "groq", "cerebras", "mistral". |
model |
string |
"google/gemini-2.5-flash" |
Model ID for the selected provider. Use :AISplitCommitModels to browse available models. |
max_tokens |
number |
4096 |
Maximum output tokens for the AI response. |
max_item_diff_length |
number |
1200 |
Per-item diff truncation (in characters) before sending to AI for grouping. Longer hunks are truncated with ... (truncated). This controls how much context the AI sees per hunk. |
max_group_count |
number |
8 |
Soft cap for the number of groups the AI proposes. The AI is instructed to prefer 1 to this many groups. |
ignored_files |
string[] |
{} |
List of file paths or glob patterns to omit from AI grouping and AI commit-message generation. Matching files remain visible in the review UI and can still be staged/committed manually. |
debug |
boolean |
false |
Save grouping prompt + response transcripts to ~/.cache/nvim/ai-split-commit-debug/ for inspection. |
grouping_prompt_template |
string? |
nil |
Custom user prompt for the grouping request. When nil, the built-in template is used. See Prompt Customization. |
grouping_system_prompt |
string? |
nil |
Custom system prompt for the grouping request. When nil, the built-in system prompt is used. |
default_view_mode |
string |
"split" |
Initial view mode when opening the UI. "split" shows groups/changes/diff columns. "group_diff" hides the changes column and shows a wider diff. |
use_delta |
boolean |
true |
Use delta for rich diff rendering if it is installed. Falls back to plain diff syntax highlighting when delta is not available or this is false. |
keymaps |
table |
{ preset = "default" } |
Blink.nvim-style keymap configuration. See Keymaps. |
keymaps.preset |
string |
"default" |
Base keymap preset. "default" loads the built-in keymaps. "none" starts with no keymaps. |
ai_options |
table |
{} |
Per-request options forwarded to ai-provider.complete_simple(). Use this for request-scoped settings such as reasoning, temperature, headers, or future request parameters added by ai-provider.nvim. |
ai_provider |
table? |
nil |
Full shared ai-provider.setup() passthrough. Use this to configure global ai-provider.nvim behavior such as reasoning, debug, debug_toast, notification, providers, or custom_models directly from ai-split-commit.nvim, with no separate ai-provider.nvim config block required. |
- Use
ai_optionsfor grouping requests issued byai-split-commit.nvim. - Use
ai_providerfor shared/globalai-provider.nvimsetup. debug = trueinai-split-commit.nvimsaves a readable grouping prompt/response transcript.ai_provider.debug = truesaves raw provider-level JSON request/response dumps.- If both
ai-split-commit.nvimand another plugin setai_provider, the resultingai-provider.nvimconfig is shared, becauseai-provider.nvimitself is global. - In normal setups, you do not need a separate
ai-provider.nvimopts = { ... }block at all.
Common patterns:
1. Higher reasoning only for grouping
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
ai_options = {
reasoning = "high",
},
}2. Enable debug dumps + live debug toast globally through ai-split-commit.nvim
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
ai_options = {
reasoning = "high",
},
ai_provider = {
debug = true,
debug_toast = { enabled = true },
notification = { enabled = true },
},
}3. Register or override models without touching ai-provider.nvim directly
opts = {
provider = "openrouter",
model = "google/gemini-3-flash-preview",
ai_provider = {
custom_models = {
openrouter = {
{
id = "google/gemini-3-flash-preview",
name = "Gemini 3 Flash Preview (my preset)",
api = "openai-completions",
provider = "openrouter",
base_url = "https://openrouter.ai/api/v1",
reasoning = true,
input = { "text", "image" },
context_window = 1048576,
max_tokens = 65536,
},
},
},
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
},
}Then authenticate once:
:AISplitCommitLoginexport OPENROUTER_API_KEY=sk-or-...{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "openrouter",
model = "google/gemini-2.5-flash",
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "openrouter",
model = "google/gemini-2.5-flash",
ai_provider = {
providers = {
openrouter = {
api_key = "sk-or-your-key-here",
},
},
},
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "claude-sonnet-4.6",
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
ai_provider = {
providers = {
["github-copilot"] = {
enterprise_domain = "company.ghe.com",
},
},
},
},
}If you only want semantic grouping and inspection without gc/ga commit message generation:
{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
},
}You can still use gs to stage a group and commit manually.
{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
default_view_mode = "group_diff",
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
use_delta = false,
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "openrouter",
model = "google/gemini-2.5-pro",
max_group_count = 12,
max_item_diff_length = 3000,
max_tokens = 8192,
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
keymaps = {
preset = "default",
-- remap
["<Esc>"] = { "close" },
["<C-j>"] = { "move_group_down", "fallback" },
["<C-k>"] = { "move_group_up", "fallback" },
-- disable a key
["q"] = false,
},
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
keymaps = {
preset = "none",
["<Esc>"] = { "close" },
["gc"] = { "generate_commit" },
["ga"] = { "generate_all_commits" },
["cc"] = { "commit_current" },
["ca"] = { "commit_all" },
["<Tab>"] = { "next_pane" },
},
},
}Use a fast model for grouping in ai-split-commit.nvim and a stronger model for commit messages in ai-commit.nvim:
-- ai-split-commit: fast grouping
{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
},
},
-- ai-commit: stronger model for commit messages
{
"cjvnjde/ai-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-telescope/telescope.nvim",
"cjvnjde/ai-provider.nvim",
},
opts = {
provider = "openrouter",
model = "anthropic/claude-sonnet-4",
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
},
},
{
"cjvnjde/ai-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-telescope/telescope.nvim",
"cjvnjde/ai-provider.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
},
}{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
debug = true,
},
}Grouping prompt + response transcripts are saved to ~/.cache/nvim/ai-split-commit-debug/.
{
"cjvnjde/ai-split-commit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"cjvnjde/ai-provider.nvim",
"cjvnjde/ai-commit.nvim",
},
opts = {
provider = "github-copilot",
model = "claude-sonnet-4.6",
max_tokens = 8192,
max_item_diff_length = 2000,
max_group_count = 10,
ignored_files = {
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml",
"dist/*",
},
debug = false,
default_view_mode = "split",
use_delta = true,
keymaps = {
preset = "default",
["<Esc>"] = { "close" },
["<C-j>"] = { "move_group_down", "fallback" },
["<C-k>"] = { "move_group_up", "fallback" },
},
ai_provider = {
providers = {
["github-copilot"] = {
enterprise_domain = "company.ghe.com",
},
},
},
},
}- Stage your changes:
git add .- Open the review UI:
:AISplitCommit- Review the proposed groups
- Move hunks / rename / merge / reorder as needed
- Generate commit messages without leaving the UI
- Edit saved commit messages if you want
- Commit current group or all prepared groups
:AISplitCommit- Navigate between groups
- Press
gcon a group to generate commit message suggestions - Pick a message from
ai-commit.nvimTelescope picker - The message is saved on that group
- Repeat for other groups
- Optionally edit the saved message in the bottom commit-message pane
- Press
ccto commit the current group, orcato commit all prepared groups
Generate commit messages for all groups at once:
:AISplitCommit- Press
ga - The plugin uses each group's name as commit guidance
- It asks
ai-commit.nvimfor one message per group automatically - Press
cato commit all groups
Each group gets a criticality level assigned by the AI:
| Level | Icon | Meaning |
|---|---|---|
high |
β² |
Bug fixes, breaking changes, security-sensitive work |
medium |
β |
Features, important behavior changes, significant refactors |
low |
β½ |
Docs, tests, style, cleanup, comments |
This is a review aid, not a hard rule.
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ¬βββββββββββββββββββββββ
β Groups β Changes β Diff Preview β
β β β β
β >β² Fix auth refresh β lua/provider/init.lua β @@ -10,3 +10,8 @@ β
β [msg] 2f 3i β H1 @@ -10,3 +10,8 @@ β + new auth logic β
β β H2 @@ -48,2 +57,11 @@ β - old handler β
β β Add fallback β β β
β 3f 5i β lua/provider/request.lua β β
β β H3 @@ -90,7 +104,18 @@ β β
β β½ Update README β β β
β [*] 1f 1i β β β
β β β β
β U Unassigned 2i β β β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββ΄βββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Commit Message β
β β
β feat(provider): add fallback routing β
β β
β Add provider fallback handling and update request selection logic. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Groups β Group Diff β
β β β
β >β² Fix auth refresh β diff --git a/... β
β [msg] 2f 3i β @@ -10,3 +10,8 @@ β
β β + new auth logic β
β β Add fallback β β
β 3f 5i β @@ -48,2 +57,11 @@ β
β β - old handler β
β β½ Update README β + updated flow β
β [*] 1f 1i β β
β β β
β U Unassigned 2i β β
ββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Commit Message β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Legend:
f= file count,i= item count (hunks / file-level diff items)[msg]= this group has a saved commit message[*]= this group changed after grouping and should be reviewed again
| Command | Description |
|---|---|
:AISplitCommit [extra prompt] |
Start a review session. Optional extra instructions are passed to the grouping prompt. |
:AISplitCommitModels [provider] |
Browse and select a model for the current or specified provider. |
:AISplitCommitLogin [provider] |
Authenticate with a provider (default: current provider). Required for GitHub Copilot. |
:AISplitCommitLogout [provider] |
Remove stored credentials for a provider. |
:AISplitCommitStatus |
Show current provider, model, and authentication status. |
Keymaps follow the blink.nvim convention. Each entry maps a key to a list of actions. Actions are tried in order β if one returns false/nil (doesn't apply to the current pane), the next action runs. "fallback" at the end of the list feeds the original key to Neovim.
Your custom keys are merged with the preset. Conflicting keys overwrite the preset. Set a key to false or {} to disable it.
{
["q"] = { "close" },
["P"] = { "preview_all" },
["gv"] = { "toggle_view" },
["gs"] = { "stage_group" },
["gc"] = { "generate_commit" },
["ga"] = { "generate_all_commits" },
["cc"] = { "commit_current" },
["ca"] = { "commit_all" },
["<Tab>"] = { "next_pane" },
["<CR>"] = { "confirm", "fallback" },
["a"] = { "add_group", "fallback" },
["e"] = { "rename_group", "fallback" },
["M"] = { "merge_group", "fallback" },
["dd"] = { "delete_group", "fallback" },
["J"] = { "move_group_down", "fallback" },
["K"] = { "move_group_up", "fallback" },
["R"] = { "regroup_all", "fallback" },
["m"] = { "move_item", "fallback" },
["n"] = { "move_item_new", "fallback" },
["x"] = { "unassign_item", "fallback" },
}keymaps = {
preset = "default", -- or "none"
["<Esc>"] = { "close" }, -- add new
["q"] = false, -- disable from preset
["<C-j>"] = { "move_group_down", "fallback" },
["<C-k>"] = { "move_group_up", "fallback" },
-- custom function (return true to consume the key)
["<C-g>"] = {
function(session, role)
if role ~= "groups" then return end
vim.notify("Groups: " .. #session.groups)
return true
end,
"fallback",
},
}| Default key | Action | Description |
|---|---|---|
q |
close |
Close session |
P |
preview_all |
Preview all groups in a new tab |
gv |
toggle_view |
Toggle between split and group_diff view |
gs |
stage_group |
Stage current group only (then close) |
gc |
generate_commit |
Generate commit message suggestions for current group (requires ai-commit.nvim) |
ga |
generate_all_commits |
Auto-generate one commit message for every group (requires ai-commit.nvim) |
cc |
commit_current |
Commit current group using its saved commit message |
ca |
commit_all |
Commit all groups that have saved commit messages |
<Tab> |
next_pane |
Cycle through panes |
<CR> |
confirm |
Context-dependent: focus next pane / message pane |
| Default key | Action | Description |
|---|---|---|
a |
add_group |
Create a new empty group |
e |
rename_group |
Rename current group |
M |
merge_group |
Merge current group into another |
dd |
delete_group |
Delete group (items go to Unassigned) |
J / K |
move_group_down / move_group_up |
Reorder groups |
R |
regroup_all |
Re-run AI grouping on all items |
| Default key | Action | Description |
|---|---|---|
m |
move_item |
Move item to another group |
n |
move_item_new |
Move item to a new group |
x |
unassign_item |
Move item to Unassigned |
Normal editing works. Additionally, gc, ga, cc, ca work from this pane too.
When you press gc or ga, ai-split-commit.nvim calls ai-commit.nvim under the hood:
gcgenerates commit message suggestions for the selected group's diff (not the full staged diff) and opens a Telescope pickergaauto-generates one commit message per group without opening Telescope- The generated message is saved on the group
- The actual
git commithappens later when you pressccorca
If ai-commit.nvim is not installed, gc / ga are unavailable, but inspection, grouping, and manual message editing still work.
| Placeholder | Description |
|---|---|
<items/> |
The list of items with their IDs, paths, and truncated diffs |
<recent_commits/> |
The last 5 commit subjects |
<extra_prompt/> |
Extra instructions passed via :AISplitCommit ... args |
<max_group_count/> |
The configured max_group_count value |
{
"cjvnjde/ai-split-commit.nvim",
opts = {
provider = "github-copilot",
model = "gpt-5-mini",
grouping_prompt_template = [[
Split the staged changes into semantic groups.
Prefer fewer groups. Use all item IDs.
<extra_prompt/>
Recent commits:
<recent_commits/>
Items:
<items/>
]],
},
}When grouping starts, you see a single notification:
Sending AI request: AISplitCommit[grouping] -> github-copilot / gpt-5-mini -> api.githubcopilot.com
- Staged changes only (all changes must be staged)
- Clean worktree required (no unstaged or untracked files)
- Binary file content is not shown in preview and is not sent to AI; only file metadata/path is used
- No missing-newline file markers
- No partial line splitting inside a hunk