Skip to content

Instantly share code, notes, and snippets.

@mitchross
Last active June 26, 2026 16:09
Show Gist options
  • Select an option

  • Save mitchross/bfa86a4b36a7008aa0346fe8787ae220 to your computer and use it in GitHub Desktop.

Select an option

Save mitchross/bfa86a4b36a7008aa0346fe8787ae220 to your computer and use it in GitHub Desktop.
Pi Agent Setup

My Pi agent setup (pi.dev)

How my Pi coding agent is configured — extensions, instructions, skills, MCP servers, LSP, and model provider. Share this with a friend to copy my setup, or use it to rebuild on a new machine.

Pi keeps everything under ~/.pi/agent/. Secrets (auth.json) are not here — run pi once and log in to regenerate them. Infra details (IPs, hostnames, my vLLM endpoint) are redacted to placeholders like <PROXMOX_IP> / <VLLM_HOST>.


1. Extensions I have installed (the important part)

Pi extensions are npm packages. I run exactly these 8 (verify yours anytime with pi list). To get the same setup, paste these:

pi install npm:@dreki-gg/pi-lsp              # language-server (LSP) support
pi install npm:@d3ara1n/pi-ask-user          # lets the agent ask me questions
pi install npm:pi-web-access                  # web search + fetch
pi install npm:pi-mcp-adapter                # MCP server support
pi install npm:@narumitw/pi-chrome-devtools  # Chrome DevTools browser automation
pi install npm:@narumitw/pi-subagents        # subagents
pi install npm:@narumitw/pi-goal             # goal tracking
pi install npm:@narumitw/pi-statusline       # status line

pi install adds each to the packages list in settings.json and pulls the npm package. Exact pinned versions are in npm__package.json.

2. My instructions

AGENTS.md is my global operating-rules file (Pi auto-loads AGENTS.md / CLAUDE.md as context). It covers: search-don't-guess, ask-before-assuming, read-only-first debugging for k8s/Proxmox/ZFS, GitOps PR-only rules, the Chrome image budget, and browser-scraping patterns. Drop it at ~/.pi/agent/AGENTS.md.

3. My skill

homelab — read-first SSH diagnostics for Proxmox + TrueNAS (qm, zpool, zfs, smartctl, midclt). Goes to ~/.pi/agent/skills/homelab/SKILL.md.

4. Config files

Gist file Restore to What it is
settings.json ~/.pi/agent/settings.json extension list + theme
npm__package.json ~/.pi/agent/npm/package.json pinned extension versions
AGENTS.md ~/.pi/agent/AGENTS.md my instructions
mcp.json ~/.pi/agent/mcp.json k8s MCP server
models.json ~/.pi/agent/models.json my vLLM model provider
extensions__lsp__config.json ~/.pi/agent/extensions/lsp/config.json LSP servers
skills__homelab__SKILL.md ~/.pi/agent/skills/homelab/SKILL.md homelab skill

Gist filenames can't contain /, so __ stands in for a path separator.


Full restore on a new machine

# 1. Install Pi (see pi.dev). Here it runs via a node install.

# 2. Pull this gist
gh gist clone <THIS_GIST_ID> /tmp/pi-backup

# 3. Lay files into ~/.pi/agent/
mkdir -p ~/.pi/agent/extensions/lsp ~/.pi/agent/skills/homelab
cp /tmp/pi-backup/settings.json                ~/.pi/agent/settings.json
cp /tmp/pi-backup/mcp.json                      ~/.pi/agent/mcp.json
cp /tmp/pi-backup/models.json                   ~/.pi/agent/models.json
cp /tmp/pi-backup/AGENTS.md                      ~/.pi/agent/AGENTS.md
cp /tmp/pi-backup/extensions__lsp__config.json  ~/.pi/agent/extensions/lsp/config.json
cp /tmp/pi-backup/skills__homelab__SKILL.md      ~/.pi/agent/skills/homelab/SKILL.md

# 4. First run installs the extensions from settings.json + prompts login
pi
pi list   # confirm all 8 extensions are present

External dependencies (set these up too)

  • MCP k8s needs bunx (Bun) + a valid ~/.kube/config.
  • LSP servers on PATH: typescript-language-server, pyright-langserver, ruff, gopls, yaml-language-server.
  • Model provider vanillax-vllm points at <VLLM_HOST>/v1 (must be reachable).
  • Chrome profile for Facebook Marketplace: ~/.pi-chrome-profile on CDP port 9222 (see AGENTS.md). Not backed up — holds a logged-in session.

Pi version at backup time: 0.80.2


Appendix — full file contents (copy these verbatim)

~/.pi/agent/settings.json

