# How the i-have-adhd Plugin Architecture Is Designed: A Multi-Runtime System

> Discover the i-have-adhd plugin architecture a multi-runtime system for LLM assistants. Learn how it enables cross-platform skill operation with a unified Markdown source.

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

---

**The i-have-adhd project implements a lightweight, runtime-agnostic plugin system that enables a single ADHD-friendly skill to operate across multiple LLM assistant platforms—including Claude Code, Codex, OpenCode, and others—through a unified Markdown source and minimal runtime-specific shims.**

The ayghri/i-have-adhd repository demonstrates how to build a portable plugin architecture for LLM assistants. By decoupling the **skill definition** from **runtime implementations**, the system allows the same 10-rule response guidelines to power behavior modifications across diverse AI coding environments. This design pattern prioritizes maintainability and user control through file-based opt-in flags.

## Core Architectural Components

The plugin architecture rests on four foundational components that separate concerns between content, configuration, and execution.

### Skill Definition (SKILL.md)

The **skill definition** serves as the single source of truth for all supported runtimes. Located at [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md), this file contains the authoritative 10 response rules written in Markdown with a YAML front-matter block. Rather than duplicating logic across platforms, every runtime references this canonical file, ensuring consistent behavior updates propagate instantly across Claude, Codex, OpenCode, and others.

### Plugin Manifests

Each supported runtime requires a **plugin manifest** to register the extension with its host environment. These JSON or TOML files declare the plugin name, description, and entry-point declarations:

