How Claude Code Hooks Enable Always-On Behavior for i-have-adhd

Claude Code hooks enable always-on behavior for i-have-adhd through a SessionStart hook that automatically loads the ADHD skill rules at the start of every session when the user has opted in.

The i-have-adhd plugin for Claude Code leverages the hooks system to deliver a seamless, automatic experience for users who need ADHD-friendly formatting. By registering a session lifecycle hook and implementing a lightweight opt-in flag, the skill activates itself without requiring manual commands each time. This article breaks down exactly how the hook infrastructure works, using the actual implementation from the ayghri/i-have-adhd repository.

Understanding the SessionStart Hook Mechanism

The foundation of always-on behavior is defined in hooks/hooks.json. This file registers a SessionStart hook that matches four session states: startup, resume, clear, and compact. When any of these events occur, Claude Code executes a Node.js command that dynamically imports the always-on module.

{
  "SessionStart": [
    {
      "matcher": "startup|resume|clear|compact",
      "hooks": [
        {
          "type": "command",
          "command": "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(()=>{})\"",
          "timeout": 30,
          "statusMessage": "Checking i-have-adhd always-on flag..."
        }
      ]
    }
  ]
}

The command uses dynamic import() with pathToFileURL to ensure cross-platform compatibility. The .catch(()=>{}) wrapper guarantees that any import failure exits silently rather than crashing the session.

How the Always-On Module Works

The hooks/always-on.mjs file implements three critical behaviors that make always-on mode reliable and unobtrusive.

Opt-In Flag Check

The module first checks for a hidden flag file at $CLAUDE_CONFIG_DIR/.i-have-adhd-always, defaulting to ~/.claude/.i-have-adhd-always. If the file does not exist, the script immediately exits with status 0, leaving the session completely unchanged.

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

Skill Loading and Output

When the flag exists, the module resolves skills/i-have-adhd/SKILL.md relative to its location, reads the content, strips YAML front-matter, and writes the rules directly to standard output. This streams the full rule set into Claude's context before any user message is processed.

process.stdout.write(
  "ADHD MODE ACTIVE (always-on). The ruleset below applies to every response…\n\n" + body
);

Non-Blocking Error Handling

Every operation is wrapped in error handling that exits with status 0. Missing files, permission errors, or read failures never block session startup. The user gets either the ADHD rules or plain behavior—never a broken session.

Enabling and Disabling Always-On Mode

Users control the always-on behavior through simple file operations.

Enable always-on permanently:

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

Disable for the current session only:


/i-have-adhd stop adhd mode

Disable permanently:

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

Testing the Hook Manually

To verify the hook executes correctly without starting a full Claude session, run this from the plugin root directory:

node -e "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)"

The script outputs nothing if the flag file is absent, or streams the ADHD rules if present.

Key Files in the Always-On Architecture

File Purpose
hooks/hooks.json Registers the SessionStart hook with matcher pattern and command configuration
hooks/always-on.mjs Implements opt-in detection, skill file loading, and rule streaming
skills/i-have-adhd/SKILL.md Contains the canonical ADHD-friendly formatting rules injected by the hook

Summary

  • SessionStart hooks in hooks/hooks.json trigger on startup|resume|clear|compact events
  • Dynamic Node execution imports always-on.mjs without blocking session initialization
  • Opt-in flag file at ~/.claude/.i-have-adhd-always gives users full control over activation
  • Direct stdout streaming injects SKILL.md rules into Claude's context before first response
  • Zero-fail design ensures session startup never breaks regardless of file system state

Frequently Asked Questions

What happens if the flag file is missing?

The always-on.mjs script exits immediately with status 0. Claude Code starts normally without ADHD formatting applied. No error messages appear, and the session proceeds unchanged.

Why use a file-based flag instead of environment variables?

File-based flags persist across terminal sessions and system restarts without requiring shell profile modifications. Users can toggle the behavior from any terminal or file manager, and the state survives Claude Code updates or reinstalls.

Can the always-on hook slow down session startup?

The hook has a 30-second timeout configured in hooks.json, but actual execution typically completes in milliseconds. The script performs only synchronous file existence checks and a single file read operation before exiting.

How does this differ from manually loading the skill?

Manual loading requires typing a command each session. The hook approach guarantees consistency—users never forget to activate the mode, and the rules apply from the very first Claude response without conscious effort.

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 →