How Does the Always-On Hook Function in the i-have-adhd Project?
TLDR: The always-on hook is a SessionStart hook in the i-have-adhd repository that automatically injects the full ADHD-friendly ruleset into every Claude session — but only when a user has explicitly opted in by creating a hidden flag file.
The ayghri/i-have-adhd repository provides a set of skills and hooks that tune Claude Code's behavior for neurodivergent users. The always-on hook is the project's centerpiece for persistent ADHD guidance: instead of manually loading the ruleset per session, this hook reads and emits the rules automatically at session start, so the assistant is already primed to respond with ADHD-friendly structure before the first prompt. Understanding how this hook works is essential for anyone who wants to configure, extend, or debug it.
How the Always-On Hook Works
The always-on hook is declared as a SessionStart hook and implemented as a JavaScript module (always-on.mjs) wrapped in a small wrapper that also detects platform-specific fallbacks. At runtime, it executes a fixed sequence of six steps, each designed to be quiet, safe, and non-blocking.
1. Determine the Opt-In Flag
The very first task of the hook is to check whether the user has explicitly enabled always-on mode. It does this by looking for a hidden file named .i-have-adhd-always inside the Claude configuration directory. The path is resolved as:
$CLAUDE_CONFIG_DIR/.i-have-adhd-always
If the $CLAUDE_CONFIG_DIR environment variable is not set (which is common), the hook falls back to ~/.claude as the default configuration path.
Important: Without this flag file, the hook does nothing. The user must specifically opt in for the persistent behavior.
2. Exit Silently When Not Opted In
If the flag file does not exist, the script immediately exits with status code 0. This ensures the hook never blocks, delays, or prompts at SessionStart for users who have not enabled this feature. Because it exits cleanly, the hook introduces zero overhead for the majority of users.
3. Locate the Skill File
Only after confirming the opt-in flag does the hook resolve the path to the actual ruleset: skills/i-have-adhd/SKILL.md. Instead of relying on environment variables, it computes this path relative to the hook's own location:
const skillPath = path.join(path.dirname(new URL(import.meta.url).pathname), 'skills/i-have-adhd/SKILL.md');
This relative resolution means the hook remains portable — you can move the entire repo, or symlink it, and the hook will still find the ruleset as long as the directory structure is intact.
4. Read and Clean the Skill Content
Next, the hook reads SKILL.md as UTF-8 text. The file contains a YAML front-matter block (a series of --- delimited lines that hold metadata). The hook strips this block with a regular expression:
const withoutFrontMatter = raw.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, '');
It also trims the trailing blank lines so that the injected output is compact and clean.
5. Emit the ADHD Mode Banner
Now the fun part. The hook writes the cleaned ruleset to stdout, prefixed with a short banner that:
- Notes that ADHD mode is active.
- Explains how to disable it for the current session by typing
"stop adhd mode"in the chat. - Explains how to permanently turn it off by deleting the
.i-have-adhd-alwaysflag file.
Because Stdout is captured by Claude Code at SessionStart, this text becomes part of the conversation — the model sees the banner plus the full ruleset and adjusts its behavior accordingly. The banner is deliberately small so as not to consume a lot of tokens.
6. Robust Error Handling
The entire logic runs inside a try/catch block. Any unexpected error (e.g., file read permission denials, path issues) is caught and the script still exits with status code 0. This guarantee means the hook can never cause a session to fail to start, even if the repository becomes malformed or the disk has transient issues.
Cross-Platform Runtime Support
The primary implementation is JavaScript (always-on.mjs), which runs on the Node.js runtime and therefore works on macOS, Linux, and Windows.
The repository also ships Bash and PowerShell fallback wrappers in the hooks.json declaration, making sure the hook functions even in environments that cannot execute the JS version (for example, where Node is not installed at the path the runtime expects). The actual configuration step is the same regardless of the shell:
Enable always-on mode:
# Unix-like shells
mkdir -p ~/.claude
touch ~/.claude/.i-have-adhd-always
# PowerShell
$claudeDir = "$env:USERPROFILE\.claude"
New-Item -ItemType Directory -Force -Path $claudeDir
New-Item -ItemType File -Force -Path "$claudeDir\.i-have-adhd-always"
After creating the flag file, every new Claude session will start with the ADHD rules automatically injected.
Disable always-on for the current session:
Within a Claude chat, type stop adhd mode — the hook's banner tells the model to stop applying the rules for that session.
Permanently disable always-on:
rm ~/.claude/.i-have-adhd-always # Unix-like shells
Remove-Item "$env:USERPROFILE\.claude\.i-have-adhd-always" # PowerShell
Key Files in the Repository
always-on.mjs— the JavaScript implementation of theSessionStarthook.SKILL.md— the markdown file containing the ADHD-friendly response rules that the hook injects.hooks.json— declares the hook type (SessionStart) and the entry point for the runtime.test_always_on_hooks.py— the unit test that verifies the hook's behavior under various conditions (flag present, flag absent, missing skill file, etc.).
Summary
- The always-on hook is a SessionStart hook that only activates when the user has explicitly opted in with a
.i-have-adhd-alwaysflag file. - It resolves the
SKILL.mdfile relative to its own location, strips the YAML frontmatter, and writes the ruleset to stdout with a banner. - The hook exits
0in all cases, including when disabled and when it throws an unexpected error — guaranteeing it never blocks a session. - It requires no session start, is cross-platform (Node.js with Bash/PowerShell fallbacks), and both temporary (
"stop adhd mode") and permanent (deleting the flag) disable paths are built in.
Frequently Asked Questions
What token overhead does the always-on hook add per session?
Very little. It emits the banner plus the full SKILL.md ruleset, which is typically a few hundred tokens. That cost is paid once per session, and the user can stop mode with a session with a meeting free temporary upgrade by typing "stop adhd mode" if they want to reduce it for a particular chat.
How do I know the always-on hook is actually running?
Look for the banner in your first Claude response of a new session—it says "ADHD mode is active" and explains your disable options. Alternatively, check that the flag file exists: ~/.claude/.i-have-adhd-always.
What happens if I delete the SKILL.md file while the hook is active?
The hook catches the resulting file-read error and still exits with code 0. The session will start normally, but no ADHD rules will be injected into that session, and no error will be shown (the hook is designed to be silent about failures).
Does the hook work with Windows without any extra setup?
Yes, as long as Node.js is available on the machine — the flag creation is PowerShell-friendly, and the hooks.json also declares a PowerShell fallback. If Node.js is absent, the embedded fallback keeps the hook functional, though the instructions for the (Bash) unix syntax will still be the same.
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 →