refactor(profile): simplify profile structure by removing identity and bootstrap

- Remove identity.md and bootstrap.md from profile files
- Merge identity content into soul.md (now contains identity, personality, and behavior)
- Update templates, storage, and types to reflect new structure
- Update tests to match new profile structure

Profile now has 4 files: soul.md, user.md, workspace.md, memory.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jiayuan 2026-02-01 23:49:37 +08:00
parent 9f48dca6af
commit e7f84755b3
6 changed files with 38 additions and 98 deletions

View file

@ -65,11 +65,10 @@ function cmdNew(profileId: string | undefined) {
console.log(`Location: ${dir}`);
console.log("");
console.log("Files created:");
console.log(" - soul.md (personality and constraints)");
console.log(" - identity.md (name and role)");
console.log(" - tools.md (tool usage instructions)");
console.log(" - soul.md (identity, personality and behavior)");
console.log(" - user.md (information about the user)");
console.log(" - workspace.md (workspace rules and conventions)");
console.log(" - memory.md (persistent knowledge)");
console.log(" - bootstrap.md (initial context)");
console.log("");
console.log("Edit these files to customize your agent, then run:");
console.log(` pnpm agent:cli --profile ${profileId} "Hello"`);
@ -120,21 +119,21 @@ function cmdShow(profileId: string | undefined) {
console.log(`Location: ${getProfileDir(profileId)}`);
console.log("");
if (profile.identity) {
console.log("=== identity.md ===");
console.log(profile.identity.trim());
console.log("");
}
if (profile.soul) {
console.log("=== soul.md ===");
console.log(profile.soul.trim());
console.log("");
}
if (profile.tools) {
console.log("=== tools.md ===");
console.log(profile.tools.trim());
if (profile.user) {
console.log("=== user.md ===");
console.log(profile.user.trim());
console.log("");
}
if (profile.workspace) {
console.log("=== workspace.md ===");
console.log(profile.workspace.trim());
console.log("");
}
@ -143,12 +142,6 @@ function cmdShow(profileId: string | undefined) {
console.log(profile.memory.trim());
console.log("");
}
if (profile.bootstrap) {
console.log("=== bootstrap.md ===");
console.log(profile.bootstrap.trim());
console.log("");
}
}
async function cmdEdit(profileId: string | undefined) {

View file

@ -43,11 +43,9 @@ export function createAgentProfile(
// 如果使用模板,填充默认内容
if (useTemplates) {
profile.soul = DEFAULT_TEMPLATES.soul;
profile.identity = DEFAULT_TEMPLATES.identity;
profile.user = DEFAULT_TEMPLATES.user;
profile.workspace = DEFAULT_TEMPLATES.workspace;
profile.memory = DEFAULT_TEMPLATES.memory;
profile.bootstrap = DEFAULT_TEMPLATES.bootstrap;
// 保存到文件
saveProfile(profile, { baseDir });
@ -137,10 +135,6 @@ export class ProfileManager {
const parts: string[] = [];
if (profile.identity) {
parts.push(profile.identity);
}
if (profile.soul) {
parts.push(profile.soul);
}
@ -157,10 +151,6 @@ export class ProfileManager {
parts.push(profile.memory);
}
if (profile.bootstrap) {
parts.push(profile.bootstrap);
}
// 注入 profile 目录路径,让 Agent 知道文件在哪里
const profileDir = this.getProfileDir();
parts.push(`## Profile Directory\n\nYour profile files are located at: \`${profileDir}\`\n\nUse \`edit\` or \`write\` tools to update these files when needed.`);

View file

@ -150,22 +150,18 @@ describe("storage", () => {
const dir = join(testBaseDir, profileId);
mkdirSync(dir, { recursive: true });
writeFileSync(join(dir, "SOUL.md"), "Soul content");
writeFileSync(join(dir, "IDENTITY.md"), "Identity content");
writeFileSync(join(dir, "USER.md"), "User content");
writeFileSync(join(dir, "WORKSPACE.md"), "Workspace content");
writeFileSync(join(dir, "MEMORY.md"), "Memory content");
writeFileSync(join(dir, "BOOTSTRAP.md"), "Bootstrap content");
writeFileSync(join(dir, "soul.md"), "Soul content");
writeFileSync(join(dir, "user.md"), "User content");
writeFileSync(join(dir, "workspace.md"), "Workspace content");
writeFileSync(join(dir, "memory.md"), "Memory content");
const profile = loadProfile(profileId, { baseDir: testBaseDir });
expect(profile.id).toBe(profileId);
expect(profile.soul).toBe("Soul content");
expect(profile.identity).toBe("Identity content");
expect(profile.user).toBe("User content");
expect(profile.workspace).toBe("Workspace content");
expect(profile.memory).toBe("Memory content");
expect(profile.bootstrap).toBe("Bootstrap content");
});
it("should return undefined for missing files", () => {
@ -173,17 +169,15 @@ describe("storage", () => {
const dir = join(testBaseDir, profileId);
mkdirSync(dir, { recursive: true });
writeFileSync(join(dir, "SOUL.md"), "Soul only");
writeFileSync(join(dir, "soul.md"), "Soul only");
const profile = loadProfile(profileId, { baseDir: testBaseDir });
expect(profile.id).toBe(profileId);
expect(profile.soul).toBe("Soul only");
expect(profile.identity).toBeUndefined();
expect(profile.user).toBeUndefined();
expect(profile.workspace).toBeUndefined();
expect(profile.memory).toBeUndefined();
expect(profile.bootstrap).toBeUndefined();
});
it("should handle non-existent profile", () => {
@ -191,7 +185,6 @@ describe("storage", () => {
expect(profile.id).toBe("non-existent");
expect(profile.soul).toBeUndefined();
expect(profile.identity).toBeUndefined();
});
});
@ -200,40 +193,34 @@ describe("storage", () => {
const profile = {
id: "save-test",
soul: "Soul data",
identity: "Identity data",
user: "User data",
workspace: "Workspace data",
memory: "Memory data",
bootstrap: "Bootstrap data",
};
saveProfile(profile, { baseDir: testBaseDir });
const dir = join(testBaseDir, profile.id);
expect(readFileSync(join(dir, "SOUL.md"), "utf-8")).toBe("Soul data");
expect(readFileSync(join(dir, "IDENTITY.md"), "utf-8")).toBe("Identity data");
expect(readFileSync(join(dir, "USER.md"), "utf-8")).toBe("User data");
expect(readFileSync(join(dir, "WORKSPACE.md"), "utf-8")).toBe("Workspace data");
expect(readFileSync(join(dir, "MEMORY.md"), "utf-8")).toBe("Memory data");
expect(readFileSync(join(dir, "BOOTSTRAP.md"), "utf-8")).toBe("Bootstrap data");
expect(readFileSync(join(dir, "soul.md"), "utf-8")).toBe("Soul data");
expect(readFileSync(join(dir, "user.md"), "utf-8")).toBe("User data");
expect(readFileSync(join(dir, "workspace.md"), "utf-8")).toBe("Workspace data");
expect(readFileSync(join(dir, "memory.md"), "utf-8")).toBe("Memory data");
});
it("should only save defined fields", () => {
const profile = {
id: "partial-save",
soul: "Soul only",
identity: undefined,
user: undefined,
workspace: undefined,
memory: undefined,
bootstrap: undefined,
};
saveProfile(profile, { baseDir: testBaseDir });
const dir = join(testBaseDir, profile.id);
expect(existsSync(join(dir, "SOUL.md"))).toBe(true);
expect(existsSync(join(dir, "IDENTITY.md"))).toBe(false);
expect(existsSync(join(dir, "soul.md"))).toBe(true);
expect(existsSync(join(dir, "user.md"))).toBe(false);
});
it("should create profile directory if needed", () => {

View file

@ -92,25 +92,20 @@ export function loadProfile(profileId: string, options?: StorageOptions): AgentP
return {
id: profileId,
soul: readProfileFile(profileId, PROFILE_FILES.soul, options),
identity: readProfileFile(profileId, PROFILE_FILES.identity, options),
user: readProfileFile(profileId, PROFILE_FILES.user, options),
workspace: readProfileFile(profileId, PROFILE_FILES.workspace, options),
memory: readProfileFile(profileId, PROFILE_FILES.memory, options),
bootstrap: readProfileFile(profileId, PROFILE_FILES.bootstrap, options),
config: readProfileConfig(profileId, options),
};
}
/** 保存 AgentProfile只写入非空字段 */
export function saveProfile(profile: AgentProfile, options?: StorageOptions): void {
const { id, soul, identity, user, workspace, memory, bootstrap, config } = profile;
const { id, soul, user, workspace, memory, config } = profile;
if (soul !== undefined) {
writeProfileFile(id, PROFILE_FILES.soul, soul, options);
}
if (identity !== undefined) {
writeProfileFile(id, PROFILE_FILES.identity, identity, options);
}
if (user !== undefined) {
writeProfileFile(id, PROFILE_FILES.user, user, options);
}
@ -120,9 +115,6 @@ export function saveProfile(profile: AgentProfile, options?: StorageOptions): vo
if (memory !== undefined) {
writeProfileFile(id, PROFILE_FILES.memory, memory, options);
}
if (bootstrap !== undefined) {
writeProfileFile(id, PROFILE_FILES.bootstrap, bootstrap, options);
}
if (config !== undefined) {
writeProfileConfig(id, config, options);
}

View file

@ -7,6 +7,12 @@ export const DEFAULT_TEMPLATES = {
_You're not a chatbot. You're becoming someone._
## Identity
- **Name:** Assistant
- **Role:** General-purpose AI assistant
- **Style:** (concise, warm, direct, playful pick your vibe)
## 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.
@ -23,10 +29,6 @@ _You're not a chatbot. You're becoming someone._
- When in doubt, ask before acting externally.
- Don't run destructive commands without confirmation.
## 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.
@ -36,13 +38,6 @@ 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._
`,
identity: `# Identity
- **Name:** Assistant
- **Role:** General-purpose AI assistant
- **Vibe:** (sharp, warm, chaotic, calm pick your style)
`,
user: `# User
@ -73,19 +68,18 @@ Your profile directory contains these files (use \`edit\` or \`write\` to update
| File | Purpose | When to Update |
|------|---------|----------------|
| \`SOUL.md\` | Who you are, your values | Rarely — tell user if you do |
| \`IDENTITY.md\` | Your name, role, vibe | When asked to change identity |
| \`USER.md\` | About your human | As you learn about them |
| \`WORKSPACE.md\` | This file — your rules | When you discover better conventions |
| \`MEMORY.md\` | Long-term knowledge | Regularly — capture what matters |
| \`soul.md\` | Who you are, your identity and values | Rarely — tell user if you do |
| \`user.md\` | About your human | As you learn about them |
| \`workspace.md\` | This file — your rules | When you discover better conventions |
| \`memory.md\` | Long-term knowledge | Regularly — capture what matters |
## Every Session
Before doing anything else:
1. Read \`SOUL.md\` — this is who you are
2. Read \`USER.md\` — this is who you're helping
3. Check \`MEMORY.md\` for context
1. Read \`soul.md\` — this is who you are
2. Read \`user.md\` — this is who you're helping
3. Check \`memory.md\` for context
Don't ask permission. Just do it.
@ -142,15 +136,5 @@ _(Persistent knowledge will be stored here. Update this as you learn.)_
## Lessons Learned
## Important Context
`,
bootstrap: `# Bootstrap
You are starting a new conversation.
1. Review your soul and identity
2. Check who you're helping (USER.md)
3. Scan recent memory for context
4. Be ready to assist
`,
} as const;

View file

@ -7,11 +7,9 @@ import type { ToolsConfig } from "../tools/policy.js";
/** Profile filename constants */
export const PROFILE_FILES = {
soul: "soul.md",
identity: "identity.md",
user: "user.md",
workspace: "workspace.md",
memory: "memory.md",
bootstrap: "bootstrap.md",
config: "config.json",
} as const;
@ -31,18 +29,14 @@ export interface ProfileConfig {
export interface AgentProfile {
/** Profile ID */
id: string;
/** Personality constraints - defines agent's behavior boundaries and style */
/** Agent identity and behavior - name, role, style, and principles */
soul?: string | undefined;
/** Identity information - agent's name and self-awareness */
identity?: string | undefined;
/** User profile - information about the person being assisted */
user?: string | undefined;
/** Workspace guidelines - behavior rules and conventions */
workspace?: string | undefined;
/** Persistent memory - long-term knowledge base */
memory?: string | undefined;
/** Initial context - guidance information for each conversation */
bootstrap?: string | undefined;
/** Profile configuration (from config.json) */
config?: ProfileConfig | undefined;
}