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

> Discover how FileManager.cs in ChocolateLM Lite manages settings and data files. Learn about its role in handling global config, per-persona directories, and thread-safe data operations.

- Repository: [Segment (gpsnmeajp)/chocolatelmlite](https://github.com/gpsnmeajp/chocolatelmlite)
- Tags: internals
- Published: 2026-03-02

---

**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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.yaml)** – Personal configuration containing display name, timers, webhook URLs, and VoiceVox options【lines 17‑28】.
- **[`system_prompt.txt`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/system_prompt.txt)** – The raw text sent to the LLM as the system prompt for that persona【lines 84‑90】.
- **[`post_process_script.js`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/post_process_script.js)** – Optional JavaScript executed after the LLM generates a response【lines 85‑92】.
- **[`keyword_knowledge.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/keyword_knowledge.yaml)** – Structured user-defined knowledge entries indexed by keywords【lines 100‑108】.
- **[`memory.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/memory.yaml)** – Long-term memory entries that persist across conversations【lines 61‑67】.
- **[`summary.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/summary.yaml)** and **[`summary_prompt.txt`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/memory.yaml), a default [`post_process_script.js`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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

```csharp
// 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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/general.yaml) and [`manage.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.yaml), [`system_prompt.txt`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/system_prompt.txt), [`summary_prompt.txt`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/summary_prompt.txt), [`memory.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/memory.yaml), [`summary.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/summary.yaml), [`post_process_script.js`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/post_process_script.js), [`keyword_knowledge.yaml`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/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`](https://github.com/gpsnmeajp/chocolatelmlite/blob/main/settings.yaml) before returning the new persona’s ID.