{
  "packages": [
    "npm:@dreki-gg/pi-lsp",
    "npm:@d3ara1n/pi-ask-user",
    "npm:pi-web-access",
    "npm:pi-mcp-adapter",
    "npm:@narumitw/pi-chrome-devtools",
    "npm:@narumitw/pi-subagents",
    "npm:@narumitw/pi-goal",
    "npm:@narumitw/pi-statusline"
  ],
  "lastChangelogVersion": "0.80.2",
  "theme": "dark"
}

~/.pi/agent/npm/package.json

{
  "name": "pi-extensions",
  "private": true,
  "dependencies": {
    "@d3ara1n/pi-ask-user": "^1.0.0",
    "@dreki-gg/pi-lsp": "^0.4.1",
    "@narumitw/pi-chrome-devtools": "^0.6.2",
    "@narumitw/pi-goal": "^0.6.2",
    "@narumitw/pi-statusline": "^0.6.2",
    "@narumitw/pi-subagents": "^0.6.2",
    "pi-mcp-adapter": "^2.10.0",
    "pi-web-access": "^0.10.7"
  }
}

~/.pi/agent/mcp.json

{
  "mcpServers": {
    "k8s": {
      "command": "bunx",
      "args": [
        "-y",
        "kubernetes-mcp-server@latest",
        "--read-only",
        "--kubeconfig",
        "<HOME>/.kube/config"
      ],
      "lifecycle": "lazy"
    }
  }
}

~/.pi/agent/models.json

{
  "providers": {
    "vanillax-vllm": {
      "baseUrl": "https://<VLLM_HOST>/v1",
      "api": "openai-completions",
      "apiKey": "vllm",
      "compat": {
        "supportsDeveloperRole": false,
        "supportsReasoningEffort": false,
        "supportsUsageInStreaming": false,
        "maxTokensField": "max_tokens",
        "thinkingFormat": "qwen-chat-template"
      },
      "models": [
        {
          "id": "qwen3.6-27b",
          "name": "Qwen3.6 27B (vLLM)",
          "reasoning": true,
          "input": ["text", "image"],
          "contextWindow": 262144,
          "maxTokens": 32768,
          "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
        }
      ]
    }
  }
}

~/.pi/agent/extensions/lsp/config.json

