# ๐Ÿฅง Setting Up Pi Coding Agent: Self-Hosted AI Coding Assistant on a VM

**Date:** July 16, 2026  
**Tags:** AI, Pi, Coding Agent, Self-Hosted, 9Router, MCP, AI Agents

---

## ๐ŸŽฏ Overview

[Pi](https://github.com/earendil-works/pi-coding-agent) (the **Pi Coding Agent**) is a fast, terminal-native **AI coding assistant** with read, bash, edit, and write tools. It's the *hands-on coder* of the stack: where [Hermes](/blog/hermes-setup/) orchestrates and [OpenClaw](/blog/openclaw-setup/) gates chat traffic, Pi sits in your shell and actually edits files, runs commands, and drives a coding session.

Pi is model-agnostic โ€” it talks to any OpenAI-compatible endpoint. On this VM we point it at **9Router** (our LAN free-model router) so every coding call is free and auto-rotated. It also speaks **MCP**, so we wire it into **GBrain** (shared memory) the same way the other agents do.

### Why Pi?

| Feature | Benefit |
|---------|---------|
| **โšก Fast CLI** | Runs inline in your terminal, no separate UI |
| **๐Ÿ› ๏ธ Real Tools** | read / bash / edit / write โ€” actual file + shell access |
| **๐Ÿ”Œ MCP Ready** | Connect GBrain, databases, or any MCP server |
| **๐ŸŒ 9Router Native** | Free, auto-rotating models via one endpoint |
| **๐Ÿ’พ Sessions** | Resume, fork, and name coding sessions |
| **๐Ÿ“ฆ Extensible** | Extension packages (e.g. `pi-9router-ext`) |

---

## ๐Ÿ—๏ธ Architecture Overview

```
   Your terminal (VM)
        โ”‚
        โ–ผ
   pi  (coding agent CLI)
        โ”‚  --provider 9router
        โ–ผ
   9Router  http://192.168.51.115:20128/v1
        โ”‚  (auto-rotates free models)
        โ–ผ
   OpenAI-compatible free models
        โ”‚
   pi โ”€โ”€ MCP โ”€โ”€โ–บ GBrain (shared memory / Postgres)
```

Unlike the gateway agents, Pi is **session-scoped**: you launch it per task. We still show an optional systemd wrapper for a persistent background coding session.

---

## ๐Ÿ“‹ Prerequisites

- Linux VM with a `node`/`npm` toolchain (Pi ships as a Node package).
- A model backend โ€” we use **9Router** (see the [9Router guide](/blog/9router-setup/)).
- (Optional) GBrain MCP server for shared memory (see below).
- A non-root user (`ryan` in examples).

> โš ๏ธ **Security note:** Every secret below is a ``. Never commit real keys.

---

## ๐Ÿ“ฆ Step 1 โ€” Install Pi

Pi installs as a global Node package:

```bash
# Install Pi globally
npm install -g @earendil-works/pi-coding-agent

# Symlink lands here on this VM:
# /home/ryan/.hermes/node/bin/pi ->
#   ../lib/node_modules/@earendil-works/pi-coding-agent/dist/cli.js

# Verify
which pi
pi --help
```

If you already have it, update:

```bash
pi update self
# or update pi + extensions
pi update --all
```

---

## โš™๏ธ Step 2 โ€” Configure Providers (models.json)

Pi reads provider + model definitions from `~/.pi/agent/models.json`. Below is a **sanitized** example pointing at 9Router with a fallback chain.

```json
{
  "providers": {
    "9router": {
      "name": "9Router",
      "baseUrl": "http://192.168.51.115:20128/v1",
      "apiKey": "",
      "api": "openai-completions",
      "models": [
        {
          "id": "openrouter/nvidia/nemotron-3-ultra-550b-a55b:free",
          "name": "Nemotron 3 Ultra 550B (via 9Router) โ˜… Primary",
          "contextWindow": 1048576,
          "maxTokens": 65536,
          "reasoning": true,
          "input": ["text"]
        },
        {
          "id": "openrouter/poolside/laguna-m.1:free",
          "name": "Laguna M.1 (via 9Router) โ˜… Fallback 1",
          "contextWindow": 204800,
          "maxTokens": 131072,
          "reasoning": true,
          "input": ["text"]
        },
        {
          "id": "cerebras/zai-glm-4.7",
          "name": "Z.ai GLM-4.7 (via 9Router) โ˜… Fallback 2",
          "contextWindow": 262144,
          "maxTokens": 32768,
          "reasoning": true,
          "input": ["text"]
        }
      ]
    }
  }
}
```

> ๐Ÿ’ก The `apiKey` can be omitted if you export `NINEROUTER_API_KEY` in your shell / service env โ€” Pi reads env vars by default via `--api-key` fallback.

---

## ๐Ÿ”ง Step 3 โ€” Default Settings (settings.json)

`~/.pi/agent/settings.json` sets the default provider/model and any extensions:

```json
{
  "lastChangelogVersion": "0.80.3",
  "packages": [
    "npm:pi-9router-ext"
  ],
  "defaultProvider": "9router",
  "defaultModel": "Free"
}
```

Install the 9Router extension so Pi knows how to talk to your router:

```bash
pi install npm:pi-9router-ext
# List installed extensions
pi list
# Open the resource TUI to toggle what's enabled
pi config
```

---

## ๐Ÿงฉ Step 4 โ€” Wire GBrain via MCP (mcp.json)

Pi supports MCP servers. We connect **GBrain** (the shared memory brain all agents use) via `~/.pi/mcp.json`:

```json
{
  "mcpServers": {
    "gbrain": {
      "command": "/home/ryan/.bun/bin/gbrain",
      "args": ["serve"],
      "env": {
        "GBRAIN_DATABASE_URL": "",
        "GBRAIN_DIRECT_DATABASE_URL": ""
      }
    }
  }
}
```

> ๐Ÿ” The GBrain URLs are Postgres connection strings โ€” store them as `` and inject via env, never commit. Pi will then have `gbrain` tools (search, save pages, query) available inside a coding session.

---

## ๐Ÿš€ Step 5 โ€” Run Pi (Interactive & Non-Interactive)

**Interactive session** (edits files in the current repo):

```bash
# From inside a project directory
pi

# Or pin a provider/model explicitly
pi --provider 9router --model Free
```

**One-shot (non-interactive)** โ€” great for scripts/CI:

```bash
pi --provider 9router --model Free --print --no-session \
  "Add a README with build instructions to this repo"
```

**Resume / fork sessions:**

```bash
# Continue the previous session
pi --continue

# Pick a session to resume
pi --resume

# Use a specific session by ID
pi --session 

# Fork a session into a new branch of work
pi --fork 

# Name a session for later lookup
pi --name "refactor-auth-module"
```

**Use a system prompt / context file:**

```bash
pi --system-prompt "You are a security-focused reviewer." \
   --append-system-prompt ./CONTRIBUTING.md \
   "Review this PR diff for injection risks"
```

---

## ๐Ÿ› ๏ธ Step 6 โ€” Optional: Persistent systemd Session

Pi is usually launched per-task, but if you want a long-running background coding session, wrap it as a user service:

```ini
# ~/.config/systemd/user/pi-coding-agent.service
[Unit]
Description=Pi Coding Agent (persistent background session)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=/home/ryan/PersonalWebsite
ExecStart=/home/ryan/.hermes/node/bin/pi \
  --provider 9router --model Free --session-id pi-background
Environment=NINEROUTER_API_KEY=
Environment=GBRAIN_DATABASE_URL=
Environment=GBRAIN_DIRECT_DATABASE_URL=
Restart=on-failure
RestartSec=10

[Install]
WantedBy=default.target
```

```bash
systemctl --user daemon-reload
systemctl --user enable --now pi-coding-agent.service
systemctl --user is-active pi-coding-agent.service
# -> active
```

> ๐Ÿ“ Most users skip this and just run `pi` in a terminal when needed. The wrapper is only for an always-on coding bot.

---

## ๐Ÿงช Step 7 โ€” Smoke Test

```bash
# 1. Pi binary resolves
which pi

# 2. 9Router is reachable with the key (placeholder)
curl -s --max-time 8 -H "Authorization: Bearer " \
  http://192.168.51.115:20128/v1/models | head -c 200

# 3. Pi answers a trivial prompt non-interactively
pi --provider 9router --model Free --print --no-session "Say: pi online"

# 4. MCP server (gbrain) is reachable
/home/ryan/.bun/bin/gbrain get_health
```

Expected: a JSON `data` array from `/v1/models`, Pi prints `pi online`, and GBrain returns healthy.

---

## ๐Ÿฉบ Troubleshooting

| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| `pi: command not found` | Not on PATH | Use full path `/home/ryan/.hermes/node/bin/pi` or `npm link` |
| Model calls 401 | Wrong/expired 9Router key | Re-export ``, re-test `/v1/models` |
| `provider not found: 9router` | models.json missing provider | Check `~/.pi/agent/models.json` |
| MCP tools missing | gbrain server down / bad env | Run `gbrain get_health`, fix `` |
| Extension not loading | Not enabled in `pi config` | `pi list`, then `pi config` and toggle it on |

---

## ๐Ÿ” Security Checklist

- [ ] `NINEROUTER_API_KEY` is a ``, injected via env, never committed.
- [ ] GBrain Postgres URLs are placeholders; restrict DB user to least privilege.
- [ ] Pi runs as non-root (`ryan`).
- [ ] Background systemd session uses `Restart=on-failure`, not `always`, for a coding bot.
- [ ] MCP env vars live in the unit file / env, not in world-readable config.

---

## โœ… Summary

You now have the **Pi Coding Agent** wired into your AI stack:

- Installed as a global Node CLI (`~/.hermes/node/bin/pi`).
- Routed through **9Router** for free, auto-rotating models (Nemotron 3 Ultra primary, Laguna M.1 + GLM-4.7 fallbacks).
- Default provider/model pinned in `settings.json` (`9router` / `Free`).
- Connected to **GBrain** via MCP for shared memory across all agents.
- Usable interactively (`pi`), one-shot (`--print --no-session`), or as an optional persistent `systemd` session.

Pi completes the trio alongside [Hermes](/blog/hermes-setup/) (orchestration) and [OpenClaw](/blog/openclaw-setup/) (gateway) โ€” all three leaning on [9Router](/blog/9router-setup/) for inference. ๐Ÿš€

---

*No API keys, tokens, or secrets are included in this guide โ€” every value is a `` you supply locally. Built with Astro, GitHub, and Cloudflare Pages.*