# How the i-have-adhd Plugin Maintains Conversation Context State Across Multiple Turns

> Discover how the i-have-adhd plugin keeps conversation context by injecting its ruleset into the system prompt, ensuring seamless multi-turn interactions.

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

---

**The i-have-adhd plugin maintains conversation context state across multiple turns by injecting the full ADHD ruleset into the system prompt on every API request when "always-on" mode is enabled.**

This persistence mechanism requires no database or session storage. Instead, the plugin leverages the LLM's own context window by repeatedly prepending behavioral instructions to each turn's system prompt. The approach is implemented in the [ayghri/i-have-adhd](https://github.com/ayghri/i-have-adhd) repository, which provides ADHD-friendly response formatting for AI coding assistants.

## How Context Persistence Works

The plugin uses a **stateless, prompt-injection architecture** rather than maintaining conversational state externally. Three core components coordinate to ensure the 10 ADHD-friendly rules remain active throughout a multi-turn conversation.

### The Opt-In Flag File

Context persistence only activates when the user explicitly opts in. The plugin checks for a flag file at startup and on every response generation.

**Flag file locations by platform:**

| Platform | Path |
|----------|------|
| OpenCode | `~/.config/opencode/.i-have-adhd-always` |
| Claude-Code / Codex | `~/.claude/.i-have-adhd-always` |

In [`i-have-adhd.mjs`](https://github.com/ayghri/i-have-adhd/blob/main/.opencode/plugins/i-have-adhd.mjs) (lines 27-33), the flag path construction looks like this:

```javascript
// Lines 27-33: Building the always-on flag path
const configDir = process.env.OPENCODE_CONFIG_DIR || 
                  path.join(os.homedir(), '.config', 'opencode');
const alwaysOnFlag = path.join(configDir, '.i-have-adhd-always');

```

The [`always-on.mjs`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs) hook performs an identical check (lines 15-20) to detect the flag at session startup.

### Ruleset Loading and Cleaning

Before injection, the plugin must extract the clean rule content from [[`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md)](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md).

The `rulesetBody()` function in [`i-have-adhd.mjs`](https://github.com/ayghri/i-have-adhd/blob/main/.opencode/plugins/i-have-adhd.mjs) (lines 35-43) handles this:

```javascript
// Lines 35-43: Reading and cleaning the ruleset
function rulesetBody() {
  const skillPath = path.join(__dirname, '..', 'skills', 'i-have-adhd', 'SKILL.md');
  const content = fs.readFileSync(skillPath, 'utf-8');
  
  // Strip YAML frontmatter if present
  return content.replace(/^---\n[\s\S]*?\n---\n*/, '').trim();
}

```

This sanitization step removes any metadata headers, ensuring only the behavioral rules reach the LLM.

### System Prompt Transformation Per Turn

The critical mechanism for maintaining conversation context state across multiple turns lives in the `experimental.chat.system.transform` hook. This hook fires before every LLM request, making it the ideal interception point for persistent behavior modification.

From [`i-have-adhd.mjs`](https://github.com/ayghri/i-have-adhd/blob/main/.opencode/plugins/i-have-adhd.mjs) (lines 55-78):

```javascript
// Lines 55-78: The transform hook that persists context
experimental: {
  chat: {
    system: {
      transform: (systemPrompt, context) => {
        if (!fs.existsSync(alwaysOnFlag)) {
          return systemPrompt; // Pass through unchanged
        }
        
        const rules = rulesetBody();
        const header = "ADHD MODE ACTIVE (always-on): Following rules below on EVERY response";
        
        return `${header}\n\n${rules}\n\n---\n\n${systemPrompt}`;
      }
    }
  }
}

```

Because the transformed system prompt travels with every API call, the rules effectively **persist for the entire conversation duration** without any server-side state management.

## Session Control: Fine-Grained Context Management

The plugin provides three mechanisms to control when context state is maintained:

- **Always-on persistence** — Flag file present: rules injected every turn
- **Session-level disable** — User types `stop adhd mode` or `normal mode` to halt injection for current session
- **Permanent disable** — Delete the flag file to stop all automatic injection

The session disable command is recognized through standard skill intent matching. The header text in [`i-have-adhd.mjs`](https://github.com/ayghri/i-have-adhd/blob/main/.opencode/plugins/i-have-adhd.mjs) (lines 67-70) and [`always-on.mjs`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs) (lines 36-40) establishes this contract:

```

ADHD MODE ACTIVE (always-on): Following rules below on EVERY response

```

## Enabling and Disabling Conversation Context Persistence

Enable always-on mode (rules persist across all turns):

```bash

# OpenCode users

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

# Claude-Code or Codex users

touch ~/.claude/.i-have-adhd-always

```

Disable for current session (type in chat):

```

stop adhd mode

```

Disable permanently:

```bash

# OpenCode users

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

# Claude-Code or Codex users

rm ~/.claude/.i-have-adhd-always

```

One-time invocation without persistence:

```

/i-have-adhd

```

This command loads the ruleset once for the remainder of the session but does not enable the transform hook for subsequent turns.

## Architecture Comparison: Prompt Injection vs. State Storage

The i-have-adhd plugin deliberately avoids conventional state management. Here's why this design maintains conversation context state across multiple turns more reliably for this use case:

| Approach | Implementation | Trade-off |
|----------|---------------|-----------|
| **Prompt injection** (used) | Transform hook prepends rules to system prompt | Zero dependencies, works across all LLM providers, no state sync issues |
| Session storage | Database or memory store of active rules | Requires persistence layer, complicates deployment, potential drift |
| Tool calling | Dynamic retrieval of rules per turn | Adds latency, requires function calling support |

The prompt injection method guarantees the LLM receives the complete behavioral context with every token generation, eliminating any risk of state desynchronization.

## Summary

- **Conversation context state** is maintained by injecting the ADHD ruleset into the system prompt on every turn via the `experimental.chat.system.transform` hook
- The `.i-have-adhd-always` flag file in the config directory activates this always-on persistence
- The `rulesetBody()` function strips YAML frontmatter from [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) before injection
- No external database or session store is required—the LLM's context window carries the state
- Users control persistence through file-based opt-in, chat commands, or manual `/i-have-adhd` invocation

## Frequently Asked Questions

### How does the plugin know to persist rules across multiple turns without a database?

The plugin checks for the `.i-have-adhd-always` flag file before every LLM request. When present, the `transform` hook prepends the ruleset to the system prompt. Since the system prompt travels with each API call, the rules persist naturally through the LLM's own context mechanism. No database is needed because the state lives in the prompt itself.

### What's the difference between always-on mode and the `/i-have-adhd` command?

Always-on mode enables the transform hook that fires before every turn, ensuring rules are present for the entire conversation. The `/i-have-adhd` command loads the ruleset once through standard skill invocation, affecting only the current session without the persistent hook. Always-on mode requires the flag file; the slash command does not.

### Can I temporarily disable context persistence without deleting the flag file?

Yes. Typing `stop adhd mode` or `normal mode` in the chat disables rule injection for the current session while preserving the flag file for future sessions. This session-level control is implemented through the skill's intent recognition system and does not modify filesystem state.

### Why does the plugin strip YAML frontmatter from SKILL.md?

The `rulesetBody()` function removes YAML frontmatter to prevent metadata from polluting the LLM's instructions. Frontmatter typically contains configuration like version tags or author information that could confuse the model's adherence to the actual behavioral rules. The cleaning ensures only actionable instructions reach the system prompt.