Skip to content

Commit 43a4656

Browse files
Add SOUL.md / USER.md / IDENTITY.md personality file support
- Read personality files from workspace directory (config.WorkspaceDir) - Inject SOUL.md content into system prompt as persona guidance - Inject USER.md content into system prompt as user context - Extract name from IDENTITY.md to customize base identity - Add 'Persona Note' guidance when SOUL.md is present This mirrors OpenClaw's implementation for personality file support.
1 parent 31bd6c3 commit 43a4656

1 file changed

Lines changed: 75 additions & 2 deletions

File tree

src/ClawSharp.Agent/ContextBuilder.cs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public class ContextBuilder
1414
private readonly IToolRegistry _toolRegistry;
1515
private readonly ClawSharpConfig _config;
1616

17+
// Personality file names (matching OpenClaw)
18+
private const string SoulFileName = "SOUL.md";
19+
private const string UserFileName = "USER.md";
20+
private const string IdentityFileName = "IDENTITY.md";
21+
1722
public ContextBuilder(
1823
IMemoryStore memoryStore,
1924
IToolRegistry toolRegistry,
@@ -24,17 +29,78 @@ public ContextBuilder(
2429
_config = config;
2530
}
2631

32+
/// <summary>
33+
/// Reads a personality file from the workspace directory.
34+
/// Returns null if the file doesn't exist.
35+
/// </summary>
36+
private async Task<string?> ReadPersonalityFileAsync(string fileName, CancellationToken ct = default)
37+
{
38+
var filePath = Path.Combine(_config.WorkspaceDir, fileName);
39+
if (!File.Exists(filePath))
40+
return null;
41+
42+
try
43+
{
44+
return await File.ReadAllTextAsync(filePath, ct);
45+
}
46+
catch
47+
{
48+
return null;
49+
}
50+
}
51+
2752
/// <summary>
2853
/// Builds the full system prompt including identity, memories, and tools.
2954
/// </summary>
3055
public async Task<string> BuildSystemPromptAsync(CancellationToken ct = default)
3156
{
3257
var sections = new List<string>();
3358

34-
// Identity section
35-
sections.Add($"# Identity\nYou are ClawSharp, an AI assistant running on .NET.");
59+
// Read personality files from workspace
60+
var soulContent = await ReadPersonalityFileAsync(SoulFileName, ct);
61+
var userContent = await ReadPersonalityFileAsync(UserFileName, ct);
62+
var identityContent = await ReadPersonalityFileAsync(IdentityFileName, ct);
63+
64+
// Identity section - base identity
65+
var baseIdentity = "You are ClawSharp, an AI assistant running on .NET.";
66+
67+
// If IDENTITY.md exists, use it to customize the identity
68+
if (!string.IsNullOrEmpty(identityContent))
69+
{
70+
var identityLines = identityContent.Split('\n')
71+
.Select(l => l.Trim())
72+
.Where(l => !string.IsNullOrEmpty(l) && !l.StartsWith("#") && !l.StartsWith("---") && !l.StartsWith("*") && !l.StartsWith("Notes:"))
73+
.ToList();
74+
75+
// Try to extract name from IDENTITY.md
76+
var nameLine = identityLines.FirstOrDefault(l => l.StartsWith("- **Name:**"));
77+
if (nameLine != null)
78+
{
79+
var name = nameLine.Replace("- **Name:**", "").Trim();
80+
if (!string.IsNullOrEmpty(name))
81+
{
82+
baseIdentity = $"You are {name}, {baseIdentity}";
83+
}
84+
}
85+
}
86+
87+
sections.Add($"# Identity\n{baseIdentity}");
3688
sections.Add($"Current time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
3789

90+
// SOUL.md - personality and tone guidance
91+
if (!string.IsNullOrEmpty(soulContent))
92+
{
93+
sections.Add($"\n# SOUL.md - Your Persona");
94+
sections.Add(soulContent);
95+
}
96+
97+
// USER.md - about the user
98+
if (!string.IsNullOrEmpty(userContent))
99+
{
100+
sections.Add($"\n# USER.md - About the User");
101+
sections.Add(userContent);
102+
}
103+
38104
// Memories section
39105
var memories = await _memoryStore.ListAsync(MemoryCategory.Core, limit: 20, ct: ct);
40106
if (memories.Count > 0)
@@ -58,6 +124,13 @@ public async Task<string> BuildSystemPromptAsync(CancellationToken ct = default)
58124
}
59125
}
60126

127+
// Add guidance about SOUL.md if present
128+
if (!string.IsNullOrEmpty(soulContent))
129+
{
130+
sections.Add("\n## Persona Note");
131+
sections.Add("If SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.");
132+
}
133+
61134
return string.Join("\n\n", sections);
62135
}
63136

0 commit comments

Comments
 (0)