# On-Disk vs. In-Memory Skill Loading in the i-have-adhd Plugin

> Understand on-disk vs in-memory skill loading in the i-have-adhd plugin. Learn how caching improves performance by reducing filesystem reads after the initial load.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: internals
- Published: 2026-08-18

---

**The i-have-adhd plugin reads behavioral rules from [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) using `readFileSync`; on-disk loading invokes this read every time the function runs, while in-memory loading caches the result in a module-level constant after the first read, eliminating subsequent filesystem I/O.**

The `ayghri/i-have-adhd` repository provides an editing assistant for users with ADHD, implemented as both a native VS Code extension and an OpenCode plugin. Understanding how this plugin loads its skill rules matters for performance, reliability, and debugging. This article examines the two loading strategies—**on-disk** versus **in-memory**—as implemented in the source code.

## How On-Disk Skill Loading Works

On-disk skill loading occurs when `loadRules()` reads the skill file from the filesystem at runtime. In [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts), this function is defined at lines 46–64:

```typescript
// extensions/i-have-adhd.ts
function loadRules(): string {
  const content = readFileSync(SKILL_PATH, "utf8");
  const rules = stripFrontmatter(content);
  return rules;
}

```

Each invocation of `loadRules()` executes `readFileSync(SKILL_PATH, "utf8")`, performing a blocking filesystem read. The `SKILL_PATH` constant points to [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) within the repository. If the file is missing or unreadable, the function throws an error (lines 50–55), propagating the failure to the caller.

This approach ensures the latest version of the rules is always retrieved, but incurs I/O overhead on every call.

## How In-Memory Skill Loading Works

In-memory skill loading caches the rules after the first successful read, reusing the cached string for all subsequent operations. The caching happens through a module-level constant declared at line 99 of [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts):

```typescript
// extensions/i-have-adhd.ts
const rules = loadRules();  // Executed once when module is first imported

```

When the extension initializes, `loadRules()` runs once, reads [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) from disk, and stores the stripped content in `rules`. The `syncContext()` function then references this cached value without additional filesystem access:

```typescript
// extensions/i-have-adhd.ts
function syncContext(ctx: ExtensionContext): void {
  const injected = rulesAreInContext(ctx);
  if (enabled && !injected) {
    // Uses in-memory `rules` — no disk I/O
    pi.sendMessage({
      customType: RULES_MESSAGE_TYPE,
      content: `${RULES_HEADER}\n\n${rules}`
    });
  }
}

```

The `rulesAreInContext()` check prevents duplicate injection, but the source of truth remains the cached `rules` string throughout the session.

## Key Differences Summarized

| Aspect | On-Disk Loading | In-Memory Loading |
|--------|-----------------|-------------------|
| **Execution frequency** | Every time `loadRules()` is called | Once at module initialization |
| **Filesystem I/O** | Occurs on each invocation | Occurs only on first import |
| **Error exposure** | Each call can throw read errors | Only the initial call can throw |
| **Performance** | Higher latency, variable | Near-zero latency after startup |
| **Rule freshness** | Always reads current file | Fixed at module load time |

## OpenCode Plugin Implementation

The `.opencode/plugins/i-have-adhd.mjs` file implements a similar pattern for the OpenCode runtime. The plugin registers the skill directory and reads [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) once during plugin initialization, aligning with the in-memory caching strategy:

```javascript
// .opencode/plugins/i-have-adhd.mjs (conceptual)
// Reads SKILL.md at startup, caches for all subsequent requests
const skillRules = fs.readFileSync(skillPath, "utf-8");

```

This mirrors the extension's design: favor single-read caching over repeated filesystem access.

## Toggle Behavior and Session Management

Users enable or disable ADHD mode through commands that affect the `enabled` flag, not the loading mechanism:

```bash
> /i-have-adhd    # Enables mode, uses cached `rules` for injection

> stop adhd       # Disables mode, cached `rules` remains in memory

```

The `hooks/always-on.mjs` file ensures the skill file exists and is readable, but does not alter the caching logic. Whether enabled or disabled, the `rules` constant remains populated after the first module load.

## Summary

- **On-disk skill loading** reads [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) via `readFileSync()` on every `loadRules()` call, ensuring fresh content at the cost of repeated I/O.
- **In-memory skill loading** executes `loadRules()` once at module initialization, storing the result in the `rules` constant for zero-overhead reuse.
- The `ayghri/i-have-adhd` extension uses **in-memory loading** as its production strategy, with on-disk loading available as the underlying mechanism.
- The OpenCode plugin (`.opencode/plugins/i-have-adhd.mjs`) follows the same pattern, reading the skill file once at startup.

## Frequently Asked Questions

### What happens if [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) is deleted after the plugin starts?

The **in-memory caching** protects against this scenario. Since `rules` is populated at module initialization, subsequent `syncContext()` calls use the cached string without filesystem access. The plugin continues functioning normally until restarted, at which point `loadRules()` would fail.

### Can I force the plugin to reload rules from disk without restarting VS Code?

The source code does not expose a reload command. The `rules` constant is initialized once via `const rules = loadRules()` and never reassigned. To refresh rules, you must reload the window or restart the extension host, triggering a new module evaluation.

### Does in-memory loading consume significant RAM?

No. The `rules` string contains the stripped content of [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md)—typically a few kilobytes of markdown. This memory footprint is negligible compared to the extension's overall resource usage and is preferable to repeated filesystem calls.

### Why does the OpenCode plugin use the same loading strategy as the extension?

Both implementations prioritize **performance and reliability**. Reading from disk once at startup eliminates file-locking issues, reduces latency during active sessions, and ensures consistent rule application. The unified approach simplifies maintenance across the native extension and OpenCode runtime environments.