How the i-have-adhd Plugin Maintains Conversation Context State Across Multiple Turns
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 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 (lines 27-33), the flag path construction looks like this:
// 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 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/skills/i-have-adhd/SKILL.md).
The rulesetBody() function in i-have-adhd.mjs (lines 35-43) handles this:
// 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 (lines 55-78):
// 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 modeornormal modeto 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 (lines 67-70) and 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):
# 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:
# 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.transformhook - The
.i-have-adhd-alwaysflag file in the config directory activates this always-on persistence - The
rulesetBody()function strips YAML frontmatter fromSKILL.mdbefore 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-adhdinvocation
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.
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 →