A note on neovim-like coding agent harnesses
Every few months there is another new coding agent, another coding agent from some frontier AI lab. Codex, Claude Code, Factory Droid, Cursor, Kimi Code, Mistral Vibe, Copilot, and you name it. Each of these coding agents is quite opinionated. Each coding agent adds features that they think you may need, while the truth is that many features are unnecessary and might make the software more buggy in the end. This makes you, the user, rent someone else’s setup and opinion of how to code with an LLM.
What if you could build your own custom coding agent and agent harness from a minimal foundation? If you are into, or familiar with, bare-bones minimal text editors like Vim/Neovim or Emacs that are very hackable, customizable, and can be riced further, you would love to have a coding agent with a similar philosophy. You could build your own TUI, rendering process, extensions, plugins, harness, agent behavior, etc. and customize it as much as you want.
Introducing pi, a minimal (terminal) coding agent, designed to adapt to your WORKFLOW instead of you adapting to the existing coding agent. Designed to let you build your own plugin, workflow, extension, etc. (you name it: skills, prompt template, TUI, etc) on your own just by asking (prompting) directly on pi. You can also ship your own plugin or package easily to other users like lazyvim via npm or git.
There are many coding agents, but this one is mine.
Last year I saw someone try to make a very very simple coding agent on the everything app, X, and name it shittycoding agent, as I remember. Back then, it was literally just a stupidly simple terminal coding agent tool. No plan mode, askQuestion tool, or sub-agent like Claude Code. Only read
, write
, edit
, and bash
. My initial response was
Stupid simple coding agent, just a read, write, and bash tools, cool
That’s it. I had no intention of trying it or any interest in it. I thought that was someone’s side project trying to build a stupidly simple coding agent tool to better understand how it works under the hood (no offense to the creator, I didn’t know him back then).
Fast forward, you know it. OpenClaw. That god damn AI assistant tool speedran GitHub stars growth and went viral on the internet. It ran on top of the Pi SDK.
OpenClaw also documents its Pi integration architecture.
There was a time when I was furiously using OpenClaw all the time as my go-to daily driver, especially for my job too. When I checked the docs and OpenClaw codebase, I realized Pi was the powerhouse, one of the core infrastructures powering OpenClaw itself.
At the time, I was looking for a simple coding agent that was very hackable for my workflow, kind of like Neovim, a terminal IDE that is very hackable and customizable, where I can build my own configuration without unnecessary features or bloat in the end. I was considering OpenCode as a top contender, but it was more like Helix than Neovim for me (this is a compliment). It was well designed in my opinion, way better than the first time I used it, but that’s it. It wasn’t intended to be fully customizable or very hackable, even though you can easily build an external plugin on top of OpenCode itself.
After a few weeks of doomscrolling on X, I saw several users sharing their experiences with Pi, specifically the creator of Flask, Armin Ronacher, in PI: The Minimal Agent Within OpenClaw. My curiosity kicked in, and it was time to try it myself. A few Pi users also shared Pi extensions and plugins they made, similar to how Neovim users share their own plugins that can be plugged in easily using LazyVim.
As I’m writing this blog, since this post has been drafted for a few months, Pi itself was finally acquired by Earendil, a company founded by Armin Ronacher and Colin Daymond. So it’s basically company software (don’t worry, still open source and properly maintained though).
As a fellow Nix user, I installed pi via llm-agents.nix by Numtide. You can also use the version available on nixpkgs, but I chose the Numtide one because it is managed and automatically pinned to the newest version. They also have their own caching workflow (aka I trust Numtide to maintain my own AI tooling package as I’m too lazy to do it myself).
It was very simple, and even the TUI itself was pretty straightforward. As I had heard that the power of Pi was being able to extend it and make customized plugins, the first thing I did on Pi was ask for a simple feature. It was a todo
task tool and an AskUser
tool.
It was straightforward. Pi read my global Pi npm
directory, where the Pi library source code and docs are located, especially the docs about custom tools and extensions. So the default harness already injects its guidance/docs pointer into the agent context at runtime, especially if we, the user, mention Pi.
Then, out of curiosity, I started asking Pi to customize and tweak the Pi TUI itself, which worked quite great. So not only are we able to customize the features and tools of Pi itself, but we can also design and hack the Pi TUI itself. You can even play Doom on Pi.
It reminds me of Neovim, customizing your own IDE from a simple plain text editor into a sophisticated IDE adjusted to your coding style and workflow.
Similar to how OpenCode works, you can also use your own Claude OAuth (RIP, it used to :( ), Codex OAuth, Antigravity OAuth (also RIP), Kimi Coding Subs, BYOK, etc.
It was literally a near-perfect coding agent for me personally due to how it was designed, which is somehow similar to Vim or Neovim. I can customize it as much as I want based on my workflow. I can also use someone’s package, similar to how I can use someone’s Neovim configuration or plugin from GitHub.
To show what makes Pi special among available coding agents right now, we can use an analogy where Pi is similar to how Neovim works:
Core app + extensions- Neovim loads Lua/Vimscript for plugins
- Pi loads TypeScript for plugins
Custom commands- Neovim plugins add commands like
:Telescope
or:<Leader-key><Map>
-
Pi lets you add your own slash commands aka prompts/skills like
/AskUser -
Neovim plugins add commands like Custom UI- Neovim plugins can create floating windows, status lines, layouts, etc.
-
Pi extensions can be used for custom TUI components, dialogs, etc.
Configuration directories- Neovim reads from places like
~/.config/nvim
- Pi reads extensions from
~/.pi/
similar to/.claude/
and~/.codex/
- Neovim reads from places like
While you are using Neovim for reading, editing, and writing code/text, you are using Pi to run a coding agent with basic tools like read
, edit
, and bash
.
I even save my own Pi configurations and extensions and treat them similarly to how I manage and save my own Neovim configurations and customizations on my dotfiles repo.

If we are talking resource-utilization-wise, Pi already mogged both Claude Code and OpenCode. Only Codex remains to be mogging here. So it’s more lightweight and less bloated than Claude Code here, even though both coding agents are written with TypeScript. You know, memory or RAM usage is one of the most important things if we are going to agentmaxxing.

Imagine running a bunch of coding agents on different tmux panes and your memory is already full despite not using Windows due to coding agents’ bad memory utilization.
The powerful feature of Pi is plugin creation, which is separated from the core software, so you can extend it as much as you want and need. It’s up to you if you want to make your Pi lean enough for your own workflow or add a bunch of plugins and make Pi into bloatware. Although most of the Pi plugins I use were directly made by asking through prompts on Pi, you can browse a bunch of plugins available to download via npm on Pi Packages.
Whenever you need a subagent, web access, an MCP adapter, a todo task tool, a browser agent, etc., maybe someone has already made it as an extension or package that is mature and bug-free enough to install directly, rather than having to iterate on your own extension until it becomes usable enough.
Even though most of my Pi extensions were iterated on by myself, I also used and installed pi-acp, an ACP adapter for Pi, so I can use Pi on my Zed IDE directly. If someone’s extension is already good and usable enough for our workflow, I don’t think we need to make it from scratch unless we have more reasons for it, such as customizability and ownership (you can also just copy and paste the extension code into your .pi/
though).
Now, let’s talk about the tradeoff of using Pi and compare it directly with another coding agent as well. There are too many coding agents currently, such as Claude Code, Codex, OpenCode, Droid, etc. In this blog, I’ll pick opencode to compare with Pi head to head since both are popular, fully open-source coding agents, and you can easily plug any model provider into both of them (no vendor lock-in).
Feature Design #
The main difference between these two is how the coding agents are designed to be used. Pi is more like a bare-metal mechanical keyboard that you customize by yourself, while OpenCode is designed like a more polished and finished mechanical keyboard that you can still tinker with. It comes with some guidance on how to assemble and disassemble it, while the materials and how it is built are fully transparent. Or maybe we can use an IDE analogy for this one:
OpenCode = Helix Pi = Neovim
I use “Helix” as an example above because Helix as an IDE is a more polished and already battery-included text editor, but it is still extensible. Unlike Neovim, which is a very bare-minimum text editor, most of the time you have to add the features you want by yourself or use someone’s plugin for that (composable though).
OpenCode ships with a built-in LSP, plan mode, subagents, multi-session, and an IDE extension. It also has a native desktop app and server configuration if you like to make it a remote gateway too. You can write your own plugin on top of that, but the core itself is already a full IDE replacement.
Meanwhile, Pi only ships with read, write, search, and bash. Anything else might be either installed from someone who made a plugin or made by yourself.
Plugin Model #
While both of them support external plugins, custom tools, and even more customized harness configurations, and both use the same main plugin language, TypeScript, their plugin model design and philosophy are quite different.
As OpenCode itself is somehow already a “complete product” here, OpenCode’s plugin model is more like an external hook that can be integrated into the product itself.
Pi feels more like the agent harness itself is programmable, either globally or at the project level (locally), so we can register tools, slash commands or skills, shortcuts, flags, event handlers, custom UI components, custom object renderers, etc. The extension or plugin design in Pi feels more like having a small framework or an entire customized harness as part of your own coding agent, rather than “it’s just a plugin”.
The tradeoff based on the plugin model itself here is obvious. OpenCode gives you more out of the box, as the plugin surface is useful but unnecessary if you are mostly doing the work inside OpenCode’s existing product shape. Pi gives you a deeper, more hackable and tweakable extension model, but that means you may need to assemble more and maintain your own preferred setup/workflow here. If you want some control over your coding agent and love to build your own tool until it feels like your own coding agent, Pi is more satisfying and suitable for your case. If you want an already finished, polished agent (battery included type of software) with extensibility on the side, OpenCode is probably better for you.
Harness Cost & Performance #
It’s common knowledge that using the same model with different coding agents can affect the model’s performance and output. There are a lot of benchmarks and evals made by several people that you can google or search for on X. Harnesses also affect the cost per task, as you know that every coding agent has its own built-in internal system prompt to optimize the input and output of the model itself. Personally, the best thing about Pi is how minimal its internal system prompt is (or maybe we can call it part of the harness). Not only does it make the coding agent more lightweight and customizable, but a minimal harness is also able to make a huge difference cost-wise.
Here, using Databricks’ internal benchmark, the Pi harness achieved roughly the same success rate as the models’ native vendor harnesses (both Opus 4.8 and GPT 5.5). Depending on the model and reasoning level, Pi was between 1x and 2x cheaper per task.
This is important, as the definition of “harness” is not merely a UX wrapper around the model, but also includes how it manages the user’s input/output, context, tools, and the loop around those things, which directly affects how many tokens are consumed per task. A more feature-packed harness does not mean that it is automatically more effective (in this case at least).
I have separate Pi configurations on my personal MacBook and Linux desktop (CachyOS, yes not fully NixOS yet :p). Despite the different configurations, there are similar Pi extensions on both machines, but I made them from scratch, iteratively from my own Pi sessions anyway.
My Pi harness is very simple, nothing complex nor complicated. Since Pi is already good enough for me, and most of the time I configure my Pi harness at the project level rather than globally, I just need a few features, which are:
grill-me skillby mattpocock. It interviews the user (me) about a design or plan relentlessly. Basically, it keeps questioning my choices until I’m ready to go. -
jujutsuas the main version control system in my globalkaisenAGENTS.md
rather thangit
.
## Version Control
Prefer Jujutsu (`jj`) over Git for source-control operations.
- Use `jj status` instead of `git status`.
- Use `jj diff` instead of `git diff`.
- Use `jj log` instead of `git log`.
- Use `jj describe` for change descriptions.
- Use `jj new`, `jj split`, and `jj squash` for change management.
- Avoid `git add`, `git commit`, and direct index/staging workflows unless explicitly requested.
- Git commands are acceptable only for operations that `jj` cannot perform or when the user asks for Git specifically.
- When reporting changes, mention the active `jj` change/workspace state rather than Git staging state.
Why
jj
over good oldgit
? I found jj’s mental model simpler than Git, especially for AI-assisted coding. In jj, there is no staging area by default, and operations such as split, squash, and rebase are first-class operations. So instead of thinking like this:
edit files -> stage -> commit -> amend(?) -> rebase maybe(?)
The mental model would be much simpler and more straightforward:
change/edit -> inspect diff -> split/squash/describe/move it later
You can also do stacked diffs through jj
easily because jj
treats changes as mutable and makes them easy to rebase. This is very useful for an agentic coding workflow since agent output is usually not perfect one-shot godtier code quality on the first try, so usually we need to reshape the previous stacks (and commits).
While jj
itself does not resolve merge conflicts magically and conflicting edits may still exist as usual even when using jj
, personally, conflict handling with jj
feels less disruptive than Git because conflicts can be carried as state and resolved later. For an agentic coding workflow, it matters because agent output is often messy and the work is iterative. I only need the output to be manageable enough and able to be reshaped easily afterward.
exa searchfor third-partyagenticweb search integration. While you can actually fetch the web easily on pi using a bash command likecurl
, I prefer a third-party search API here so my agent can easily search for specific information or docs, find the right context with efficient token usage, and avoid getting blocked too. Exa search itself has a few features for specific use cases, likeExa Code, which I included here to reduce code hallucination and provide efficient web context for coding work.
I also added custom model inference from crof.ai for cheap custom GLM 5.2 Q8 inference, which you can check on their website, with fast enough TPS (around 60-70 if I remember).
Well that’s it. I didn’t add many plugins; I just add what I need most of the time. You can also edit your own features, remove them easily, or import someone’s extension. You have control over your own coding agent features, while the core system itself is diligently updated by senior experienced developers that I trust.
Maybe some of you do not want to read all of this after all, so I condensed the whole blog here for short attention-span enjoyer ppl who want a quick summary.
- Use codex orclaude code if you do not want to own or tweak the harness, specifically want to use their frontier models, and want something that is literally plug-and-play with easily available models.Note: codex is more hackable than claude code and you can easily hack or configure it as codex itself is OSS. - Use Opencode if you want a polished coding agent with strong defaults (battery included). - Use Pi, the coding agent that is literally what I’m talking about here, if you want to own the harness, the workflow, and your weird personal configurations of how you want it to work while the core system itself is being maintained regularly. - I chose Pi because I don’t need too many features. I just need a good enough foundation and a very simple coding agent that I can tinker with and configure to my workflow style, similar to Neovim, as they say: There are many coding agents, but this one is mine.