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

> Discover how Claude Code hooks power always-on behavior for i-have-adhd by automatically loading ADHD skill rules at session start. Learn more about this innovative feature.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: how-to-guide
- Published: 2026-09-01

---

**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`](https://github.com/ayghri/i-have-adhd/blob/main/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.

```json
{
  "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.

```js
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`](https://github.com/ayghri/i-have-adhd/blob/main/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.

```js
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:**

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

```

**Disable for the current session only:**

```

/i-have-adhd stop adhd mode

```

**Disable permanently:**

```bash
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:

```bash
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`](https://github.com/ayghri/i-have-adhd/blob/main/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`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) | Contains the canonical ADHD-friendly formatting rules injected by the hook |

## Summary

- **SessionStart hooks** in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/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`](https://github.com/ayghri/i-have-adhd/blob/main/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`](https://github.com/ayghri/i-have-adhd/blob/main/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.