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

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 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
// 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.

// 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.

// 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.

// 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, which abstracts runtime API differences.

contextMessages: Safe Context Extraction

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

// 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.

// 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 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 Registers flags, commands, and all four lifecycle hooks
Context utilities extensions/context-compat.ts Provides contextMessages() and latestMarkerIsActive() for safe context reading
Rule source skills/i-have-adhd/SKILL.md Contains the ADHD-friendly rule set injected into conversations
Extension manifest 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 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 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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →