How the Always-On Hook Mechanism Works in i-have-adhd
The always-on hook mechanism automatically injects ADHD-friendly formatting rules into every Claude session by checking for an opt-in flag file and outputting the ruleset to stdout at session start.
The i-have-adhd repository provides a Claude plugin designed to enforce concise, structured responses optimized for users with ADHD. The always-on hook mechanism ensures these formatting rules are automatically applied to every conversation without requiring manual activation, creating a seamless experience across all Claude sessions.
Hook Registration and Event Triggers
The always-on functionality is registered in hooks/hooks.json, which defines a SessionStart event listener. This configuration matches four specific session initiation commands: startup, resume, clear, and compact.
When any of these commands trigger, Claude executes a Node.js one-liner that dynamically imports hooks/always-on.mjs. The hook definition specifies the exact command to run, ensuring the script loads only when the environment variables CLAUDE_PLUGIN_ROOT or PLUGIN_ROOT are present.
node -e "(async()=>{const root=process.env.CLAUDE_PLUGIN_ROOT||process.env.PLUGIN_ROOT;
if(root)await import(require('node:url').pathToFileURL(require('node:path')
.join(root,'hooks','always-on.mjs')).href)})().catch(()=>{})"
This approach uses pathToFileURL from the node:url module to correctly resolve file paths across different operating systems before importing the ECMAScript module.
Execution Flow in always-on.mjs
The hooks/always-on.mjs script implements the core logic for the always-on hook mechanism. Upon execution, it immediately checks for the existence of an opt-in flag file at $CLAUDE_CONFIG_DIR/.i-have-adhd-always, defaulting to ~/.claude/.i-have-adhd-always if the environment variable is unset.
If the flag file is absent, the script exits silently via process.exit(0), ensuring zero impact on users who have not opted in. This check prevents any performance overhead or blocking behavior for standard Claude sessions.
When the flag is present, the script resolves the absolute path to skills/i-have-adhd/SKILL.md relative to its own location, not the current working directory. It reads the file contents, strips any YAML front-matter delimiters (---), and constructs the output payload.
Loading and Injecting the Ruleset
The script outputs a banner message followed by the complete ADHD-friendly ruleset to stdout. The output format includes:
ADHD MODE ACTIVE (always-on). The ruleset below applies to every response.
"stop adhd mode" turns it off for this session; delete /home/user/.claude/.i-have-adhd-always
to turn always-on off for good.
<rules ...>
Claude’s runtime captures this stdout output and prepends it to every response generated during the session. This injection happens at the system level, ensuring the constraints defined in SKILL.md—such as short paragraphs, bullet points, and structured formatting—are enforced automatically without user intervention.
Practical Usage: Enable, Disable, and Verify
Users control the always-on hook mechanism through filesystem operations and natural language commands.
Enable always-on mode permanently:
mkdir -p ~/.claude
touch ~/.claude/.i-have-adhd-always
Disable for the current session only:
Type stop adhd mode in any Claude conversation. This command is recognized by the runtime and suppresses the ruleset for that specific session without removing the flag file.
Disable permanently:
rm ~/.claude/.i-have-adhd-always
When enabled, every session start displays the activation banner, confirming that the ADHD-friendly formatting rules are active and will apply to all subsequent responses.
Safety Features and Error Handling
The always-on hook mechanism is designed with fail-safe principles to prevent disruption to Claude sessions. All file path resolutions in always-on.mjs are calculated relative to the script’s directory using __dirname and node:path joins, eliminating dependency on the process working directory.
Any errors encountered during file reading, path resolution, or output generation are caught by the outer try-catch block, which terminates the process with process.exit(0). This guarantees that a corrupted ruleset, missing skill file, or permission error will never prevent a Claude session from starting. The tests/test_always_on_hooks.py file validates this behavior, ensuring the hook exits cleanly both when the flag is absent and when errors occur during execution.
Summary
- The always-on hook mechanism is registered in
hooks/hooks.jsonunder theSessionStartevent, triggering onstartup,resume,clear, andcompactcommands. - It executes
hooks/always-on.mjs, which checks for the opt-in flag at~/.claude/.i-have-adhd-alwaysbefore proceeding. - When enabled, it reads
skills/i-have-adhd/SKILL.md, strips YAML front-matter, and outputs the rules to stdout for Claude to prepend to responses. - Users enable the feature by creating the flag file, disable it temporarily with "stop adhd mode", or remove the file for permanent deactivation.
- The implementation includes comprehensive error handling that always exits with code 0, ensuring session start is never blocked.
Frequently Asked Questions
What file activates the always-on hook mechanism?
The presence of ~/.claude/.i-have-adhd-always (or $CLAUDE_CONFIG_DIR/.i-have-adhd-always) acts as the opt-in flag. The script hooks/always-on.mjs checks for this file at runtime; if it exists, the ADHD ruleset is injected into the session.
How do I disable the always-on hook temporarily?
Type stop adhd mode in any active Claude session. This natural language command instructs Claude to ignore the always-on ruleset for that specific conversation without deleting the flag file, allowing you to restore the behavior in the next session.
Where are the ADHD-friendly rules stored?
The source rules are defined in skills/i-have-adhd/SKILL.md. The always-on script reads this file dynamically at session start, strips any YAML front-matter, and outputs the content. Modifying this file changes the behavior of all future sessions where the always-on hook is active.
What happens if the always-on script encounters an error?
The script is wrapped in a try-catch block that calls process.exit(0) on any exception. This design ensures that filesystem errors, missing dependencies, or corrupted skill files will never block Claude from starting a session, maintaining system stability even when the hook fails.
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 →