# How the Pi CLI Extension Restores 'ADHD Mode' State Across Sessions

> Learn how the Pi CLI extension restores your 'ADHD mode' state automatically. It saves and recalls your session settings for a seamless workflow.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: how-to-guide
- Published: 2026-09-02

---

**The Pi CLI extension restores 'ADHD mode' state by calling `restoreState()` on every session start, which reads a saved boolean from extension storage or falls back to default enablement based on the `adhd` flag, `alwaysOn` config, or sentinel file presence.**

The ADHD mode feature in the [i-have-adhd](https://github.com/ayghri/i-have-adhd) repository allows users to persist a preference for ADHD-friendly terminal output across multiple Pi CLI sessions. This capability hinges on a deterministic state restoration pipeline implemented in the core extension file, orchestrated through Pi's event lifecycle hooks.

## The Entry Point: `restoreState()` Function

The state restoration logic centers on the [`restoreState`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts#L61-L71) function in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts). This function executes synchronously whenever a new session initializes or the session tree rebuilds.

```typescript
// Simplified structure based on lines 61-71
async function restoreState(ctx: ExtensionContext): Promise<void> {
  const savedState = await getSavedState(ctx);
  const enabledByDefault = 
    pi.getFlag("adhd") === true || 
    config.alwaysOn === true || 
    fs.existsSync(ADHD_SENTINEL_FILE);
  
  enabled = savedState ?? enabledByDefault;
  updateStatus(ctx);
  await syncContext(ctx);
}

```

The function performs three critical operations:

1. **Retrieve persisted state** via `getSavedState(ctx)`
2. **Compute default enablement** when no saved state exists
3. **Synchronize the resolved value** to both UI and Pi context

## Default Enablement Hierarchy

When `getSavedState()` returns `undefined`, the extension evaluates three fallback conditions in order of priority ([lines 64-66](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts#L64-L66)):

| Source | Mechanism | Example |
|--------|-----------|---------|
| **Pi flag** | Command-line `--adhd` flag | `pi --adhd` |
| **Configuration** | `alwaysOn` extension setting | `config.set("i-have-adhd.alwaysOn", true)` |
| **Sentinel file** | Existence of `.i-have-adhd-always` | `touch ~/.i-have-adhd-always` |

The first condition that evaluates to `true` determines the default state. This design provides multiple user-centric override mechanisms without requiring explicit command invocation.

## Lifecycle Event Registration

The extension ensures automatic state restoration by binding to Pi's session events ([lines 37-40](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts#L37-L40)):

```typescript
pi.on("session_start", async (_event, ctx) => restoreState(ctx));
pi.on("session_tree",  async (_event, ctx) => restoreState(ctx));
pi.on("session_compact", async (_event, ctx) => syncContext(ctx));

```

- **`session_start`**: Triggers when a new Pi session initializes
- **`session_tree`**: Triggers when the session tree structure rebuilds (e.g., after configuration changes)
- **`session_compact`**: Triggers during session compaction; only synchronizes context without full restoration

This event binding guarantees that ADHD mode state remains consistent regardless of how the session evolves.

## State Persistence Mechanism

The `getSavedState()` function (lines ~70-90) implements the read path from extension storage:

```typescript
// Conceptual implementation based on source analysis
async function getSavedState(ctx: ExtensionContext): Promise<boolean | undefined> {
  const statePath = path.join(ctx.extensionPath, ".i-have-adhd-state.json");
  try {
    const content = await fs.promises.readFile(statePath, "utf-8");
    const parsed = JSON.parse(content);
    return parsed.enabled as boolean;
  } catch {
    return undefined; // No saved state or invalid file
  }
}

```

When users explicitly toggle ADHD mode via `/pi i-have-adhd on|off`, the extension writes to this JSON file, ensuring subsequent sessions inherit the preference.

## Practical Usage Patterns

### Toggle Mode Mid-Session

```bash
/pi i-have-adhd on   # Enable for current session and persist

/pi i-have-adhd off  # Disable for current session and persist

```

### Launch with Flag Override

```bash
pi --adhd            # Start with ADHD mode enabled regardless of saved state

```

### Permanent System-Wide Enablement

```bash
touch ~/.i-have-adhd-always  # Create sentinel file for default-on behavior

```

### Programmatic Access in Scripts

```typescript
import { pi } from "pi";

// Check current effective state
const isAdhdMode: boolean = pi.getFlag("adhd");

// Conditionally format output
console.log(isAdhdMode ? "✓ Task complete" : "Done");

```

## Key Source Files

| File | Purpose |
|------|---------|
| [[`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts)](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) | Core extension implementing `restoreState`, `getSavedState`, and Pi event hooks |
| [[`scripts/check_pi_extension.ts`](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_pi_extension.ts)](https://github.com/ayghri/i-have-adhd/blob/main/scripts/check_pi_extension.ts) | Verification utility for extension loading and flag propagation |
| [[`README.md`](https://github.com/ayghri/i-have-adhd/blob/main/README.md)](https://github.com/ayghri/i-have-adhd/blob/main/README.md) | Feature overview and configuration documentation |
| [[`INSTALL.md`](https://github.com/ayghri/i-have-adhd/blob/main/INSTALL.md)](https://github.com/ayghri/i-have-adhd/blob/main/INSTALL.md) | Setup instructions for Pi CLI integration |

## Summary

- **Automatic restoration**: The `restoreState()` function runs on every `session_start` and `session_tree` event, ensuring ADHD mode state is always current
- **Hierarchical defaults**: Without saved state, the extension checks the `adhd` flag, `alwaysOn` config, and sentinel file in sequence
- **Persistent storage**: User preferences serialize to [`.i-have-adhd-state.json`](https://github.com/ayghri/i-have-adhd/blob/main/.i-have-adhd-state.json) in the extension context directory
- **Multiple override paths**: Command-line flags take precedence for one-off sessions, while config and sentinel files support persistent defaults

## Frequently Asked Questions

### How does the extension know which state to restore first?

The [`restoreState`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts#L61-L71) function prioritizes explicitly saved state via `getSavedState()`. Only when that returns `undefined` does it evaluate default enablement conditions. This ensures user-explicit choices always override system defaults.

### Can I enable ADHD mode by default without using the toggle command?

Yes. Create a sentinel file named `.i-have-adhd-always` in your home directory, or set `"i-have-adhd.alwaysOn": true` in your Pi configuration. Both methods cause `restoreState()` to default `enabled` to `true` when no explicit saved state exists.

### What happens if I launch Pi with the `--adhd` flag but have a conflicting saved state?

The `--adhd` flag affects `enabledByDefault` calculation, but `savedState` takes precedence in the nullish coalescing expression `savedState ?? enabledByDefault`. To force flag dominance, delete the state file at `{extensionPath}/.i-have-adhd-state.json` before launching.

### Why does `session_compact` use `syncContext` instead of `restoreState`?

Session compaction preserves existing logical state without re-evaluating default conditions. The [`syncContext`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts#L37-L40) call ensures Pi's internal flag registry remains synchronized with the extension's `enabled` variable, avoiding redundant file I/O during optimization passes.