# Pi vs OMP Extension Implementation Differences in i-have-adhd: A Technical Comparison

> Explore Pi vs OMP extension implementation differences in i-have-adhd. Discover how identical core logic leverages runtime-specific SDKs and infrastructure for seamless operation.

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

---

**Both the Pi and OMP extensions in i-have-adhd share identical core logic but differ only in runtime-specific SDK imports and underlying infrastructure, with the same source file [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) powering both runtimes.**

The `ayghri/i-have-adhd` repository provides an ADHD-friendly coding assistant mode that can be injected into AI conversations. The project uniquely ships the same extension for two different agent runtimes: **Pi** (a local daemon-based runtime) and **OMP** (a web-socket server-based runtime). Understanding how these implementations differ helps developers port extensions between runtimes or debug runtime-specific issues.

## Runtime-Specific SDK Imports

The most visible difference between the Pi and OMP extensions lies in their **import statements**. Both implementations reside in the same file ([`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts)), but the runtime environment determines which SDK package provides the `ExtensionAPI`.

**Pi extension import:**

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

```

**OMP extension import:**

```typescript
import { getAgentDir, type ExtensionAPI, type ExtensionContext } from "@earendel-works/omp-agent"

```

Despite the different package names, both imports expose **identical type names**: `ExtensionAPI`, `ExtensionContext`, and `getAgentDir`. The concrete implementations differ internally—Pi's SDK wraps a local daemon process, while OMP's SDK communicates via web-socket—but the extension code consumes them through the same interface contract.

## Flag and Command Registration APIs

Both runtimes use the same registration patterns, but the underlying registries are runtime-specific.

### Flag Registration

```typescript
pi.registerFlag("adhd", {
  description: "Enable ADHD-friendly mode",
  default: false
})

```

The `registerFlag` method exists on both Pi and OMP `ExtensionAPI` objects. According to the i-have-adhd source code, the semantics are identical: the flag can be set via CLI (`--adhd`) or through the "always-on" file mechanism. Only the flag store implementation differs—Pi maintains flags in its daemon memory, while OMP persists them through its server.

### Command Registration

```typescript
pi.registerCommand("i-have-adhd", {
  description: "Toggle ADHD mode",
  handler: async (ctx, args) => {
    // Toggle logic...
    return { action: "handled" }
  }
})

```

Both runtimes expect the same return shape: `{ action: "handled" | "continue" | "transform" }`. However, the **execution path** differs: Pi invokes commands through local process IPC, while OMP routes them through web-socket messages.

## Event Hook Implementations

The extension listens to four session lifecycle events in both runtimes:

- `"input"` — triggered on every user utterance
- `"session_start"` — fires when a new session begins
- `"session_tree"` — fires when session state is restored
- `"session_compact"` — fires after session compaction

Hook registration uses identical syntax:

```typescript
pi.on("input", async (ctx, event) => {
  // Rule-set injection logic
})

```

The **event names and handler signatures** are runtime-agnostic. The difference lies in the event emitter implementation: Pi's events originate from the local daemon's event loop, while OMP's events propagate from the web-socket connection. Timing characteristics may vary slightly due to network latency in OMP versus local IPC in Pi.

## State Persistence Mechanism

Both implementations persist the enabled state using the same entry type:

```typescript
const STATE_ENTRY_TYPE = "i-have-adhd-state"

// Persisting state
pi.appendEntry(STATE_ENTRY_TYPE, { enabled: true })

// Reading state
const entries = ctx.session.getEntries(STATE_ENTRY_TYPE)

```

The `appendEntry` and `getEntries` methods are part of the `ExtensionAPI` contract. Pi's implementation appends to a local session branch; OMP's implementation serializes entries across the web-socket to the OMP server's session manager. The **entry shape** (`{ enabled: boolean }`) remains unchanged between runtimes.

## Context Synchronization Logic

The extension uses the **shared helper** `latestMarkerIsActive` from [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) to determine whether to inject the rule-set or a disabled notice:

```typescript
import { latestMarkerIsActive } from "./context-compat"

const shouldInject = latestMarkerIsActive(ctx, "adhd-rules")

```

This helper is runtime-agnostic. It operates on the `ExtensionContext` message list, which is populated by whichever runtime's session manager is active. The algorithm for determining the "latest marker" is **identical** in both Pi and OMP contexts.

## "Always-On" File Detection

Both extensions check for an agent-local file to enable automatic ADHD mode:

```typescript
const alwaysOnPath = path.join(getAgentDir(), ".i-have-adhd-always")
const alwaysOn = fs.existsSync(alwaysOnPath)

```

The `getAgentDir()` function resolves to:
- **Pi**: `~/.config/pi-agent/` (or platform equivalent)
- **OMP**: The OMP server's configured agent directory

This path resolution is the only runtime-specific behavior; the file existence check and resulting flag behavior are shared.

## UI Theme Compatibility

Visual feedback uses identical theme tokens:

```typescript
const statusIcon = ctx.ui.theme.fg("success", "●")
const statusText = ctx.ui.theme.fg("accent", "ADHD ON")

```

Both Pi and OMP UI themes implement the `"success"` and `"accent"` color tokens, ensuring consistent visual output regardless of runtime.

## Architecture Summary Table

| Aspect | Pi Implementation | OMP Implementation |
|--------|-------------------|-------------------|
| **SDK Package** | `@earendil-works/pi-coding-agent` | `@earendil-works/omp-agent` |
| **Transport** | Local daemon IPC | Web-socket server |
| **Event Source** | Pi daemon event loop | OMP server event emitter |
| **Session Storage** | Local branch files | Server-persisted entries |
| **Agent Directory** | `~/.config/pi-agent/` | OMP server agent path |
| **Core Logic** | Identical | Identical |

## Practical Porting Example

The same extension loads into either runtime with only the agent creator changing:

**Pi runtime entry point:**

```typescript
import { createPiAgent } from "@earendil-works/pi-coding-agent"
import iHaveAdhdExtension from "./extensions/i-have-adhd"

const pi = createPiAgent()
pi.loadExtension(iHaveAdhdExtension)
pi.start()

```

**OMP runtime entry point:**

```typescript
import { createOmpAgent } from "@earendil-works/omp-agent"
import iHaveAdhdExtension from "./extensions/i-have-adhd"

const omp = createOmpAgent()
omp.loadExtension(iHaveAdhdExtension)
omp.start()

```

The [`i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/i-have-adhd.ts) file requires **zero modifications** between these two setups. The runtime-specific `ExtensionAPI` implementation is injected by the respective `create*Agent` function.

## Summary

- **Single source file**: Both Pi and OMP extensions compile from [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) with no runtime-specific branches in the core logic.

- **SDK swap**: The only code-level difference is the package name in the import statement—`@earendil-works/pi-coding-agent` versus `@earendil-works/omp-agent`.

- **Pluggable infrastructure**: Flag registries, command dispatchers, event emitters, and session managers are abstracted behind the `ExtensionAPI` interface.

- **Identical user experience**: Commands, configuration files, color themes, and state persistence behave the same regardless of runtime.

- **Shared utilities**: Helper functions like `latestMarkerIsActive` in [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts) are runtime-agnostic.

## Frequently Asked Questions

### Why does i-have-adhd use the same file for both Pi and OMP?

The [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts) file contains **no runtime-specific conditional logic**. Both Pi and OMP provide an `ExtensionAPI` that satisfies the same interface contract. The runtime environment (determined by which SDK is imported) supplies the concrete implementation, allowing the extension code to remain portable.

### Can I run both Pi and OMP versions simultaneously?

Yes, but they operate as **separate processes with separate session stores**. The Pi extension manages local daemon sessions; the OMP extension connects to a remote or local OMP server. They do not share state unless you configure external synchronization.

### What happens if I mix Pi and OMP SDK imports in the same project?

This produces **type conflicts and runtime errors**. The `ExtensionAPI` types from `@earendil-works/pi-coding-agent` and `@earendil-works/omp-agent` are structurally identical but nominally distinct. Your build system should enforce one runtime per process—the [`package.json`](https://github.com/ayghri/i-have-adhd/blob/main/package.json) scripts in i-have-adhd demonstrate this separation with distinct `"pi"` and `"omp"` entry points.

### How do I debug which runtime is active?

Check the resolved path from `getAgentDir()`. In [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts), the extension logs or error messages that include the agent directory will reveal whether you're running under Pi (`~/.config/pi-agent/`) or OMP (server-configured path). The runtime-specific package name will also appear in stack traces.