- [`plugin.json`](https://github.com/ayghri/i-have-adhd/blob/main/plugin.json) — Generic manifest for general-purpose loaders
- [`opencode.json`](https://github.com/ayghri/i-have-adhd/blob/main/opencode.json) — OpenCode-specific registration  
- [`.claude-plugin/plugin.json`](https://github.com/ayghri/i-have-adhd/blob/main/.claude-plugin/plugin.json) — Claude Code integration
- [`.codex-plugin/plugin.json`](https://github.com/ayghri/i-have-adhd/blob/main/.codex-plugin/plugin.json) — Codex-specific configuration

These manifests tell the runtime where to find the JavaScript entry points without hardcoding implementation details.

### Runtime Entry Points

**Runtime entry points** are lightweight JavaScript modules that bridge the host environment with the skill system. Each follows a consistent "read-flag-and-inject" pattern:

- **OpenCode**: `.opencode/plugins/i-have-adhd.mjs` implements the `config` hook to register the skills directory and the `experimental.chat.system.transform` hook for prompt injection.
- **Claude/Code**: `hooks/always-on.mjs` executes at session start to check for the opt-in flag and conditionally output the ruleset.

Both modules resolve the path to [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) relative to their own location, strip the YAML front-matter using regex, and prepare the content for injection.

### Opt-in Flag System

User agency is enforced through a **file-based opt-in flag** system. Rather than modifying configuration files, users toggle the "always-on" mode by creating or deleting empty files in their home directory:

- OpenCode: `~/.config/opencode/.i-have-adhd-always`
- Claude: `~/.claude/.i-have-adhd-always`

The entry point modules check for file existence using `fs.existsSync()` before injecting the ruleset into system prompts.

## How the Plugin System Works

The architecture operates through a four-stage lifecycle that remains consistent across every supported runtime.

### Discovery

When an LLM assistant runtime initializes, it scans for plugin manifests (e.g., [`opencode.json`](https://github.com/ayghri/i-have-adhd/blob/main/opencode.json)). Upon detection, the host loads the specified JavaScript module—such as `./.opencode/plugins/i-have-adhd.mjs`—into the runtime environment.

### Skill Registration

The module's exported `config` hook modifies the host configuration object to include the skills directory. In OpenCode, this means updating `cfg.skills.paths` to include the relative path `../../skills`, enabling the `/i-have-adhd` command and generic skill tool to locate [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md).

### Always-On Injection

For users who have opted into persistent mode, the system intercepts system prompts before they reach the model:

1. **Flag Check**: The module verifies the existence of the runtime-specific flag file.
2. **Content Processing**: If the flag exists, the module reads [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) and strips the YAML front-matter using the regex replace `/^---[^\S\r\n]*\r?\n[\s\S]*?\r?\n---[^\S\r\n]*(?:\r?\n|$)/`.
3. **Header Injection**: A system message header ("ADHD MODE ACTIVE (always-on)...") is prepended to explain the modification.
4. **Prompt Appending**: The cleaned rule body is appended to `output.system` (OpenCode) or written to `process.stdout` (Claude), depending on the runtime's API.

This implementation guarantees that the same 10-rule logic governs behavior across all platforms without code duplication.

## Platform-Specific Implementations

While the core logic remains runtime-agnostic, each platform requires slight adaptations to accommodate different hook APIs.

### OpenCode Implementation

The OpenCode module (`.opencode/plugins/i-have-adhd.mjs`) exports a default async function returning an object with two hooks:

```javascript
export default async () => ({
  config: async (cfg) => {
    cfg.skills = cfg.skills || {};
    cfg.skills.paths = cfg.skills.paths || [];
    if (!cfg.skills.paths.includes(skillsDir)) 
      cfg.skills.paths.push(skillsDir);
  },
  'experimental.chat.system.transform': async (_, out) => {
    if (!fs.existsSync(flagPath)) return;
    const body = rulesetBody();
    const header = 'ADHD MODE ACTIVE (always‑on)...';
    out.system.push(`${header}\n\n${body}`);
  },
});

```

The `rulesetBody()` helper function handles the file reading and regex-based YAML stripping, ensuring the raw Markdown rules are injected cleanly.

### Claude and Codex Implementation

For Claude Code and Codex, the `hooks/always-on.mjs` module operates as a session-start script:

```javascript
const flagPath = path.join(
  process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'), 
  '.i-have-adhd-always'
);

if (fs.existsSync(flagPath)) {
  const skillPath = path.join(__dirname, '..', 'skills', 'i-have-adhd', 'SKILL.md');
  const body = fs.readFileSync(skillPath, 'utf8')
    .replace(/^---[\s\S]*?---/, '')
    .trim();
  console.log(`ADHD MODE ACTIVE (always‑on). ...\n\n${body}\n`);
}

```

This approach leverages stdout writing rather than object mutation, adapting to Claude's different plugin hook interface while executing identical logic.

## Enabling and Disabling the Plugin

Users control the always-on mode through simple filesystem commands. To enable persistent ADHD-friendly formatting in OpenCode:

```bash
mkdir -p ~/.config/opencode
touch ~/.config/opencode/.i-have-adhd-always

```

To disable:

```bash
rm ~/.config/opencode/.i-have-adhd-always

```

This design eliminates the need for JSON editing or API calls, making the toggle accessible to users regardless of technical expertise.

## Summary

- The **i-have-adhd plugin architecture** uses a single Markdown file ([`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md)) as the source of truth across Claude, Codex, OpenCode, and other LLM runtimes.
- **Plugin manifests** ([`plugin.json`](https://github.com/ayghri/i-have-adhd/blob/main/plugin.json), [`opencode.json`](https://github.com/ayghri/i-have-adhd/blob/main/opencode.json), etc.) register tiny JavaScript shims that adapt the skill to each platform's specific API.
- The **opt-in flag system** uses file-existence checks (`~/.config/opencode/.i-have-adhd-always`) to let users toggle "always-on" mode without code changes.
- Each runtime entry point follows a consistent pattern: check flag, read [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md), strip YAML front-matter, and inject rules into system prompts.
- This **runtime-agnostic design** allows rapid extension to new LLM platforms by copying the manifest-and-shim pattern.

## Frequently Asked Questions

### How does the i-have-adhd plugin architecture support multiple LLM runtimes simultaneously?

The architecture decouples the skill content from runtime-specific implementations. The [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) file contains the canonical rules, while each platform loads a small JavaScript shim that reads this file and injects it into the appropriate system prompt mechanism. Updates to the 10 rules in [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) instantly apply across all supported platforms without requiring changes to the runtime adapters.

### What determines whether the ADHD rules are injected into every conversation?

A **file-based flag system** controls this behavior. Each runtime checks for a specific hidden file in the user's home directory—such as `~/.config/opencode/.i-have-adhd-always` for OpenCode or `~/.claude/.i-have-adhd-always` for Claude Code. If the file exists, the runtime entry point automatically appends the ruleset to every system prompt; if absent, the plugin remains available but inactive by default.

### How does the plugin strip YAML front-matter from the SKILL.md file?

The runtime entry points use a regular expression to remove the YAML front-matter before injection. In `.opencode/plugins/i-have-adhd.mjs`, the `rulesetBody()` function executes `fs.readFileSync(skillPath, 'utf8').replace(/^---[^\S\r\n]*\r?\n[\s\S]*?\r?\n---[^\S\r\n]*(?:\r?\n|$)/, '')`, which matches the opening `---`, the YAML block content, and the closing `---`, replacing the entire block with an empty string. This ensures only the human-readable Markdown rules reach the LLM.

### Can I extend this plugin architecture to support a new LLM assistant?

Yes, the architecture is designed for extensibility. To add a new runtime, create two files: a **plugin manifest** (e.g., [`newplatform.json`](https://github.com/ayghri/i-have-adhd/blob/main/newplatform.json)) declaring the entry point, and a **JavaScript shim** that implements the "check flag, read SKILL.md, inject content" pattern. As long as your shim resolves the path to [`../../skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/../../skills/i-have-adhd/SKILL.md) and handles the host's specific API for modifying system prompts, the skill will function identically to the Claude or OpenCode implementations.