hooks/always-on.mjs in i-have-adhd: Purpose, Execution, and Opt-In Behavior

TLDR: hooks/always-on.mjs is a SessionStart hook that conditionally injects the i-have-adhd ruleset into every Claude or Codex session via stdout only when the user has created an opt-in flag file.

The i-have-adhd plugin for Claude Code provides ADHD-friendly response guidelines through an automatic always-on mode. The hooks/always-on.mjs file is the core mechanism that enables this behavior, checking for explicit user consent and prepending the rule set to the session prompt. According to the ayghri/i-have-adhd source code, this script runs silently in the background and only modifies the session when explicitly enabled.

What Is the Purpose of hooks/always-on.mjs?

The primary purpose of hooks/always-on.mjs is to act as a conditional prompt injector. It verifies that the user has opted into persistent ADHD mode before loading and emitting the full ruleset defined in skills/i-have-adhd/SKILL.md.

Opt-In Flag Detection

The script first checks for a flag file at $CLAUDE_CONFIG_DIR/.i-have-adhd-always, defaulting to ~/.claude/.i-have-adhd-always. If the file does not exist, the hook exits immediately with return code 0, leaving the session completely unchanged. This design ensures that the plugin remains strictly opt-in and never modifies sessions without explicit user consent.

Ruleset Injection and Banner Output

When the flag file is present, hooks/always-on.mjs reads the skill file, strips any leading YAML front-matter, and writes a banner message followed by the rule text to STDOUT. The banner notifies the user that "ADHD MODE ACTIVE (always-on)" and includes instructions for disabling the mode. Because stdout from the hook becomes part of the initial prompt, every subsequent response in the session follows the ADHD-friendly guidelines.

When Is hooks/always-on.mjs Executed?

The hook executes during the SessionStart event, which fires before any model response is generated.

SessionStart Hook Registration

Execution is declared in hooks/hooks.json, which maps the SessionStart event to a Node.js one-liner. This one-liner dynamically imports hooks/always-on.mjs from the plugin root using process.env.CLAUDE_PLUGIN_ROOT or process.env.PLUGIN_ROOT. The Claude-Code or Codex runtime evaluates this entry every time a new session context begins.

Session Events That Trigger the Hook

As implemented in ayghri/i-have-adhd, the hook runs on every session initialization, including:

  • Initial startup
  • Session resume
  • Clear operations
  • Compact operations

Because the hook fires before response generation, the ADHD ruleset is guaranteed to be present in the system prompt from the first interaction onward.

How the Hook Logic Works

The internal logic of hooks/always-on.mjs can be reproduced with the following minimal implementation. This script mirrors the actual source code behavior, including flag checking, front-matter stripping, and stdout emission:

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";

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

// Bail out if the opt-in flag is missing
if (!fs.existsSync(flagPath)) process.exit(0);

// Locate the skill file relative to this script
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const skillPath = path.join(scriptDir, "..", "skills", "i-have-adhd", "SKILL.md");
if (!fs.existsSync(skillPath)) process.exit(0);

// Remove leading YAML front-matter (--- … ---) if present
const body = fs.readFileSync(skillPath, "utf8")
  .replace(/^---[^\S\r\n]*\r?\n[\s\S]*?\r?\n---[^\S\r\n]*(?:\r?\n|$)/, "")
  .replace(/(?:\r?\n)+$/, "");

// Emit the banner + rules to stdout (this becomes the session prompt)
process.stdout.write(
  `ADHD MODE ACTIVE (always-on). The ruleset below applies to every response. ` +
  `"stop adhd mode" turns it off for this session; delete ${flagPath} to turn always-on off for good.\n\n${body}\n`
);

Key Files in the Hook Pipeline

Several files in the ayghri/i-have-adhd repository work together to define when the hook fires and what content it injects:

  • hooks/always-on.mjs — Implements the SessionStart hook that conditionally injects the ADHD ruleset.
  • hooks/hooks.json — Declares the SessionStart hook and launches always-on.mjs via a Node one-liner.
  • skills/i-have-adhd/SKILL.md — Contains the ADHD-friendly response rules that the hook strips of front-matter and injects.
  • tests/test_always_on_hooks.py — Unit tests verifying opt-in handling, front-matter stripping, and silent execution when disabled.

Summary

  • hooks/always-on.mjs is a SessionStart hook that conditionally prepends the i-have-adhd ruleset to the session prompt.
  • The hook only activates when the user creates the opt-in flag file at ~/.claude/.i-have-adhd-always.
  • It is triggered by the SessionStart event declared in hooks/hooks.json and runs before any response is generated.
  • The script reads skills/i-have-adhd/SKILL.md, strips YAML front-matter, and emits the rules to STDOUT with an activation banner.
  • If the flag file is missing, the script exits silently with code 0 and leaves the session unchanged.

Frequently Asked Questions

What happens if I don't create the opt-in flag file?

If the flag file does not exist at $CLAUDE_CONFIG_DIR/.i-have-adhd-always, hooks/always-on.mjs exits immediately with return code 0 and produces no output. The session starts normally without any ADHD-specific prompt modifications.

How do I disable the always-on ADHD mode?

You can disable the mode for the current session by typing "stop adhd mode" in the chat. To permanently disable always-on mode, delete the flag file at ~/.claude/.i-have-adhd-always (or the equivalent path defined in $CLAUDE_CONFIG_DIR).

Does the hook modify any configuration files permanently?

No. The hook only reads the flag file and the skill file; it does not write to or modify any configuration files. Its entire effect is transient, lasting only for the current session via stdout injection.

What triggers the SessionStart hook besides opening a new session?

According to the hooks/hooks.json configuration in ayghri/i-have-adhd, the SessionStart event also fires when resuming a session, clearing the context, or compacting the conversation history. The hook runs on each of these events to ensure the ruleset remains active.

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 →