# How the i‑have‑adhd Extension Integrates with the Pi Runtime: A Complete Technical Guide

> Discover how the i-have-adhd extension integrates with the Pi runtime using ExtensionAPI. Learn about runtime flags, session events, LLM context manipulation, and ADHD-friendly output.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: deep-dive
- Published: 2026-09-13

---

**The i‑have‑adhd extension interacts with the Pi runtime through the ExtensionAPI, registering runtime flags and slash commands, listening to session lifecycle events, and manipulating the LLM context via custom messages to enable ADHD‑friendly output modes.**

The [ayghri/i‑have‑adhd](https://github.com/ayghri/i-have-adhd) repository implements a Pi runtime extension that transforms how the coding agent generates responses for users with ADHD. Built on the `@earendil‑works/pi‑coding‑agent` package, this extension leverages the `ExtensionAPI` to persist state, inject behavioral rules into the prompt context, and synchronize UI indicators. Understanding this integration requires examining how the extension hooks into Pi’s session management and message handling systems.

## Importing the Pi Runtime Extension API

Every Pi extension begins by importing the runtime types and utilities that enable communication with the agent.

```typescript
import { getAgentDir, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";

```

In [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) (lines 4‑8), the default export function receives the `ExtensionAPI` instance (conventionally named `pi`) as its argument. This object provides the complete interface for registering capabilities and interacting with the session. The `ExtensionContext` type represents the current session state, including access to UI components and message history.

## Registering Pi Runtime Flags and Commands

The extension exposes user‑facing controls through Pi’s flag and command registries.

### Runtime Flags

At lines 81‑85 of [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts), the code registers a boolean flag that allows users to start sessions with ADHD mode pre‑enabled:

```typescript
pi.registerFlag("adhd", {
  description: "Start with ADHD-friendly output enabled",
  type: "boolean",
  default: false
});

```

This makes the flag accessible via `pi.getFlag("adhd")` during initialization, allowing the extension to check user preferences before any commands are issued.

### Slash Commands

The extension registers the `/i-have-adhd` command at lines 87‑90:

```typescript
pi.registerCommand("i-have-adhd", {
  description: "Toggle ADHD-friendly output for this session",
  handler: async (args, ctx) => { ... }
});

```

When invoked, this handler toggles the internal `enabled` state and persists the change using `pi.appendEntry`, ensuring the mode survives session compactions and restarts.

## Persisting State with Session Entries

To maintain continuity across session compactions, the extension stores its state using Pi’s entry system.

```typescript
pi.appendEntry(STATE_ENTRY_TYPE, { enabled } satisfies AdhdModeState);

```

As implemented at lines 73‑76 of [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts), this creates a custom session entry with the type `i-have-adhd-state`. The `getSavedState` function later traverses the session tree to locate this entry and restore the previous mode on startup. This mechanism ensures that ADHD mode remains active even when Pi compacts the conversation history to manage context window limits.

## Handling Pi Runtime Lifecycle Events

The extension synchronizes its state by hooking into three critical Pi runtime events, as shown at lines 37‑40:

```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 session begins, allowing immediate restoration of saved preferences.
- **session_tree**: Fires when the conversation branches, ensuring consistent behavior across session variations.
- **session_compact**: Executes after Pi compresses the conversation history, giving the extension an opportunity to re‑inject rules that might have been removed during compaction.

## Injecting ADHD Rules into the Context

The core functionality involves inserting behavioral instructions into the LLM’s prompt context without displaying them to the user.

### Enabling ADHD Mode

When activating the mode, the extension sends a custom message at lines 34‑47:

```typescript
pi.sendMessage({
  customType: RULES_MESSAGE_TYPE,
  content: `${RULES_HEADER}\n\n${rules}`,
  display: false
}, { triggerTurn: false });

```

The `display: false` property keeps the instructions hidden from the chat interface, while `triggerTurn: false` prevents immediate model execution. Pi treats these custom messages as part of the system prompt, making the rules visible to the LLM for all subsequent generations.

### Disabling ADHD Mode

When toggling off, the extension injects a marker indicating the mode is disabled:

```typescript
pi.sendMessage({
  customType: DISABLED_MESSAGE_TYPE,
  content: DISABLED_NOTICE,
  display: false
}, { triggerTurn: false });

```

This ensures the model receives explicit instructions to return to standard response patterns.

## Context Detection and Synchronization

The extension must verify whether its rules are currently active in the context, particularly after compactions that may strip custom messages.

In [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) (lines 5‑12), the `latestMarkerIsActive` helper examines `contextMessages` to determine if the most recent custom message contains the rules marker or the disabled marker. The `rulesAreInContext` function uses this to decide whether re‑injection is necessary:

```typescript
const injected = rulesAreInContext(ctx);

```

If the rules are missing—for instance, after a `session_compact` event—the `syncContext` function re‑injects them to maintain consistent behavior.

## Updating the Pi UI Status Bar

The extension provides visual feedback through Pi’s status indicator system. At lines 24‑28 of the main extension file:

```typescript
ctx.ui.setStatus(STATUS_KEY, `${dot} ${label}`);

```

This updates the status bar with a green dot and "ADHD ON" label when the mode is active, unless the user has configured `hideStatus`. The status persists across turns, giving users immediate awareness of the current output mode.

## Reacting to User Input

The extension monitors incoming messages to detect disable commands without requiring explicit slash command invocation. At lines 11‑34:

```typescript
pi.on("input", async (event, ctx) => {
  // Detection logic for "stop adhd mode" or "normal mode"
});

```

When these phrases are detected, the extension disables ADHD mode and, in headless environments, forces the model to emit a confirmation string defined as `DISABLE_CONFIRMATION`. This provides graceful degradation when users prefer natural language over slash commands.

## Summary

- The i‑have‑adhd extension integrates with Pi through the `ExtensionAPI` interface, receiving the `pi` object and `ExtensionContext` during initialization.
- It registers the `adhd` runtime flag and `/i-have-adhd` slash command to expose user controls via `pi.registerFlag` and `pi.registerCommand`.
- State persistence relies on `pi.appendEntry` with custom entry types, retrieved during `session_start` and `session_tree` events to restore previous configurations.
- Rules injection uses `pi.sendMessage` with `customType` and `display: false` to insert hidden instructions into the LLM context.
- The `session_compact` event handler ensures rules remain present after context window compression by detecting their absence and re‑injecting them.
- UI synchronization occurs through `ctx.ui.setStatus`, while input monitoring via `pi.on("input")` enables natural language deactivation.

## Frequently Asked Questions

### How does the extension maintain ADHD mode across session restarts?

The extension persists the current mode using `pi.appendEntry(STATE_ENTRY_TYPE, { enabled })`, which stores a custom entry in Pi’s session tree. When Pi emits `session_start` or `session_tree` events, the `restoreState` function traverses the session history to locate this entry and reactivate the previous configuration. This mechanism ensures continuity even when the coding agent restarts or branches the conversation.

### Why does the extension need to handle the `session_compact` event separately?

Pi periodically compacts conversation history to manage token limits, which can remove custom messages containing the ADHD rules. The extension listens for `session_compact` events to trigger `syncContext`, which checks if the rules are still present using `rulesAreInContext`. If the compaction removed the instructions, the function re‑injects them via `pi.sendMessage`, ensuring the LLM continues receiving ADHD‑friendly guidance without interruption.

### Can users disable ADHD mode without using the slash command?

Yes. The extension monitors all user input through the `pi.on("input")` listener, scanning for phrases like "stop adhd mode" or "normal mode". When detected, the extension automatically disables the mode, updates the persisted state, and adjusts the UI status. In environments without a UI, it also injects a confirmation message to acknowledge the mode change, providing a natural language alternative to the `/i-have-adhd` command.

### What prevents the ADHD rules from cluttering the chat interface?

The extension passes `display: false` in the message options when calling `pi.sendMessage`. This flag instructs Pi to include the content in the LLM’s prompt context while hiding it from the user interface. Combined with `triggerTurn: false`, this ensures the rules remain invisible to users but fully visible to the model, maintaining a clean chat experience while influencing response generation.