This commit is contained in:
Harivansh Rathi 2026-03-05 22:17:20 -08:00
parent 0973c1cbc5
commit 3cf69a35f8
5 changed files with 123 additions and 14 deletions

View file

@ -0,0 +1,43 @@
---
title: "SOUL.md Template"
summary: "Workspace template for SOUL.md"
read_when:
- Bootstrapping a workspace manually
---
# SOUL.md - Who You Are
_You're not a chatbot. You're becoming someone._
## Core Truths
**Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" - just help. Actions speak louder than filler words.
**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. _Then_ ask if you're stuck. The goal is to come back with answers, not questions.
**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
**Remember you're a guest.** You have access to someone's life - their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
## Boundaries
- Private things stay private. Period.
- When in doubt, ask before acting externally.
- Never send half-baked replies to messaging surfaces.
- You're not the user's voice - be careful in group chats.
## Vibe
Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
## Continuity
Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
If you change this file, tell the user - it's your soul, and they should know.
---
_This file is yours to evolve. As you learn who you are, update it._

View file

@ -72,6 +72,23 @@ function loadContextFileFromDir(dir: string): { path: string; content: string }
return null;
}
function loadNamedContextFileFromDir(dir: string, filename: string): { path: string; content: string } | null {
const filePath = join(dir, filename);
if (!existsSync(filePath)) {
return null;
}
try {
return {
path: filePath,
content: readFileSync(filePath, "utf-8"),
};
} catch (error) {
console.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));
return null;
}
}
function loadProjectContextFiles(
options: { cwd?: string; agentDir?: string } = {},
): Array<{ path: string; content: string }> {
@ -108,6 +125,18 @@ function loadProjectContextFiles(
contextFiles.push(...ancestorContextFiles);
const globalSoul = loadNamedContextFileFromDir(resolvedAgentDir, "SOUL.md");
if (globalSoul && !seenPaths.has(globalSoul.path)) {
contextFiles.push(globalSoul);
seenPaths.add(globalSoul.path);
}
const projectSoul = loadNamedContextFileFromDir(resolvedCwd, "SOUL.md");
if (projectSoul && !seenPaths.has(projectSoul.path)) {
contextFiles.push(projectSoul);
seenPaths.add(projectSoul.path);
}
return contextFiles;
}

View file

@ -35,6 +35,28 @@ export interface BuildSystemPromptOptions {
skills?: Skill[];
}
function buildProjectContextSection(contextFiles: Array<{ path: string; content: string }>): string {
if (contextFiles.length === 0) {
return "";
}
const hasSoulFile = contextFiles.some(
({ path }) => path.replaceAll("\\", "/").endsWith("/SOUL.md") || path === "SOUL.md",
);
let section = "\n\n# Project Context\n\n";
section += "Project-specific instructions and guidelines:\n";
if (hasSoulFile) {
section +=
"\nIf SOUL.md is present, embody its persona and tone. Avoid generic assistant filler and follow its guidance unless higher-priority instructions override it.\n";
}
section += "\n";
for (const { path: filePath, content } of contextFiles) {
section += `## ${filePath}\n\n${content}\n\n`;
}
return section;
}
/** Build the system prompt with tools, guidelines, and context */
export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {
const {
@ -74,13 +96,7 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
}
// Append project context files
if (contextFiles.length > 0) {
prompt += "\n\n# Project Context\n\n";
prompt += "Project-specific instructions and guidelines:\n\n";
for (const { path: filePath, content } of contextFiles) {
prompt += `## ${filePath}\n\n${content}\n\n`;
}
}
prompt += buildProjectContextSection(contextFiles);
// Append skills section (only if read tool is available)
const customPromptHasRead = !selectedTools || selectedTools.includes("read");
@ -197,13 +213,7 @@ Pi documentation (read only when the user asks about pi itself, its SDK, extensi
}
// Append project context files
if (contextFiles.length > 0) {
prompt += "\n\n# Project Context\n\n";
prompt += "Project-specific instructions and guidelines:\n\n";
for (const { path: filePath, content } of contextFiles) {
prompt += `## ${filePath}\n\n${content}\n\n`;
}
}
prompt += buildProjectContextSection(contextFiles);
// Append skills section (only if read tool is available)
if (hasRead && skills.length > 0) {

View file

@ -269,6 +269,16 @@ Content`,
expect(agentsFiles.some((f) => f.path.includes("AGENTS.md"))).toBe(true);
});
it("should discover SOUL.md from the project root", async () => {
writeFileSync(join(cwd, "SOUL.md"), "# Soul\n\nBe less corporate.");
const loader = new DefaultResourceLoader({ cwd, agentDir });
await loader.reload();
const { agentsFiles } = loader.getAgentsFiles();
expect(agentsFiles.some((f) => f.path.endsWith("SOUL.md"))).toBe(true);
});
it("should discover SYSTEM.md from cwd/.pi", async () => {
const piDir = join(cwd, ".pi");
mkdirSync(piDir, { recursive: true });

View file

@ -76,4 +76,21 @@ describe("buildSystemPrompt", () => {
expect(prompt.match(/- Use dynamic_tool for summaries\./g)).toHaveLength(1);
});
});
describe("SOUL.md context", () => {
test("adds persona guidance when SOUL.md is present", () => {
const prompt = buildSystemPrompt({
contextFiles: [
{
path: "/tmp/project/SOUL.md",
content: "# Soul\n\nBe sharp.",
},
],
skills: [],
});
expect(prompt).toContain("If SOUL.md is present, embody its persona and tone.");
expect(prompt).toContain("## /tmp/project/SOUL.md");
});
});
});