How the i‑have‑adhd Plugin Architecture Supports Multiple AI Runtimes (Claude, Codex, OpenCode, Pi, OMP)
The i‑have‑adhd project uses a single source‑of‑truth skill file (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 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:
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.
// 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 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:
// .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 entry point ("omp" script for OMP).
The extension registers:
- A flag for state tracking
- The
/i‑have‑adhdcommand withon/off/stoparguments - Event listeners:
input,session_start,session_tree,session_compact
// 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 historylatestMarkerIsActive(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 |
Summary
- Single source of truth:
SKILL.mdcentralizes 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.tsnormalizes 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 "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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →