# How the i‑have‑adhd Plugin Architecture Supports Multiple AI Runtimes (Claude, Codex, OpenCode, Pi, OMP)

> Explore how the i-have-adhd plugin architecture seamlessly integrates multiple AI runtimes including Claude, Codex, Pi, and OMP using a single skill file and adapter system.

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

---

**The i‑have‑adhd project uses a single source‑of‑truth skill file ([`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md)) with runtime‑specific adapters that translate universal ADHD‑friendly rules into each AI platform's native extension or hook mechanism.**

The [ayghri/i‑have‑adhd](https://github.com/ayghri/i-have-adhd) open‑source project solves a fragmentation problem: AI coding assistants each expose different APIs for customizing behavior. Rather than maintaining separate rule sets, the plugin architecture centralizes the core ADHD‑friendly instructions in one Markdown file, then provides lightweight runtime adapters that inject those rules using whatever mechanism each platform supports.

## How the Multi‑Runtime Plugin Architecture Works

The architecture follows three principles: **unified rules**, **runtime‑specific injection**, and **shared utility abstractions**. This design lets users toggle ADHD mode consistently across Claude, Codex, OpenCode, Pi, and OMP without learning different command syntaxes.

### The Canonical Skill File

All runtimes read from `skills/i‑have‑adhd/SKILL.md` — a single Markdown file containing the complete rule set. This file has no frontmatter and uses plain language instructions that any runtime can parse and inject.

In `extensions/i‑have‑adhd.ts` (lines 46‑64), the Pi/OMP extension reads this file directly:

```typescript
const rules = await Deno.readTextFile(
  new URL("../skills/i-have-adhd/SKILL.md", import.meta.url)
);

```

The OpenCode plugin performs the same read at lines 38‑42 of `.opencode/plugins/i‑have‑adhd.mjs`. By keeping rules in one place, updates propagate automatically to all supported runtimes.

## Runtime‑Specific Implementation Details

Each adapter implements the same user‑visible command (`/i‑have‑adhd`) but uses the host platform's native extension mechanism.

### Claude Code: SessionStart Hook

Claude Code uses a plugin manifest at `.claude‑plugin/plugin.json` that declares an always‑on hook. The `hooks/always‑on.mjs` file injects the ruleset once at session start and supports a "stop adhd mode" command to remove it.

```javascript
// hooks/always-on.mjs (simplified execution flow)
if (enabled && !alreadyInjected) {
  pi.sendMessage({
    customType: "i-have-adhd-rules",
    content: `${RULES_HEADER}\n\n${rules}`,
    display: false,
  }, { triggerTurn: false });
}

```

The `alreadyInjected` check prevents duplicate injection across turns.

### Codex: Native Skill Manifest

Codex requires no custom code. The `.codex‑plugin/plugin.json` manifest points directly to the `skills/` directory, and Codex reads [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) as a native skill file. The `/i‑have‑adhd` command logic works through Codex's built‑in skill command system.

### OpenCode: System Transform Hook

OpenCode supports experimental hooks for modifying conversation state. The `.opencode/plugins/i‑have‑adhd.mjs` plugin registers an `experimental.chat.system.transform` handler that runs on every turn:

```javascript
// .opencode/plugins/i-have-adhd.mjs
'experimental.chat.system.transform': async (_input, output) => {
  if (!fs.existsSync(flagPath)) return;  // check always-on flag
  
  const body = rulesetBody();            // strips frontmatter from SKILL.md
  const header = 'ADHD MODE ACTIVE (always‑on)...';
  const injected = `${header}\n\n${body}`;
  output.system.push(injected);
},

```

The flag file at `~/.config/opencode/.i‑have‑adhd‑always` persists user preference across sessions.

### Pi and OMP: Shared TypeScript Extension

Pi (the coding agent) and OMP (Open Model Platform) share the same extension implementation in `extensions/i‑have‑adhd.ts`. Both platforms expose identical extension APIs, differentiated only by the [`package.json`](https://github.com/ayghri/i-have-adhd/blob/main/package.json) entry point (`"omp"` script for OMP).

The extension registers:
- A flag for state tracking
- The `/i‑have‑adhd` command with `on`/`off`/`stop` arguments
- Event listeners: `input`, `session_start`, `session_tree`, `session_compact`

```typescript
// extensions/i-have-adhd.ts
pi.registerCommand("i-have-adhd", {
  description: "Toggle ADHD-friendly output for this session",
  handler: async (args, ctx) => {
    const argument = args.trim().toLowerCase();
    if (argument === "") setEnabled(!enabled, ctx);
    else if (argument === "on") setEnabled(true, ctx);
    else if (argument === "off" || argument === "stop") setEnabled(false, ctx);
    else ctx.ui.notify("Usage: /i-have-adhd [on|off]", "warning");
  },
});

```

Rule injection uses the `latestMarkerIsActive` helper from `extensions/context‑compat.ts` to avoid duplicates.

## Shared Infrastructure: Context Compatibility

The `extensions/context‑compat.ts` module abstracts differences between Pi/OMP and Claude session managers. It exports two critical functions:

- `contextMessages(ctx)` — normalizes access to conversation history
- `latestMarkerIsActive(ctx, markerType)` — checks whether rules are already present

This abstraction allows the same `setEnabled()` logic to work across runtimes with different internal APIs.

## Always‑On Mode Implementation

Both Claude and OpenCode support persistent "always‑on" behavior through flag files:

| Runtime | Flag Location | Trigger |
|---------|-------------|---------|
| Claude | `~/.config/claude/.i‑have‑adhd‑always` | `hooks/always‑on.mjs` checks at session start |
| OpenCode | `~/.config/opencode/.i‑have‑adhd‑always` | Checked in every `system.transform` call |

This design pattern keeps the user experience identical: create the flag file once, and ADHD mode activates automatically for all future sessions without explicit `/i‑have‑adhd` commands.

## Key Files and Their Roles

| Purpose | Path |
|---------|------|
| Universal skill rules | `skills/i‑have‑adhd/SKILL.md` |
| Claude plugin manifest | `.claude‑plugin/plugin.json` |
| Codex skill manifest | `.codex‑plugin/plugin.json` |
| OpenCode runtime plugin | `.opencode/plugins/i‑have‑adhd.mjs` |
| Pi/OMP extension source | `extensions/i‑have‑adhd.ts` |
| Session manager abstractions | `extensions/context‑compat.ts` |
| Claude session hook | `hooks/always‑on.mjs` |
| Runtime documentation | [`AGENTS.md`](https://github.com/ayghri/i-have-adhd/blob/main/AGENTS.md) |

## Summary

- **Single source of truth**: [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) centralizes ADHD‑friendly rules for all runtimes
- **Runtime adapters**: Each platform gets a thin translation layer matching its native extension API
- **Command uniformity**: `/i‑have‑adhd [on|off|stop]` works identically across Claude, Codex, OpenCode, Pi, and OMP
- **Always‑on persistence**: Flag files enable automatic session‑wide activation without per‑turn overhead
- **Shared utilities**: `context‑compat.ts` normalizes session inspection across incompatible APIs

## Frequently Asked Questions

### What file contains the actual ADHD rules that all runtimes use?

All runtimes read from `skills/i‑have‑adhd/SKILL.md`. This Markdown file contains the complete rule set without frontmatter, making it portable across platforms with different parsing requirements.

### How does the plugin prevent injecting the same rules multiple times?

Each runtime adapter tracks injection state differently. The Claude hook uses an `alreadyInjected` boolean, while Pi/OMP use `latestMarkerIsActive()` from `context‑compat.ts` to scan conversation history for existing markers. OpenCode appends on every turn but relies on deterministic content hashing to avoid visible duplicates.

### Can I use the same always‑on flag for both Claude and OpenCode?

No — each runtime checks its own configuration directory. Claude looks for `~/.config/claude/.i‑have‑adhd‑always`, while OpenCode uses `~/.config/opencode/.i‑have‑adhd‑always`. Both use the same filename convention, but the paths are runtime‑specific.

### Why do Pi and OMP share the same extension file?

Both platforms expose identical JavaScript/TypeScript extension APIs for registering commands, flags, and event listeners. The [`package.json`](https://github.com/ayghri/i-have-adhd/blob/main/package.json) `"omp"` script entry point selects the appropriate runtime context, allowing `extensions/i‑have‑adhd.ts` to serve both without code duplication.

### Does Codex require any custom code to support the skill?

No — Codex's native skill system reads `.codex‑plugin/plugin.json`, which points directly to the `skills/` directory. This makes Codex the simplest integration: zero custom code, just a manifest file declaring where to find [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md).