# core/loaders/agent.loader.ts

> 61 lines of code and 14 definitions.

Tree: GovLab Stats
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/stats#file-stats-core-loaders-agent-loader-ts
Source text: https://banes-lab.com/source/stats/core/loaders/agent.loader.ts.txt

Listed in [core/loaders](https://banes-lab.com/api/source/stats/core/loaders.md), before [core/loaders/catalog.loader.ts](https://banes-lab.com/source/stats/core/loaders/catalog.loader.ts.md).

## Definitions

- `projectSlug` (lexical_declaration, line 22)
- `countIndexEntries` (lexical_declaration, line 31)
- `readMemoryFile` (lexical_declaration, line 35)
- `memoryDirOf` (lexical_declaration, line 44, exported)
- `slug` (lexical_declaration, line 23)
- `at` (lexical_declaration, line 24)
- `ch` (lexical_declaration, line 25)
- `abs` (lexical_declaration, line 36)
- `text` (lexical_declaration, line 37)
- `collectMemory` (lexical_declaration, line 48, exported)
- `dir` (lexical_declaration, line 49, exported)
- `entries` (lexical_declaration, line 50, exported)
- `parsed` (lexical_declaration, line 54, exported)
- `typed` (lexical_declaration, line 57, exported)

## Contained in

- [core/loaders](https://banes-lab.com/anatomy/stats/folder-stats-core-loaders.md)

## Uses

- [core/selectors/document.selector.ts](https://banes-lab.com/source/stats/core/selectors/document.selector.ts.md)

## Linked from

- [core/loaders](https://banes-lab.com/anatomy/stats/folder-stats-core-loaders.md)
- [core/selectors](https://banes-lab.com/anatomy/stats/folder-stats-core-selectors.md)

## Source

```typescript
import {
    INDEX_ENTRY_MARK,
    MARKDOWN_EXTENSION,
    MEMORY_FOLDER,
    MEMORY_INDEX,
    MEMORY_PROJECTS,
    SLUG_CHARS,
    SLUG_FILL,
    TYPE_KEY,
    UNSPECIFIED_TYPE,
} from "#configuration/constants/agent.constants";
import type { MemoryFile, MemoryStats } from "#types/agent.types";
import { readFileSync, statSync } from "node:fs";
import { LINE_BREAK } from "#configuration/constants/source.constants";
import { frontmatterValue } from "#core/selectors/document.selector";
import { homedir } from "node:os";
import path from "node:path";
import { readdirOrNull } from "#core/loaders/folder.loader";
import { relativePath } from "@ssot/paths";
import { tally } from "#core/selectors/metric.selector";

const projectSlug = function projectSlug(absPath: string): string {
    let slug = "";
    for (let at = 0; at < absPath.length; at += 1) {
        const ch = absPath.charAt(at);
        slug += SLUG_CHARS.has(ch) ? ch : SLUG_FILL;
    }
    return slug;
};

const countIndexEntries = function countIndexEntries(text: string): number {
    return text.split(LINE_BREAK).filter((line) => line.trim().startsWith(INDEX_ENTRY_MARK)).length;
};

const readMemoryFile = function readMemoryFile(dir: string, name: string): MemoryFile {
    const abs = path.join(dir, name);
    const text = readFileSync(abs, "utf8");
    if (name === MEMORY_INDEX) {
        return { bytes: 0, index: countIndexEntries(text), type: null };
    }
    return { bytes: statSync(abs).size, index: 0, type: frontmatterValue(text, TYPE_KEY) ?? UNSPECIFIED_TYPE };
};

export const memoryDirOf = function memoryDirOf(root: string): string {
    return path.join(homedir(), relativePath("claude.root"), MEMORY_PROJECTS, projectSlug(root), MEMORY_FOLDER);
};

export const collectMemory = function collectMemory(root: string): MemoryStats {
    const dir = memoryDirOf(root);
    const entries = readdirOrNull(dir);
    if (entries === null) {
        return { byType: new Map(), bytes: 0, dir, files: 0, indexEntries: 0, present: false };
    }
    const parsed = entries
        .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(MARKDOWN_EXTENSION))
        .map((entry) => readMemoryFile(dir, entry.name));
    const typed = parsed.filter((info) => info.type !== null);
    return {
        byType: tally(typed, (info) => info.type ?? UNSPECIFIED_TYPE),
        bytes: parsed.reduce((sum, info) => sum + info.bytes, 0),
        dir,
        files: typed.length,
        indexEntries: parsed.reduce((sum, info) => sum + info.index, 0),
        present: true,
    };
};
```
