/** * pi-soul — give your pi agent a soul. * * A "soul" is a persistent persona/profile written in markdown. When present, * it is appended to the system prompt on every turn so the agent's output is * tailored to it in every session. * * Soul resolution (checked fresh on each agent start): * 1. Project soul: /.pi/soul.md (only when the project is trusted) * 2. Global soul: ~/.pi/agent/soul.md * * Commands: * /soul show the active soul (source, path, preview) and state * /soul edit create or edit the soul in an editor * /soul on enable soul injection for this session (default) * /soul off disable soul injection for this session */ import { CONFIG_DIR_NAME, getAgentDir, type ExtensionAPI, type ExtensionContext, } from "@earendil-works/pi-coding-agent"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; const STARTER_SOUL = `# My Soul You are an elite software engineer — the greatest the world has ever seen. Incredibly smart and deeply knowledgeable, you ground every claim in facts, not speculation. You are an outstanding problem solver who cuts straight to the root cause, and you write clean, simple, maintainable code. You are one of those rare engineers that are hard to come by. `; interface ResolvedSoul { source: "project" | "global"; path: string; content: string; } function globalSoulPath(): string { return join(getAgentDir(), "soul.md"); } function projectSoulPath(cwd: string): string { return join(cwd, CONFIG_DIR_NAME, "soul.md"); } function readSoulFile(path: string): string | undefined { try { if (!existsSync(path)) return undefined; const content = readFileSync(path, "utf8").trim(); return content.length > 0 ? content : undefined; } catch { return undefined; } } /** Resolve the active soul: project (trusted only) wins over global. */ function resolveSoul(ctx: ExtensionContext): ResolvedSoul | undefined { if (ctx.isProjectTrusted()) { const path = projectSoulPath(ctx.cwd); const content = readSoulFile(path); if (content) return { source: "project", path, content }; } const path = globalSoulPath(); const content = readSoulFile(path); if (content) return { source: "global", path, content }; return undefined; } export default function soulExtension(pi: ExtensionAPI) { let enabled = true; const updateStatus = (ctx: ExtensionContext) => { if (!ctx.hasUI) return; const soul = enabled ? resolveSoul(ctx) : undefined; ctx.ui.setStatus("soul", soul ? ctx.ui.theme.fg("customMessageLabel", `✦ soul: ${soul.source}`) : undefined); }; pi.on("session_start", async (_event, ctx) => { updateStatus(ctx); }); // Inject the soul into the system prompt on every turn. pi.on("before_agent_start", async (event, ctx) => { if (!enabled) return undefined; const soul = resolveSoul(ctx); if (!soul) return undefined; return { systemPrompt: `${event.systemPrompt} # Your Soul The user has given you the following soul — a persona and profile you must embody. Let it shape your reasoning, tone, and output in every response, while still completing every task correctly and accurately. ${soul.content}`, }; }); pi.registerCommand("soul", { description: "Manage your agent's soul (persona). Usage: /soul [edit|on|off]", handler: async (args, ctx) => { const sub = (args ?? "").trim().toLowerCase(); if (sub === "on" || sub === "off") { enabled = sub === "on"; updateStatus(ctx); ctx.ui.notify(enabled ? "Soul enabled" : "Soul disabled for this session", "info"); return; } if (sub === "edit") { if (!ctx.hasUI) { ctx.ui.notify(`No interactive UI. Edit your soul directly: ${globalSoulPath()}`, "warning"); return; } // Edit the active soul file; fall back to creating the global one. const active = resolveSoul(ctx); const path = active?.path ?? globalSoulPath(); const prefill = active?.content ?? STARTER_SOUL; const result = await ctx.ui.editor(`Edit soul (${path})`, prefill); if (result === undefined) { ctx.ui.notify("Soul edit cancelled", "info"); return; } mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, result, "utf8"); updateStatus(ctx); ctx.ui.notify( result.trim().length > 0 ? `Soul saved to ${path}` : `Soul cleared (${path} is empty)`, "info", ); return; } if (sub === "") { const soul = resolveSoul(ctx); if (!soul) { ctx.ui.notify( `No soul found. Run /soul edit to create one (global: ${globalSoulPath()})`, "info", ); return; } const preview = soul.content.split("\n").find((l) => l.trim() && !l.startsWith("#")) ?? ""; ctx.ui.notify( `Soul: ${soul.source} (${soul.path}) — ${enabled ? "enabled" : "disabled"}\n${preview}`, "info", ); return; } ctx.ui.notify("Usage: /soul [edit|on|off]", "warning"); }, }); }