# How the Pi Extension Handles Session Lifecycle Events for i-have-adhd

> Discover how the Pi extension manages session lifecycle events for i-have-adhd. Learn about session_start, session_tree, session_compact, and input events with TypeScript state restoration.

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

---

**The Pi extension for i-have-adhd synchronizes ADHD-friendly rules across session boundaries by hooking into four lifecycle events—`session_start`, `session_tree`, `session_compact`, and `input`—with state restoration and context reinjection logic written in TypeScript.**

The `i-have-adhd` extension is a Pi runtime extension that maintains persistent ADHD-friendly formatting rules throughout a conversation's lifespan. Unlike stateless skill prompts, this extension must survive session tree rewrites, compaction events, and user toggles. Understanding how it handles **Pi extension session lifecycle events** reveals the architecture behind reliable context management in the `ayghri/i-have-adhd` repository.

## The Four Lifecycle Hooks in i-have-adhd.ts

The extension registers handlers in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) using `pi.on()` bindings. Each hook addresses a specific session state transition.

### session_start: Initial State Restoration

When a new session begins, the extension must recover the user's ADHD mode preference from previous interactions. The `session_start` handler triggers `restoreState()` to:

- Check stored preferences or apply defaults
- Update the UI status indicator
- Inject the rule set if ADHD mode is enabled

```typescript
// extensions/i-have-adhd.ts L237-L239
pi.on("session_start", async (_event, ctx) => restoreState(ctx));

```

### session_tree: Branch Survival

Pi sessions can fork into new trees after rewrites or re-runs. The `session_tree` event fires when this occurs, and the extension runs identical restoration logic to ensure rules propagate across branches.

```typescript
// extensions/i-have-adhd.ts L237-L239 (shared handler)
pi.on("session_tree", async (_event, ctx) => restoreState(ctx));

```

### session_compact: Context Reinjection

Session compaction collapses old entries to manage token limits. This risks removing the injected rule set from context. The `session_compact` handler verifies marker presence and reinjects rules when necessary.

```typescript
// extensions/i-have-adhd.ts L239-L240
pi.on("session_compact", async (_event, ctx) => syncContext(ctx));

```

### input: Toggle Detection and Response

The `input` hook intercepts user commands to enable or disable ADHD mode. It recognizes trigger phrases and manages both UI and non-UI response paths.

```typescript
// extensions/i-have-adhd.ts L211-L236
pi.on("input", async (event, ctx) => {
  const input = event.text.trim().toLowerCase();

  if (input === "/skill:i-have-adhd") {
    setEnabled(true, ctx);
    return { action: "handled" };
  }

  if (enabled && STOP_PHRASES.has(input)) {
    setEnabled(false, ctx);
    return ctx.hasUI
      ? { action: "handled" }
      : { action: "transform", text: `Reply with exactly: ${DISABLE_CONFIRMATION}` };
  }

  return { action: "continue" };
});

```

## Helper Utilities in context-compat.ts

The extension delegates context inspection to [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts), which abstracts runtime API differences.

### contextMessages: Safe Context Extraction

The `contextMessages()` function extracts raw context entries regardless of Pi runtime version.

```typescript
// Extracts messages from various Pi runtime context formats
// Source: extensions/context-compat.ts L12-L34
function contextMessages(ctx: any): Array<{role: string, content: string}> {
  // Handles both legacy ctx.messages and modern ctx.context arrays
  if (Array.isArray(ctx.context)) return ctx.context;
  if (Array.isArray(ctx.messages)) return ctx.messages;
  return [];
}

```

### latestMarkerIsActive: State Detection

The `latestMarkerIsActive()` function scans for custom markers—`i-have-adhd-rules` versus `i-have-adhd-disabled`—to determine current mode status without relying on external state.

```typescript
// Scans context for most recent marker to determine active state
// Source: extensions/context-compat.ts L41-L60
function latestMarkerIsActive(ctx: any): boolean | null {
  const messages = contextMessages(ctx);
  // Reverse scan finds most recent marker
  for (let i = messages.length - 1; i >= 0; i--) {
    const content = messages[i].content || '';
    if (content.includes('i-have-adhd-rules')) return true;
    if (content.includes('i-have-adhd-disabled')) return false;
  }
  return null; // No marker found
}

```

## Rule Set Injection Flow

The extension maintains persistence through marker-based state tracking rather than external storage:

1. **Enable**: Inject [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) content with `i-have-adhd-rules` marker
2. **Track**: Scan context on each lifecycle event via `latestMarkerIsActive()`
3. **Restore**: Reinject on `session_start`, `session_tree`, or `session_compact` if marker missing
4. **Disable**: Inject `i-have-adhd-disabled` marker and skip rule injection

This design ensures ADHD-friendly formatting survives session interruptions without requiring server-side state synchronization.

## Key Files and Their Responsibilities

| File | Location | Purpose |
|------|----------|---------|
| Main extension | [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) | Registers flags, commands, and all four lifecycle hooks |
| Context utilities | [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) | Provides `contextMessages()` and `latestMarkerIsActive()` for safe context reading |
| Rule source | [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) | Contains the ADHD-friendly rule set injected into conversations |
| Extension manifest | [`package.json`](https://github.com/ayghri/i-have-adhd/blob/main/package.json) | Declares Pi extension entry point |
| Test suite | `tests/…` | Validates injection behavior and state persistence across session events |

## Summary

- **Four hooks handle all transitions**: `session_start` and `session_tree` restore state; `session_compact` guards against rule loss; `input` enables user toggles.
- **Marker-based detection**: The extension uses `i-have-adhd-rules` and `i-have-adhd-disabled` markers in context rather than external storage.
- **Context abstraction**: `contextMessages()` and `latestMarkerIsActive()` in [`context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/context-compat.ts) isolate runtime API variations.
- **Dual response paths**: Toggle commands return `"handled"` for UI sessions or `"transform"` with forced confirmation text for headless contexts.

## Frequently Asked Questions

### Where are the Pi extension lifecycle handlers registered in i-have-adhd?

The handlers are registered at lines 237-240 of [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) using `pi.on()` calls for `session_start`, `session_tree`, and `session_compact`. The `input` handler occupies lines 211-236. All registrations occur during extension initialization.

### Why does i-have-adhd need a session_compact handler?

Session compaction removes old context entries to manage token limits. Without reinjection logic, the ADHD rule set could be eliminated while the user still expects ADHD-friendly formatting. The `session_compact` handler detects this condition via `latestMarkerIsActive()` and calls `syncContext()` to restore markers.

### How does the extension detect whether ADHD mode is currently active?

It scans conversation context in reverse chronological order using `latestMarkerIsActive()` from [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts). This function looks for `i-have-adhd-rules` (active) or `i-have-adhd-disabled` (inactive) markers, returning `null` if neither exists.

### What's the difference between the "handled" and "transform" action responses?

`{ action: "handled" }` terminates input processing and signals the Pi runtime to proceed without modification—used when a UI is present to display status. `{ action: "transform", text: "..." }` forces a specific model response, ensuring users receive confirmation when disabling ADHD mode in headless contexts without UI feedback.