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

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, 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:

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

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 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:

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:

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:

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

To disable:

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) as the source of truth across Claude, Codex, OpenCode, and other LLM runtimes.
  • Plugin manifests (plugin.json, 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, 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 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 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) 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 and handles the host's specific API for modifying system prompts, the skill will function identically to the Claude or OpenCode implementations.

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 →