{
  "lsp": {
    "typescript": {
      "command": ["typescript-language-server", "--stdio"],
      "extensions": [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"]
    },
    "python": {
      "command": ["pyright-langserver", "--stdio"],
      "extensions": [".py"],
      "initialization": {
        "python": { "analysis": { "typeCheckingMode": "basic" } }
      }
    },
    "ruff": {
      "command": ["ruff", "server"],
      "extensions": [".py"]
    },
    "go": {
      "command": ["gopls"],
      "extensions": [".go"]
    },
    "yaml": {
      "command": ["yaml-language-server", "--stdio"],
      "extensions": [".yaml", ".yml"]
    }
  }
}

~/.pi/agent/AGENTS.md

# Operating rules

## Search, don't guess
If unsure about a current API, library version, CLI flag, CRD field, Helm value,
or anything that may have changed recently, you MUST use web search before
answering. Never state versions, flags, or config keys from memory when a search
can verify them. Prefer official docs and source repos over blogs.

## Ask, don't assume
If a task needs a value I haven't specified — port, namespace, hostname, secret
location, image tag, branch, or any decision with more than one reasonable answer
— ask me with explicit options instead of picking one. Always ask before
guessing on infrastructure changes.

## Debugging discipline
- Kubernetes: inspect READ-ONLY first. Pod status, events, logs before proposing
  changes. Never delete/scale without asking.
- Infra (Proxmox/ZFS/TrueNAS): SSH commands hit production hardware. Show the exact
  command and expected effect before anything destructive (zpool, qm, zfs destroy).
  Read state first.
- React Native / web: reproduce and describe what you see before editing. Prefer
  reading the DOM/text over screenshots (see Image budget). Screenshot only when a
  visual/layout question genuinely can't be answered from text.

## GitOps
- GitOps repo: NEVER push to the deploy branch. Feature branch + PR only; ArgoCD
  deploys. Check diagnostics on manifests before opening a PR.

## Image budget (HARD LIMIT — qwen3.6 via vLLM)
The model accepts **at most 4 images per request**, and Pi resends the whole
conversation every turn. `chrome_devtools_screenshot` embeds an inline image that
is NEVER freed, so the 5th screenshot in a session permanently breaks the chat
with `400 At most 4 image(s) may be provided in one prompt`.
- Treat screenshots as a scarce, non-renewable resource: **2 per session, max.**
- NEVER screenshot to read text, prices, listings, tables, or DOM data. Use
  `chrome_devtools_evaluate` and return JSON/text instead — it costs zero images.
- If you ever see the "At most 4 images" error, the session is wedged: tell me to
  `/clear` (or compact). Do not keep retrying — every retry fails identically.

## Browser scraping with Chrome DevTools
Workflow: `chrome_devtools_navigate` → wait → `chrome_devtools_evaluate` returning
JSON. Extract structured data in ONE evaluate call; never screenshot-and-read.
Pattern (adapt the selectors per site):
```js
(() => {
  const out = [];
  document.querySelectorAll('SELECTOR_FOR_EACH_ROW').forEach(c => {
    out.push({
      title: c.querySelector('TITLE_SEL')?.innerText?.trim() || '',
      price: c.querySelector('PRICE_SEL')?.innerText?.trim() || '',
    });
  });
  return JSON.stringify(out);
})()

eBay note: real (non-headless) Chrome passes the "Pardon Our Interruption" challenge automatically after wait --load networkidle; cards are li.s-card, title .s-card__title, price .s-card__price. Headless Chrome gets blocked.

Facebook Marketplace (needs the logged-in CDP browser)

Marketplace shows nothing logged-out and is login-walled. There is a persistent, already-logged-in Chromium for exactly this: profile ~/.pi-chrome-profile on CDP port 9222 — chrome_devtools connects to it by default. DO NOT rely on pi's auto-launched browser: it uses a throwaway temp profile (deleted each run), so it is never logged in. If chrome_devtools_navigate to Marketplace shows a login form, the 9222 browser isn't running — tell me to start it (systemctl --user start pi-chrome if installed, else launch chromium with --remote-debugging-port=9222 --user-data-dir=~/.pi-chrome-profile). Never type my FB credentials; I log in myself in that window.

Search URL (location slug + local pickup): https://www.facebook.com/marketplace/<city-slug>/search?query=<q>&exact=false&deliveryMethod=local_pick_up e.g. city-slug grand-rapids. After navigate, wait --load networkidle then wait 2200 (lazy load), then extract. Cards are obfuscated — DON'T guess class names; instead collect item anchors and parse their text:

(() => {
  const seen=new Set(), out=[];
  document.querySelectorAll('a[href*="/marketplace/item/"]').forEach(a=>{
    const id=(a.href.match(/item\/(\d+)/)||[])[1]; if(!id||seen.has(id))return; seen.add(id);
    const L=a.innerText.split('\n').map(s=>s.trim()).filter(Boolean);
    const price=L.find(l=>/^\$[\d,]/.test(l))||'';
    const loc=L.find(l=>/, [A-Z]{2}$/.test(l))||'';
    const title=L.filter(l=>l!==price&&l!==loc&&!/^\$/.test(l)).sort((a,b)=>b.length-a.length)[0]||'';
    out.push({price,title:title.slice(0,70),loc});
  });
  return JSON.stringify(out);
})()

Results carry per-listing town; FB's radius is approximate, so filter by town distance yourself rather than trusting a radius param.

Style

  • Read neighboring files first. Minimal, reviewable diffs. I review every PR.
  • I do all git commits and pushes myself. Never run git add/commit/push without an explicit instruction.

## `~/.pi/agent/skills/homelab/SKILL.md`

```markdown
---
name: homelab
description: Read-first diagnostics for the homelab Proxmox host and TrueNAS box over SSH (qm, zpool, zfs, smartctl, arcstat, midclt). Use when inspecting VM, ZFS pool, disk, or storage health. Destructive ops require explicit confirmation.
---

# homelab diagnostics

Hosts:
- proxmox = <PROXMOX_IP>
- truenas = <TRUENAS_IP>

## SAFE — read-only, run freely

```bash
ssh proxmox "qm list; pvesm status; cat /proc/loadavg"
ssh proxmox "zpool status; zpool list; zfs list -o name,used,avail,compressratio"
ssh truenas "zpool status; zpool iostat -v 1 3; arcstat 1 3"
ssh truenas "smartctl -a /dev/<disk>"        # SMART for the SAS/SATA array

TrueNAS: prefer the middleware API over poking the host where possible — respects the appliance model:

ssh truenas "midclt call pool.query | jq '.[].name'"
ssh truenas "midclt call disk.query"

DESTRUCTIVE — NEVER without explicit confirmation

zpool destroy/replace/offline, zfs destroy, qm stop/destroy, iLO/firmware, reboot, dd, mkfs.

Rule

Read state first. Show the exact command and its expected effect. Ask before acting on anything that mutates pools, VMs, or disks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment