Skip to content

Code Snippets Reference

Concrete code examples referenced throughout the handbook.

Extension: Permission Gate

Block dangerous bash commands with user confirmation:

// ~/.pi/agent/extensions/permission-gate.ts
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";

const DANGEROUS_PATTERNS = ["rm -rf", "sudo", "DROP TABLE", "format", "mkfs"];

export default function (pi: ExtensionAPI) {
  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName !== "bash") return;

    const cmd = event.input.command || "";
    const match = DANGEROUS_PATTERNS.find((p) => cmd.includes(p));

    if (match) {
      const ok = await ctx.ui.confirm(
        "Dangerous Command",
        `Command contains "${match}":\n${cmd}\n\nAllow?`
      );
      if (!ok) return { block: true, reason: `Blocked: contains ${match}` };
    }
  });
}

Extension: Oracle (Simplified)

Consult a second model for review and brainstorming:

// ~/.pi/agent/extensions/oracle.ts
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
import { getModel, stream } from "@mariozechner/pi-ai";

export default function (pi: ExtensionAPI) {
  pi.registerTool({
    name: "oracle",
    label: "Oracle",
    description: `Consult a second model for review, debugging, or brainstorming.
      The oracle analyzes and advises but doesn't make changes.`,
    parameters: Type.Object({
      question: Type.String({ description: "What to ask the oracle" }),
      context: Type.String({ description: "Relevant code or situation summary" }),
    }),
    async execute(toolCallId, params, signal, onUpdate, ctx) {
      const oracle = getModel("openai", "gpt-5.2");

      const response = await stream(
        oracle,
        {
          system: `You are a senior engineering oracle. You review, analyze,
            and advise. You do NOT write code directly.`,
          messages: [
            {
              role: "user",
              content: `${params.context}\n\n${params.question}`,
            },
          ],
        },
        { apiKey: process.env.OPENAI_API_KEY! }
      );

      return {
        content: [{ type: "text", text: await response.text }],
        details: { model: "gpt-5.2", role: "oracle" },
      };
    },
  });
}

Extension: Session Recall

Read and summarize another session (cross-session reference):

// ~/.pi/agent/extensions/recall.ts
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { readFileSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";

export default function (pi: ExtensionAPI) {
  pi.registerCommand("recall", {
    description: "Load summary of another session by ID",
    handler: async (sessionId, ctx) => {
      if (!sessionId) {
        ctx.ui.notify("Usage: /recall <session-id>", "error");
        return;
      }

      const sessionsDir = join(homedir(), ".pi", "agent", "sessions");
      // Search all project directories for the session
      const projects = readdirSync(sessionsDir);

      for (const project of projects) {
        const projectDir = join(sessionsDir, project);
        const files = readdirSync(projectDir).filter((f) =>
          f.startsWith(sessionId)
        );

        if (files.length > 0) {
          const content = readFileSync(join(projectDir, files[0]), "utf-8");
          const lines = content.split("\n").filter(Boolean);
          const messages = lines
            .map((l) => JSON.parse(l))
            .filter(
              (e) => e.type === "message" && e.message?.role === "assistant"
            )
            .slice(-5); // Last 5 assistant messages

          const summary = messages
            .map((m) =>
              m.message.content
                .filter((c: any) => c.type === "text")
                .map((c: any) => c.text)
                .join("")
            )
            .join("\n---\n");

          ctx.ui.notify(
            `Loaded session ${sessionId} from ${project}`,
            "success"
          );
          // Inject as context for next turn
          return { inject: summary };
        }
      }

      ctx.ui.notify(`Session ${sessionId} not found`, "error");
    },
  });
}

Skill: Web Search (CLI-based)

A skill file that teaches the agent to use a CLI search tool:

<!-- .pi/skills/web-search/SKILL.md -->
# Web Search

Search the web and fetch page content using CLI tools.

## Tools

### search
Search the web: `search "your query" --max-results 5`
Returns: title, URL, snippet for each result.

### fetch
Read a web page: `fetch <url> --format text --max-chars 5000`
Returns: page content as plain text.

## Usage Pattern
1. Search for information: `search "topic"`
2. Read promising results: `fetch <url>`
3. Synthesize findings for the user

## Notes
- Prefer specific queries over broad ones
- Fetch only the pages that look relevant from search results
- Summarize rather than dumping raw content

AGENTS.md Template

# AGENTS.md

## Project
- Name: [project name]
- Stack: [e.g., Next.js 15, TypeScript, Tailwind, Drizzle ORM]
- Deployed on: [e.g., Vercel, Cloudflare Workers]

## Conventions
- Use pnpm for package management
- Use strict TypeScript
- Components in src/components/
- Keep functions under 30 lines
- Write tests for business logic

## Model Preferences
- Simple file ops / quick questions: switch to "cheap"
- Complex refactoring / architecture: switch to "coding"
- Code review: switch to "review"
- Default to budget-friendly models unless quality is needed

## Do Not
- Commit credentials or .env files
- Modify files in node_modules/
- Run destructive commands without confirmation

Custom Provider Configuration

// ~/.pi/agent/models.json
{
  "providers": [
    {
      "id": "my-ollama",
      "name": "Local Ollama",
      "api": "openai-completions",
      "baseUrl": "http://localhost:11434/v1",
      "models": [
        {
          "id": "llama-3.1-70b",
          "name": "Llama 3.1 70B",
          "contextWindow": 128000,
          "maxTokens": 32000,
          "reasoning": false,
          "input": ["text"]
        }
      ]
    }
  ]
}