# Main Architectural Decisions in the i-have-adhd Plugin: A Cross-Runtime Architecture Analysis

> Discover i-have-adhd plugin's architectural decisions: cross-runtime compatibility via declarative Markdown rules, imperative TypeScript logic, abstracted APIs, and custom session persistence. Learn how it works.

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

---

**The i-have-adhd plugin achieves cross-runtime compatibility by separating declarative rules in Markdown from imperative TypeScript logic, abstracting session-manager APIs through a compatibility layer, and persisting state via custom session entries rather than external storage.**

The ayghri/i-have-adhd repository demonstrates a deliberate architectural approach to building LLM extensions that must operate across disparate coding assistant runtimes. The codebase prioritizes **runtime agnosticism**, **declarative configuration**, and **stateless synchronization** to deliver ADHD-friendly output formatting without vendor lock-in. Below is a detailed examination of the main architectural decisions that enable this flexibility.

## Separation of Concerns: Declarative Rules vs. Runtime Code

### Skill Definitions in Markdown

The plugin stores its behavioral rules outside the codebase in [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md). This decision keeps the rule set human-readable, versionable, and localizable without requiring recompilation. The `loadRules()` function (lines 61-78 in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts)) loads this Markdown file at runtime, strips any YAML frontmatter using `stripFrontmatter()`, and caches the result for injection into the model context.

```typescript
function loadRules(): string {
  const content = readFileSync(SKILL_PATH, "utf8");
  const rules = stripFrontmatter(content);
  if (!rules) throw new Error("The i-have-adhd rules file is empty");
  return rules;
}

```

### TypeScript Extension Bridge

While rules live in Markdown, the programmatic bridge resides in [`extensions/i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/i-have-adhd.ts). This module provides the **runtime-agnostic integration point** for any Pi-compatible assistant (Claude Code, Codex, OMP, etc.). By keeping the extension logic separate from the rule content, the architecture allows updates to the ADHD communication style without touching executable code.

## Runtime Agnosticism via Abstraction Layers

### The Context Compatibility Layer

To handle differing APIs across coding assistants, the plugin implements [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts). This module abstracts session-manager inconsistencies—such as `buildSessionContext` versus `buildContextEntries`—behind a unified interface. The same synchronization logic therefore functions identically whether running in Anthropic’s Claude or OpenAI’s Codex environment.

### Multi-Manifest Support

Rather than maintaining separate codebases for each runtime, the repository includes multiple manifest files (`.claude-plugin`, `.codex-plugin`, [`qwen-extension.json`](https://github.com/ayghri/i-have-adhd/blob/main/qwen-extension.json)) alongside the core [`plugin.json`](https://github.com/ayghri/i-have-adhd/blob/main/plugin.json). Each runtime reads its own manifest entry point, yet all share identical core logic in [`i-have-adhd.ts`](https://github.com/ayghri/i-have-adhd/blob/main/i-have-adhd.ts). This eliminates code duplication while satisfying each platform’s packaging requirements.

## State Persistence and Session Management

### Custom Session Entries for State

The architecture rejects external databases or file-based state in favor of **session entry persistence**. Using the `STATE_ENTRY_TYPE` constant, the plugin stores the ADHD mode toggle directly within the session context via `pi.appendEntry()` (lines 73-76). This guarantees that the `enabled` state survives session reloads, compactions, and restarts without dependencies on filesystem access or external storage.

```typescript
pi.appendEntry(STATE_ENTRY_TYPE, { enabled } satisfies AdhdModeState);

```

### Stateless Context Synchronization

To prevent duplicate rule injection and minimize context bloat, the `syncContext` function (lines 34-59) implements idempotent synchronization. Before injecting, `rulesAreInContext()` (lines 5-12) checks existing context messages for `RULES_MESSAGE_TYPE` or `DISABLED_MESSAGE_TYPE` markers using `latestMarkerIsActive()`. Rules are only appended when absent, keeping the model’s context window minimal.

```typescript
function rulesAreInContext(ctx: ExtensionContext): boolean {
  return latestMarkerIsActive(
    contextMessages(ctx.sessionManager),
    RULES_MESSAGE_TYPE,
    DISABLED_MESSAGE_TYPE,
  );
}

```

## Configuration Architecture and User Experience

### Hierarchical Configuration System

The plugin supports three configuration mechanisms in descending precedence:

1.  **JSON Configuration**: The [`i-have-adhd.json`](https://github.com/ayghri/i-have-adhd/blob/main/i-have-adhd.json) file supports boolean flags `alwaysOn` and `hideStatus` for per-project customization, loaded via `loadConfig()`.
2.  **Always-On Flag File**: A `.i-have-adhd-always` file in the project root provides a simple file-based opt-in mechanism that functions even when JSON configuration is absent (lines 15-18).
3.  **Startup Flag**: The `adhd` flag registration allows activation at session initialization.

This layered approach accommodates both persistent project settings and temporary, file-based overrides.

### Command Interface and Graceful Shutdown

User interaction follows a dual-path design. The `/i-have-adhd` command (registered at lines 87-88) accepts `on`, `off`, or toggle arguments through a handler at lines 87-100. For natural language interaction, the `STOP_PHRASES` constant recognizes phrases like "stop adhd mode" or "normal mode" in the input listener (lines 21-32), allowing users to exit the mode conversationally without memorizing slash commands.

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

```

### Visual Feedback Integration

When running in supported agents, the architecture exposes state through `ctx.ui.setStatus` (lines 20-28). This provides immediate visual confirmation of ADHD mode activation, addressing the accessibility requirement for clear system status indicators.

## Summary

-   **Declarative/Imperative Separation**: Rules live in [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) while logic resides in TypeScript, enabling non-technical updates to behavior.
-   **Runtime Abstraction**: The [`context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/context-compat.ts) layer normalizes disparate session manager APIs across Claude, Codex, and OMP environments.
-   **Session-Native Persistence**: State survives restarts via `STATE_ENTRY_TYPE` entries appended to the session context, eliminating external storage dependencies.
-   **Layered Configuration**: JSON files, filesystem flags, and startup arguments provide flexible deployment options.
-   **Idempotent Injection**: The `syncContext` logic prevents duplicate rule messages, maintaining optimal context window usage.

## Frequently Asked Questions

### How does i-have-adhd maintain state across session restarts?

The plugin persists the ADHD mode toggle using custom session entries rather than external databases. By calling `pi.appendEntry(STATE_ENTRY_TYPE, { enabled })` at lines 73-76, the state becomes part of the session context itself. This approach ensures the configuration survives session compactions and reloads without requiring filesystem write access or separate state management infrastructure.

### What architectural pattern enables compatibility with multiple AI coding assistants?

The repository implements an **adapter pattern** through [`extensions/context-compat.ts`](https://github.com/ayghri/i-have-adhd/blob/main/extensions/context-compat.ts), which abstracts differences between session manager APIs like `buildSessionContext` and `buildContextEntries`. Combined with runtime-specific manifest files (`.claude-plugin`, `.codex-plugin`) that all point to the shared core logic, this architecture allows the same extension to function across Claude Code, Codex, and other Pi-compatible runtimes without code duplication.

### Why are the behavioral rules stored in Markdown rather than compiled TypeScript?

Storing rules in [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) separates the **what** (behavioral guidelines) from the **how** (implementation logic). This declarative approach allows technical writers, clinicians, or translators to modify ADHD-friendly output patterns without reviewing TypeScript code or triggering rebuilds. The `loadRules()` function strips frontmatter and validates content at runtime, treating the Markdown file as a configuration asset rather than source code.

### How can users configure the plugin without modifying repository code?

The architecture provides three code-free configuration mechanisms: (1) Creating an [`i-have-adhd.json`](https://github.com/ayghri/i-have-adhd/blob/main/i-have-adhd.json) file with `alwaysOn` or `hideStatus` flags; (2) Adding an empty `.i-have-adhd-always` file to the project root for automatic activation; or (3) Passing the `adhd` flag at session startup. These options allow per-project customization through filesystem operations alone, requiring no changes to the TypeScript source or rebuilds of the extension.