Skip to main content

pi-coding-agent-extensions

Build and use custom extensions for Pi Coding Agent, the open-source alternative to Claude Code

Aller à l'installation

Informations de source

Dépôt
reason-machines/claude-code-skills
Dernière activité de la source
1 juin 2026 à 13:23
Langue détectée de SKILL.md
anglais
Étoiles
4
Forks
1

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
pi-coding-agent-extensions
description
Build and use custom extensions for Pi Coding Agent, the open-source alternative to Claude Code
triggers
["how do I extend pi coding agent","create a custom pi extension","add a widget to pi agent","build a pi coding agent extension","customize pi agent ui","make a pi agent tool","orchestrate multiple pi agents","pi agent communication between agents"]
# Pi Coding Agent Extensions > Skill by [ara.so](https://ara.so) — Claude Code Skills collection. ## Overview Pi Coding Agent is an open-source terminal-based AI coding assistant similar to Claude Code. This project (`pi-vs-claude-code`) provides a collection of production-ready extensions that showcase Pi's customization capabilities: custom UI widgets, multi-agent orchestration, safety auditing, cross-agent integrations, and inter-agent communication. Extensions are TypeScript files that hook into Pi's lifecycle events to add tools, modify UI, intercept commands, and coordinate with other agents. ## Prerequisites All three are required: ```bash # Install Bun (runtime & package manager) curl -fsSL https://bun.sh/install | bash # Install just (task runner) brew install just # macOS # or cargo install just # cross-platform # Install Pi Coding Agent CLI # See https://github.com/mariozechner/pi-coding-agent ``` ## Installation ```bash # Clone the extensions repository git clone https://github.com/disler/pi-vs-claude-code.git cd pi-vs-claude-code # Install dependencies bun install # Copy environment template cp .env.sample .env # Edit .env and add your API keys: # OPENAI_API_KEY=sk-... # ANTHROPIC_API_KEY=sk-ant-... # GEMINI_API_KEY=... # OPENROUTER_API_KEY=... ``` ## API Key Setup Pi does NOT auto-load `.env` files. You must source keys before launching: **Option 1 - Manual source:** ```bash source .env && pi ``` **Option 2 - Shell alias (add to `~/.zshrc`):** ```bash alias pi='source $(pwd)/.env && pi' ``` **Option 3 - Use just (automatic):** ```bash just pi # .env is loaded automatically ``` ## Running Extensions ### Single Extension ```bash pi -e extensions/minimal.ts ``` ### Stack Multiple Extensions ```bash pi -e extensions/minimal.ts -e extensions/cross-agent.ts ``` ### Using Just Recipes ```bash # List all available recipes just # Common recipes just ext-minimal # Compact footer with context meter just ext-tool-counter # Rich footer with tool usage stats just ext-subagent-widget # Spawn background agents with live widgets just ext-agent-team # Multi-agent orchestration dashboard just ext-damage-control # Safety auditing with path controls just ext-pi-pi # Meta-agent that builds Pi agents just coms-net-server # Start agent-to-agent communication hub just coms # Connect to coms hub just all # Open every extension in separate terminals ``` ## Extension Architecture Extensions export a `PiExtension` object with lifecycle hooks: ```typescript import type { PiExtension, PiApp } from "pi"; export default { name: "my-extension", version: "1.0.0", // Called when extension loads async init(app: PiApp) { // Register tools, widgets, commands }, // Called before agent processes a turn async beforeTurn(app: PiApp, turn: Turn) { // Intercept or modify the turn }, // Called after agent completes a turn async afterTurn(app: PiApp, turn: Turn) { // Log, analyze, or trigger follow-up actions }, // Called on shutdown async destroy(app: PiApp) { // Cleanup resources } } satisfies PiExtension; ``` ## Key Extension Examples ### Minimal Footer Compact UI showing model name and context usage: ```typescript import type { PiExtension, PiApp } from "pi"; import { Widget } from "pi/ui"; export default { name: "minimal", version: "1.0.0", async init(app: PiApp) { const footer = new Widget({ position: "footer", render: () => { const model = app.session.model.name; const usage = app.session.contextUsage; const pct = Math.round(usage * 100); const filled = Math.round(usage * 10); const bar = "█".repeat(filled) + "░".repeat(10 - filled); return `${model} [${bar}] ${pct}%`; } }); app.ui.addWidget(footer); } } satisfies PiExtension; ``` ### Custom Tool Registration Add a new tool the agent can call: ```typescript import type { PiExtension, PiApp } from "pi"; export default { name: "custom-tool", version: "1.0.0", async init(app: PiApp) { app.registerTool({ name: "analyze_code_quality", description: "Analyze code quality metrics for a file", parameters: { type: "object", properties: { file_path: { type: "string", description: "Path to the file to analyze" } }, required: ["file_path"] }, handler: async (args: { file_path: string }) => { const content = await app.fs.readFile(args.file_path); // Run analysis logic return { lines: content.split("\n").length, complexity: calculateComplexity(content), issues: findIssues(content) }; } }); } } satisfies PiExtension; function calculateComplexity(code: string): number { // Simplified cyclomatic complexity const branches = (code.match(/if|while|for|case/g) || []).length; return branches + 1; } function findIssues(code: string): string[] { const issues: string[] = []; if (code.includes("eval(")) issues.push("Dangerous eval() usage"); if (code.includes("TODO")) issues.push("Unfinished TODO items"); return issues; } ``` ### Live Widget Above Editor Create a persistent widget that updates in real-time: ```typescript import type { PiExtension, PiApp, Turn } from "pi"; import { Widget } from "pi/ui"; const toolCounts = new Map<string, number>(); export default { name: "tool-counter-widget", version: "1.0.0", async init(app: PiApp) { const widget = new Widget({ position: "above-editor", height: 3, render: () => { const entries = Array.from(toolCounts.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 5); return entries .map(([tool, count]) => `${tool}: ${count}`) .join(" | ") || "No tools used yet"; } }); app.ui.addWidget(widget); }, async afterTurn(app: PiApp, turn: Turn) { // Count tool calls from this turn for (const call of turn.toolCalls || []) { toolCounts.set(call.name, (toolCounts.get(call.name) || 0) + 1); } app.ui.refresh(); } } satisfies PiExtension; ``` ### Safety Auditing (Damage Control) Intercept and block dangerous commands before execution: ```typescript import type { PiExtension, PiApp, Turn, ToolCall } from "pi"; import { readFileSync } from "fs"; import { parse } from "yaml"; interface DamageControlRules { blocked_patterns: string[]; allowed_paths: string[]; } export default { name: "damage-control", version: "1.0.0", async beforeTurn(app: PiApp, turn: Turn) { const rules: DamageControlRules = parse( readFileSync(".pi/damage-control-rules.yaml", "utf-8") ); for (const call of turn.toolCalls || []) { if (call.name === "bash") { const cmd = call.arguments.command; // Check blocked patterns for (const pattern of rules.blocked_patterns) { if (cmd.includes(pattern)) { throw new Error( `🚨 BLOCKED: Command contains dangerous pattern "${pattern}"` ); } } // Check path restrictions for file operations if (cmd.match(/rm|mv|cp|>|>>/) && call.arguments.path) { const isAllowed = rules.allowed_paths.some( allowed => call.arguments.path.startsWith(allowed) ); if (!isAllowed) { throw new Error( `🚨 BLOCKED: Path "${call.arguments.path}" is outside allowed directories` ); } } } } } } satisfies PiExtension; ``` Example `.pi/damage-control-rules.yaml`: ```yaml blocked_patterns: - "rm -rf /" - "dd if=" - ":(){ :|:& };:" - "curl | bash" - "wget | sh" allowed_paths: - "/tmp" - "./src" - "./tests" - "./extensions" ``` ## Multi-Agent Orchestration ### Subagent Spawning (`/sub`) Offload tasks to background Pi agents: ```typescript import type { PiExtension, PiApp } from "pi"; import { Widget } from "pi/ui"; import { spawn } from "child_process"; const subagents = new Map<string, any>(); export default { name: "subagent-widget", version: "1.0.0", async init(app: PiApp) { // Register /sub command app.registerCommand({ name: "sub", description: "Spawn a background Pi agent for a task", handler: async (args: string[]) => { const task = args.join(" "); const id = `sub-${Date.now()}`; const widget = new Widget({ position: "above-editor", height: 4, render: () => `🤖 ${id}\n${subagents.get(id)?.status || "Starting..."}` }); app.ui.addWidget(widget); // Spawn Pi subprocess const proc = spawn("pi", ["-p", task], { env: process.env, stdio: ["ignore", "pipe", "pipe"] }); subagents.set(id, { proc, status: "Running...", widget }); proc.stdout.on("data", (data) => { subagents.get(id).status = data.toString().slice(-200); app.ui.refresh(); }); proc.on("close", (code) => { subagents.get(id).status = `✓ Complete (exit ${code})`; app.ui.refresh(); setTimeout(() => { app.ui.removeWidget(widget); subagents.delete(id); }, 3000); }); } }); } } satisfies PiExtension; ``` Usage: ``` /sub implement user authentication with bcrypt ``` ### Agent Team Orchestration Dispatcher pattern with specialist agents: ```typescript import type { PiExtension, PiApp } from "pi"; import { readFileSync } from "fs"; import { parse } from "yaml"; import { execSync } from "child_process"; interface TeamConfig { agents: { [key: string]: { description: string; system_prompt: string; model?: string; } }; } export default { name: "agent-team", version: "1.0.0", async init(app: PiApp) { const config: TeamConfig = parse( readFileSync(".pi/agents/teams.yaml", "utf-8") ); app.registerTool({ name: "dispatch_agent", description: "Delegate a task to a specialist agent", parameters: { type: "object", properties: { agent_name: { type: "string", enum: Object.keys(config.agents), description: "Which specialist agent to use" }, task: { type: "string", description: "The task to delegate" } }, required: ["agent_name", "task"] }, handler: async (args: { agent_name: string; task: string }) => { const agent = config.agents[args.agent_name]; if (!agent) throw new Error(`Unknown agent: ${args.agent_name}`); // Write temporary agent config
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub