No description
  • TypeScript 100%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Brandon Verkamp 1b9f9a64d0 fix: use a dim underline for every unfocused Vim cursor fallback.
The previous insert-vs-normal fallback glyphs were hard to see; a mode-colored underline keeps the caret visible without a hollow block.
2026-09-24 21:40:20 -06:00
modes Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
test feat: expose VimEditor normal-mode capability 2026-09-08 12:37:44 -06:00
config.ts Color cursor by Vim mode 2026-09-01 15:47:23 -06:00
index.ts Color cursor by Vim mode 2026-09-01 15:47:23 -06:00
keys.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
LICENSE Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
motions.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
operators.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
package.json Color cursor by Vim mode 2026-09-01 15:47:23 -06:00
README.md Color cursor by Vim mode 2026-09-01 15:47:23 -06:00
registers.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
repeat.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
search.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
state.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
text-objects.ts Fork pi-vim 1.7.0 2026-09-01 15:24:33 -06:00
vim-editor.ts fix: use a dim underline for every unfocused Vim cursor fallback. 2026-09-24 21:40:20 -06:00

pi-vim

Vim motions extension for pi-coding-agent. Replaces the default input editor with a vim-modal editor supporting normal, insert, visual, and replace modes.

Install

pi install npm:@burneikis/pi-vim

With Fuzzy File Picker

Install pi-fzfp as a separate pi package alongside pi-vim:

pi install npm:@burneikis/pi-fzfp

That's it. When both are installed, pi-vim detects pi-fzfp at startup and integrates its fuzzy autocomplete automatically. pi-fzfp will not install its own editor — pi-vim handles the editor and wraps its autocomplete provider with fzfp's fuzzy matching.

Requires fd and fzf on your PATH.

Configuration

pi-vim reads optional JSON configuration, with project settings overriding global settings:

  • Global: ~/.pi/agent/pi-vim/config.json
  • Project: <project>/.pi/pi-vim/config.json
{
  "disableModeIndicator": true
}

disableModeIndicator defaults to false; set it to true to hide the right-border NORMAL/INSERT/VISUAL label and rely on cursor color instead. Cursor colors use OSC 12, which Sakura/VTE supports; terminals that do not support it retain their configured cursor color.

Features

Vim Motions

  • Normal, Insert, Visual, and Replace modes
  • Mode-colored terminal cursor (Normal = yellow-orange, Insert = green, Visual = light purple), with Insert as a bar and other modes as a block. In tmux, focus hooks provide a dim, mode-colored software fallback on blur (insert = underline; other modes = outlined block)
  • Motions (h, j, k, l, w, b, e, 0, $, gg, G, etc.)
  • Operators (d, c, y, p, etc.)
  • Text objects (iw, aw, i", a(, etc.)
  • Search (/, ?, n, N)
  • Registers and yank/paste
  • Dot repeat

Fuzzy File Picker (optional, via pi-fzfp)

  • Replaces @file autocomplete with weighted dual-key fuzzy matching
  • Basename matches scored 2× higher than path matches
  • Suffix alignment bonus for extension-aware matching (@acts → abct.ts over abct.scss)
  • Path prefix pre-filtering when query contains /
  • Test file penalty as a tiebreaker

Integration Protocol

pi-vim and pi-fzfp coordinate via pi.events so they work regardless of which extension loads first. Other custom editors can use the same protocol to integrate pi-fzfp.

As an editor extension that wants pi-fzfp integration

Register listeners during your extension factory (before session_start), so they are in place regardless of load order:

import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import type { AutocompleteProvider } from "@mariozechner/pi-tui";

export default function (pi: ExtensionAPI) {
  let wrapAutocomplete: ((provider: AutocompleteProvider) => AutocompleteProvider) | undefined;

  // Tell pi-fzfp not to set its own editor component.
  pi.events.on("pi-fzfp:check-editor", (ack: () => void) => { ack(); });

  // Receive the provider (pi-fzfp emits this from both its factory and
  // session_start to cover both load orderings).
  pi.events.on("pi-fzfp:provider", (fn: (provider: AutocompleteProvider) => AutocompleteProvider) => {
    wrapAutocomplete = fn;
  });

  pi.on("session_start", (_event, ctx) => {
    ctx.ui.setEditorComponent((tui, theme, keybindings) =>
      new MyEditor(tui, theme, keybindings, wrapAutocomplete)
    );
  });
}

Then apply wrapAutocomplete inside your editor's setAutocompleteProvider:

override setAutocompleteProvider(provider: AutocompleteProvider): void {
  super.setAutocompleteProvider(this.wrapAutocomplete ? this.wrapAutocomplete(provider) : provider);
}