# How Claude Code Always-On Mode Works via hooks/always-on.mjs

> Discover how Claude Code always-on mode functions using hooks/always-on.mjs. Learn how it injects ADHD-friendly rules automatically to enhance your coding sessions.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: internals
- Published: 2026-08-23

---

**Claude Code's always-on mode automatically injects ADHD-friendly response rules at the start of every session by executing `hooks/always-on.mjs` through a SessionStart hook that checks for a flag file and outputs sanitized rules from [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) to stdout.**

The `ayghri/i-have-adhd` repository provides a Claude Code plugin designed to support neurodivergent users through persistent behavioral modifications. The always-on mode ensures the complete ADHD rule set defined in the project’s [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) automatically prepends every Claude response without requiring manual activation commands. This functionality is implemented primarily through the Node.js module `hooks/always-on.mjs`, which is triggered automatically via hook configuration when sessions start, resume, or refresh.

## How the SessionStart Hook Triggers Always-On Mode

The always-on mode activates through a **SessionStart** hook defined in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json). This hook matcher triggers on session state changes including `startup`, `resume`, `clear`, and `compact` events.

When a matching session event occurs, Claude Code executes the following command:

```bash
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 inline Node.js command resolves the plugin root directory from environment variables (`CLAUDE_PLUGIN_ROOT` or `PLUGIN_ROOT`), constructs a file URL path to `hooks/always-on.mjs`, and dynamically imports the module. The trailing `.catch(()=>{})` ensures execution failures never block the session from starting.

## Inside hooks/always-on.mjs: Step-by-Step Execution

The `hooks/always-on.mjs` script follows a strict six-step pipeline to safely load and inject the ADHD rule set.

### Flag File Detection and Opt-In Check

The script first determines the configuration directory using the `CLAUDE_CONFIG_DIR` environment variable, defaulting to `~/.claude` if unset ([lines 16-18](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs#L16-L18)). It checks for the existence of a flag file at `/.i-have-adhd-always` within that directory.

If the flag file does **not** exist, the script exits silently with `process.exit(0)`, allowing the session to proceed without modification ([line 20](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs#L20)).

### Loading and Sanitizing SKILL.md

Upon confirming the opt-in flag, the script locates the master rule set by resolving [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) relative to the script’s own directory using `import.meta.dirname` ([lines 22-25](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs#L22-L25)). This approach avoids reliance on user-controlled environment variables for path resolution.

The script reads the file as UTF-8 and performs content sanitization ([lines 27-34](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs#L27-L34)):
- Strips YAML front-matter blocks (content between `---` delimiters) using a regular expression
- Removes trailing newlines to ensure clean output formatting

### Rule Injection and Output

The cleaned ruleset is written to `stdout` prefixed with an activation notice indicating that **ADHD MODE ACTIVE (always-on)** is engaged ([lines 36-40](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs#L36-L40)). Claude Code captures this standard output and prepends the content to every subsequent response for the session. The notice includes instructions for disabling the mode temporarily ("stop adhd mode") or permanently (deleting the flag file).

### Safe Failure Handling

The implementation includes defensive error handling at multiple levels ([lines 41-44](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.mjs#L41-L44)). Any exception thrown during flag checking, file reading, or output operations is caught silently, and the process exits with code `0`. This guarantees that a corrupted [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) file or missing permissions never prevent Claude Code from starting.

## Shell Fallback for Non-Node Environments

For environments where Node.js is unavailable, the repository provides [`hooks/always-on.sh`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh), a POSIX shell script that mirrors the Node.js implementation exactly. This fallback ensures cross-platform reliability by duplicating the flag file check, YAML front-matter stripping logic, and safe exit behavior using standard shell utilities.

## Enabling and Disabling Always-On Mode

To enable always-on mode permanently, create the flag file in your Claude configuration directory:

```bash
mkdir -p "$HOME/.claude"
touch "$HOME/.claude/.i-have-adhd-always"

```

To disable always-on mode for the current session only, type the following in Claude:

```

stop adhd mode

```

To permanently disable always-on mode, remove the flag file:

```bash
rm "$HOME/.claude/.i-have-adhd-always"

```

## Summary

- **SessionStart hook**: Located in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json), triggers on `startup|resume|clear|compact` events to launch `hooks/always-on.mjs` via a dynamic Node.js import command.
- **Opt-in mechanism**: Checks for `~/.claude/.i-have-adhd-always` (or `$CLAUDE_CONFIG_DIR/.i-have-adhd-always`) and exits silently if absent.
- **Content processing**: Reads [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md), strips YAML front-matter with regex, removes trailing newlines, and prepends an activation header.
- **Safety guarantees**: All failures exit with code `0`, ensuring session startup never blocks; a POSIX shell fallback ([`hooks/always-on.sh`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh)) provides Node-independent support.
- **Rule injection**: Cleaned rules output to stdout are automatically prepended to every Claude response for the duration of the session.

## Frequently Asked Questions

### What happens if the flag file is missing?

If `~/.claude/.i-have-adhd-always` does not exist when `hooks/always-on.mjs` runs, the script calls `process.exit(0)` immediately and the session proceeds normally without loading ADHD mode rules. This design ensures the plugin is strictly opt-in and never interferes with standard Claude Code usage.

### Can I use always-on mode without Node.js installed?

Yes. The repository includes [`hooks/always-on.sh`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh), a POSIX-compliant shell script that implements identical logic for flag checking, YAML front-matter removal, and rule output. This fallback ensures the always-on mode functions in containerized or minimal environments where Node.js may not be available.

### How do I temporarily disable always-on for just one session?

Type `stop adhd mode` in the Claude Code chat interface. This command disables the ADHD rule injection for the current session only without deleting the flag file, allowing you to resume always-on mode automatically the next time you start a session.

### Where does the plugin store the configuration flag?

The script checks the `CLAUDE_CONFIG_DIR` environment variable first, defaulting to the `~/.claude` directory if unset. The specific file path is `.i-have-adhd-always` within that directory. This location is consistent across both the Node.js and shell implementations.