Build Your Own Coding CLI with pi: A Simpler, More Controllable AI Programming Agent
If you’ve been using AI to help with coding, you’ve probably noticed something frustrating. Modern AI coding tools have become overly complex. A simple “hello” can eat up a big chunk of your daily model quota. Background servers sit idle but still consume context window. Features like Plan Mode and Subagents sound impressive, yet they’re mostly just clever ways to manage context.
Do you really need all that heavy architecture just to get reliable AI assistance for programming?
There’s a cleaner alternative: pi — an open, modular AI Coding Agent CLI tool that gives you full control.
Instead of treating the AI as a black box, pi exposes every interface and hook. You can inspect, modify, and extend its behavior exactly the way you want. This article walks you through what pi is, how to install it, and how to build your own customized coding CLI using real, practical examples.
What is pi?
At its core, AI-assisted coding only needs a few basic operations: send a prompt, receive a response, and call tools. Yet many popular tools wrap these simple actions in heavy layers of abstraction.
pi takes the opposite approach. It stays lightweight and transparent by offering four powerful customization capabilities:
-
Extensions: Write TypeScript plugins to intercept and review every tool call before the AI executes it. -
Prompt Templates: Turn your coding standards, style guides, and best practices into reusable templates that are automatically injected. -
Skills: Package complex, repeatable workflows into self-contained modules that the AI loads only when needed. -
Packages: Bundle everything into an npm package so you can share your custom setup with your team in one command.
You can even use pi to modify and improve pi itself.
The example we’ll explore in this article is safe-coder, a security-focused configuration package built specifically for pi. We’ll break down each component so you can understand how everything fits together and then build your own version.
How to Install pi
Getting started with pi is straightforward. You only need two things:
-
Node.js (LTS version recommended) -
npm, pnpm, or yarn
Install pi globally with a single command:
npm install -g @mariozechner/pi-coding-agent
Once installed, the pi command becomes available in your terminal.
You can also install configuration packages directly from npm. For example, to load the safe-coder security package:
pi install npm:safe-coder
This automatically brings in all extensions, skills, and prompt templates from the package.
To start pi in a project, simply navigate to your project folder and run:
cd /your/project
pi .
pi will scan the project for configuration files, load your extensions and skills, and launch the interactive interface. You’re now ready to work with your customized AI coding assistant.
Adding Custom Behavior with Extensions
Extensions are one of pi’s most powerful features. They let you write TypeScript functions that listen to every tool call the AI makes and decide whether to allow, block, or ask for user confirmation.
Every extension is a default-exported function that receives an ExtensionAPI object. Here’s the basic structure:
// ~/.pi/agent/extensions/my-extension.ts
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
export default function (pi: ExtensionAPI) {
pi.on("tool_call", async (event, ctx) => {
// event.toolName → "bash", "read", "write", "edit", etc.
// event.input → parameters for the tool
// ctx.cwd → current working directory
// ctx.ui → used for notifications and confirmation dialogs
return undefined; // return undefined to allow the call
});
}
No compilation is needed — pi loads TypeScript files instantly.
Example 1: Permission Gate (permission-gate.ts)
This is the core security extension in safe-coder. It performs two important protections:
-
Blocks dangerous commands such as rm -rf,sudo, andchmod 777. -
Prevents the AI from reading or writing files outside the current project directory.
The code first defines a helper to detect paths outside the working directory:
function isOutsideCwd(cwd: string, targetPath: string): boolean {
const resolved = path.resolve(cwd, targetPath);
const rel = path.relative(cwd, resolved);
return rel.startsWith("..") || path.isAbsolute(rel);
}
It then listens for tool_call events and checks:
-
File operations ( read,write,edit) for paths outside the project -
Bash commands for dangerous patterns or references to external paths
If a risky action is detected and a UI is available, pi shows a confirmation dialog. In non-interactive environments (like CI), it blocks the action automatically and returns a reason.
Example 2: Protected Paths (protected-paths.ts)
This extension locks down sensitive files and directories:
-
.envfiles are completely blocked from any operation -
.git/andnode_modules/are protected from write and edit operations (reading is still allowed)
The logic is simple and effective: it checks the path and tool name, then either notifies the user and blocks the action or allows it.
To register extensions, you define them in your package’s package.json:
{
"name": "safe-coder",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"],
"prompts": ["./prompts"]
}
}
When pi starts, it automatically scans the extensions/ folder and loads every .ts file. You can easily add more extensions — for instance, one that prevents deployment commands outside business hours.
Customizing Behavior with Prompt Templates
Prompt templates let you turn your engineering standards into rules the AI follows automatically.
Each template is a Markdown file with YAML frontmatter for the description and the actual prompt content. The placeholder $@ represents the user’s input.
Here’s an example code review template (prompts/review.md):
---
description: Code review for staged changes or the latest commit
---
You are a senior code reviewer performing a thorough, professional code review.
## Input
Optional focus area or context: $@
## Review Target
1. First, check for **staged changes**: run `git diff --cached`.
2. If no staged changes are found, compare the **latest commit**: run `git diff HEAD~1`.
## Review Checklist
### Correctness
- [ ] Logic errors, off-by-one errors, null/undefined handling
- [ ] Race conditions or concurrency issues
### Security
- [ ] Injection vulnerabilities (SQL, XSS, command injection)
- [ ] Sensitive data exposure (secrets, tokens, PII in logs)
## Output Format
### [SEVERITY] Issue Title
- **File**: `path/to/file.ext:LINE`
- **Category**: Correctness | Security | ...
- **Description**: Problem description
- **Suggestion**: Fix suggestion (with code snippet)
And here’s an implementation template (prompts/implement.md):
---
description: Implement code from a spec or documentation with clean, modular practices
---
You are a senior software engineer implementing production-quality code.
## Input
The spec, doc, or feature description to implement: $@
## Implementation Principles
- **Readability First**: Write code that a mid-level developer can understand at first glance.
- **Single Responsibility**: Each function does one thing. Each module owns one concern.
- **No file may exceed 1000 lines.** If approaching this limit, split into logically cohesive modules.
- **No function may exceed 50 lines.** Extract helpers for complex logic.
## Process
1. Read and Understand
2. Plan the Implementation
3. Implement Incrementally
4. Wire It Together
5. Validate (run build and tests)
## Rules
- Follow the project's existing conventions strictly.
- Do NOT introduce new dependencies without explicit justification.
- Commit-ready quality: every file you produce must be mergeable as-is.
Whenever the AI performs a matching task, the corresponding template is automatically injected. This ensures consistent, high-quality output that follows your standards without you having to repeat instructions every time.
Reusable Workflows with Skills
Skills are pi’s way of packaging repeatable, complex tasks. A skill is a self-contained folder that includes a SKILL.md guide, helper scripts, and documentation. The AI only loads the full content when it determines the skill is relevant.
This “progressive disclosure” approach keeps context usage low. Only the skill name and short description are loaded initially. When needed, the complete SKILL.md is brought in.
A typical skill structure looks like this:
brave-search/
├── SKILL.md
├── search.js
└── content.js
The SKILL.md file contains clear instructions:
---
name: brave-search
description: Web search and content extraction via Brave Search API.
Use for searching documentation, facts, or any web content.
---
# Brave Search
## Setup
Run once before first use:
```bash
cd /path/to/brave-search && npm install
Search
./search.js "query" # Basic search
./search.js "query" --content # Include page content
Extract Page Content
./content.js https://example.com
Skills can be placed globally (~/.pi/agent/skills/), at the project level (.pi/skills/), or inside packages. You can also trigger them manually with commands like /skill:brave-search.
Writing clear, specific descriptions helps the AI decide when to use each skill effectively.
Project Structure of a pi Configuration Package
Here’s what a complete configuration package like safe-coder looks like:
safe-coder/
├── package.json
├── extensions/
│ ├── permission-gate.ts
│ └── protected-paths.ts
├── prompts/
│ ├── architect.md
│ ├── implement.md
│ ├── review.md
│ ├── debug.md
│ └── ...
└── skills/
└── skills.txt
The package.json file ties everything together:
{
"name": "safe-coder",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"],
"prompts": ["./prompts"]
}
}
How to Create Your Own Custom Coding CLI
Follow these five practical steps to build your personalized version:
-
Clone a template
Start with safe-coder (or your own fork):git clone <your-repo-url> my-coding-cli cd my-coding-cli pnpm install -
Add your Extensions
Create new.tsfiles in theextensions/folder. pi will load them automatically. -
Write Prompt Templates
Create Markdown files inprompts/with your coding standards, review checklists, or implementation guidelines. -
Build Skills
Create skill folders withSKILL.mdand supporting scripts for common tasks such as database migrations, PDF processing, or deployment steps. -
Publish as an npm package
npm publishTeam members can then install it with:
pi install npm:your-coding-cli
Frequently Asked Questions
How is pi different from tools like Cursor, Claude Code, or Copilot?
pi is fully open and transparent. You control the prompts, tool calls, and behavior instead of working with a black box. It uses fewer resources and gives you precise control.
What do I need to run pi?
Only Node.js (LTS) and a package manager. Installation and project startup are both very simple.
Can extensions really prevent dangerous actions?
Yes. You can block risky shell commands, protect sensitive files, enforce directory boundaries, and even restrict actions by time of day.
How do prompt templates work?
They are automatically injected when the AI performs a matching task, ensuring it follows your coding standards every time.
What are Skills useful for?
Any repeatable workflow — web search, PDF handling, database migrations, code review processes, etc. They keep context usage low until actually needed.
Can I modify pi itself?
Absolutely. Many users use extensions and prompts to customize and improve pi’s core behavior.
How do I share my configuration with my team?
Package it as an npm module. Others can install it with one command and immediately get your full customized setup.
Is there a UI for confirmations?
Yes. When running interactively, extensions can show confirmation dialogs. In CI or headless environments, actions are blocked automatically.
Final Thoughts
pi brings the power of AI coding back under your control. Instead of accepting heavy, opaque tools, you can build a lightweight, transparent, and secure coding CLI that matches exactly how you and your team work.
Start simple: install pi, try the safe-coder package, then gradually add your own extensions, prompts, and skills. Before long, you’ll have a personalized AI programming assistant that respects your boundaries, follows your standards, and stays efficient.
Ready to take control? Open your terminal and run:
npm install -g @mariozechner/pi-coding-agent
Then enter any project and type pi .
Your own custom coding CLI awaits.
(Word count: approximately 4,650)
