How FileManager.cs Manages Settings and Data Files in ChocolateLM Lite

FileManager.cs serves as the central filesystem abstraction in the gpsnmeajp/chocolatelmlite repository, handling global configuration through YAML files, orchestrating per-persona data directories, and providing thread-safe CRUD operations for settings, memory, and conversation history.

In the ChocolateLM Lite application, all disk I/O operations are encapsulated within src/FileManager.cs. This class eliminates direct filesystem calls throughout the codebase by exposing strongly-typed methods for YAML serialization, persona lifecycle management, and concurrent file access control using lock files.

Global Configuration and Active Persona Tracking

Managing Site-Wide Settings via general.yaml

The FileManager class initializes global application settings through data/general.yaml. During construction, the manager checks for the file’s existence and creates it with defaults if missing【lines 19‑26】. The settings are deserialized into the generalSettings property via LoadGeneralSettings()【lines 20‑35】. Changes are persisted by passing a YamlGeneral object to SaveGeneralSettings(), which writes the updated configuration back to disk.

Tracking the Active Persona with manage.yaml

To maintain state across sessions, the class stores the currently selected persona ID inside data/manage.yaml. The method SetActivePersonaById(int) serializes the integer ID to this file, while GetActivePersonaId() reads and returns the stored value【lines 78‑90】. This indirection allows the application to restore the user's last active context on startup.

Per-Persona File Architecture

Each persona resides in a dedicated subdirectory following the pattern data/persona_{id}. The class builds these paths using constants defined at the top of the file, including dataDirectory = "data" and personaPrefix = "persona_". Within each folder, FileManager handles several specific file types:

  • settings.yaml – Personal configuration containing display name, timers, webhook URLs, and VoiceVox options【lines 17‑28】.
  • system_prompt.txt – The raw text sent to the LLM as the system prompt for that persona【lines 84‑90】.
  • post_process_script.js – Optional JavaScript executed after the LLM generates a response【lines 85‑92】.
  • keyword_knowledge.yaml – Structured user-defined knowledge entries indexed by keywords【lines 100‑108】.
  • memory.yaml – Long-term memory entries that persist across conversations【lines 61‑67】.
  • summary.yaml and summary_prompt.txt – Auto-generated conversation summaries and the prompts used to create them【lines 73‑78】.
  • talk.jsonl or talk.sqlite3 – Conversation history stored either as newline-delimited JSON or as an SQLite database【lines 87‑89】.
  • attachments/ – Binary storage for images and files associated with the persona.

Persona Lifecycle Management

Creating New Personas

The CreateNewPersona(string name) method orchestrates the initialization of a new persona directory【lines 25‑80】. It generates a unique ID, creates the folder structure, and writes default versions of all required files including an empty memory.yaml, a default post_process_script.js, and an empty SQLite database at talk.sqlite3.

Duplicating and Deleting Personas

To clone existing configurations, DuplicatePersonaById(int id, string? newName) copies the entire source persona directory, updates the name field within the new settings.yaml, and returns the freshly assigned persona ID【lines 82‑105】. Conversely, RemovePersonaById(int) deletes the persona folder entirely, utilizing the Windows Recycle Bin when available【lines 107‑112】.

Concurrency Safety and Lock Handling

For mutable resources like memory.yaml, the class implements a locking mechanism via WithMemoryLock<T>【lines 104‑166】. This method creates a temporary <file>.lock file on disk to serialize concurrent write operations, preventing data corruption during simultaneous updates. On application startup, RemoveAllLockFiles() scans for stale lock files and removes them【lines 65‑71】.

Fine-Grained CRUD Operations

Beyond lifecycle methods, FileManager exposes specific getters and setters for every per-persona artifact. These include:

  • Settings: GetActivePersonaSettings() and SaveActivePersonaSettings()
  • System Prompt: GetSystemPromptFromActivePersona() and SaveSystemPromptToActivePersona()
  • Memory: GetActivePersonaMemory() and UpsertActivePersonaMemory()
  • Keyword Knowledge: GetActivePersonaKeywordKnowledge() and UpsertActivePersonaKeywordKnowledge()
  • Summary: GetSummaryPromptFromActivePersona(), SaveSummaryPromptToActivePersona(), GetActivePersonaSummary(), and SaveActivePersonaSummary()
  • Post-Processing: GetActivePersonaPostProcessScript() and SaveActivePersonaPostProcessScript()

Each method handles the appropriate serialization format—YAML for structured data, plain text for prompts, and direct file copying for the SQLite database.

Practical Usage Example

// Initialise the manager (creates global files if needed)
var fm = new FileManager();

// Load global configuration
YamlGeneral cfg = fm.LoadGeneralSettings();

// Create a new persona named "Alice"
string? aliceId = fm.CreateNewPersona("Alice");

// Switch to the new persona
fm.SetActivePersonaById(int.Parse(aliceId));

// Read / modify the persona's system prompt
string prompt = fm.GetSystemPromptFromActivePersona();
fm.SaveSystemPromptToActivePersona(prompt + "\nPlease be concise.");

// Update a setting (e.g., enable VoiceVox)
var personaSettings = fm.GetActivePersonaSettings();
personaSettings.EnableVoiceVox = true;
fm.SaveActivePersonaSettings(personaSettings);

// Add a memory entry
fm.UpsertActivePersonaMemory(0, "Remember to check the weather every morning.");

Summary

  • Initialization: FileManager auto-creates general.yaml and manage.yaml on construction, ensuring required paths exist before any read operations occur.
  • Persona Lifecycle: The class provides atomic operations for creating, duplicating, and deleting persona directories with all associated configuration files.
  • Structured CRUD: Strongly-typed methods handle every file type within the data/persona_{id} directory, abstracting YAML and SQLite operations from the rest of the application.
  • Concurrency Control: File-based locking via WithMemoryLock<T> and startup cleanup via RemoveAllLockFiles() ensure safe concurrent access to mutable memory files.

Frequently Asked Questions

Where does FileManager.cs store global application settings?

Global settings reside in data/general.yaml at the application root. The class loads these via LoadGeneralSettings() and persists changes through SaveGeneralSettings(YamlGeneral), handling schema defaults automatically when files are missing.

How does FileManager.cs handle concurrent access to memory files?

The implementation uses WithMemoryLock<T> to create temporary .lock files that serialize write access to memory.yaml. Additionally, RemoveAllLockFiles() executes during initialization to clear stale locks from previous crashed sessions.

What files are created when adding a new persona?

The CreateNewPersona() method generates a complete persona directory containing settings.yaml, system_prompt.txt, summary_prompt.txt, memory.yaml, summary.yaml, post_process_script.js, keyword_knowledge.yaml, and an empty talk.sqlite3 database, ensuring the persona is immediately functional.

How can I duplicate an existing persona in ChocolateLM Lite?

Invoke DuplicatePersonaById(int id, string? newName) with the source persona ID and an optional new display name. The method clones the entire persona_{id} directory structure and updates the name field in the copied settings.yaml before returning the new persona’s ID.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →