# How the SessionStart Hook Automatically Loads the i-have-adhd Ruleset in Claude Code

> Discover how the SessionStart hook in ayghri/i-have-adhd automatically loads the i-have-adhd ruleset into Claude Code sessions. Learn about the opt-in flag and dynamic import process.

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

---

**The SessionStart hook defined in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) executes a Node command that checks for an opt-in flag file and, if present, dynamically imports `hooks/always-on.mjs` to stream the ADHD ruleset into every Claude Code session.**

The `ayghri/i-have-adhd` repository provides a Claude Code plugin that enforces ADHD-friendly response rules through an automatic loading mechanism. When you start a new session, the SessionStart hook runs a validation check before injecting the ruleset from [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md), ensuring the behavior activates only for users who explicitly opt in.

## Where the SessionStart Hook Is Defined

The hook declaration lives in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) at the root of the repository. Claude Code triggers this hook whenever a session begins or when you type the commands `startup`, `resume`, `clear`, or `compact`. The configuration executes a Node.js one-liner that dynamically imports the loader module:

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

```

This command resolves the plugin root using the `CLAUDE_PLUGIN_ROOT` environment variable (falling back to `PLUGIN_ROOT`), constructs the absolute path to `hooks/always-on.mjs`, and imports it via `await import()`. The trailing `.catch(()=>{})` silently consumes any errors, guaranteeing that session initialization never crashes due to plugin issues.

## How the Loader Script Validates the Opt-In Flag

The actual ruleset loading logic resides in `hooks/always-on.mjs`. The script first determines whether you have enabled automatic loading by checking for a specific flag file in your Claude configuration directory:

```javascript
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);

```

If the file `~/.claude/.i-have-adhd-always` does not exist (or the path specified by `CLAUDE_CONFIG_DIR`), the script exits immediately with code `0`. This **fail-silent** approach ensures that the ruleset never loads without explicit user consent, leaving standard Claude Code behavior unchanged for users who have not opted in.

## Reading and Streaming the Ruleset

Once the flag file is confirmed, `always-on.mjs` resolves the path to the ruleset relative to its own location:

```javascript
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const skillPath = path.join(scriptDir, "..", "skills", "i-have-adhd", "SKILL.md");
if (!fs.existsSync(skillPath)) process.exit(0);

```

The script then reads [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md), strips the YAML frontmatter using a regular expression, and outputs the cleaned ruleset to stdout along with activation instructions:

```javascript
const body = fs
  .readFileSync(skillPath, "utf8")
  .replace(/^---[^\S\r\n]*\r?\n[\s\S]*?\r?\n---[^\S\r\n]*(?:\r?\n|$)/, "")
  .replace(/(?:\r?\n)+$/, "");

process.stdout.write(
  "ADHD MODE ACTIVE (always‑on). The ruleset below applies to every response. " +
  '"stop adhd mode" turns it off for this session; ' +
  `delete ${flagPath} to turn always‑on off for good.\n\n${body}\n`,
);

```

This output appears at the start of every Claude Code session, ensuring the ADHD-friendly guidelines defined in the repository are immediately visible and active for every response.

## Managing the Automatic Ruleset Loading

**Enabling always-on mode permanently**

Create the flag file to activate automatic loading for all future sessions:

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

```

**Disabling for a single session**

Type the command `stop adhd mode` during your Claude Code session. The SessionStart hook still executes and displays the activation header, but the mechanism respects this temporary override and suppresses the ruleset application for that specific session only.

**Permanently disabling automatic loading**

Remove the flag file to prevent the SessionStart hook from loading the ruleset in future sessions:

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

```

When the flag file is absent, `hooks/always-on.mjs` exits during the initial check, and Claude Code runs without the ADHD-specific ruleset.

## Summary

- The **SessionStart hook** in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) launches a Node script at every Claude Code session start or specific command invocation.
- The script in `hooks/always-on.mjs` checks for the opt-in flag at `~/.claude/.i-have-adhd-always` (or `CLAUDE_CONFIG_DIR`) before proceeding.
- If enabled, it reads [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md), strips YAML frontmatter, and streams the ruleset to stdout for immediate visibility.
- All failures are silently caught with `.catch(()=>{})` and early `process.exit(0)` calls to ensure session initialization never crashes.
- Users control activation through filesystem flags and can temporarily override the behavior with session commands.

## Frequently Asked Questions

### What triggers the SessionStart hook in Claude Code?

According to the [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) configuration in the `ayghri/i-have-adhd` repository, the hook executes when a new session begins or when you type the commands `startup`, `resume`, `clear`, or `compact`. These events trigger the Node command that validates the always-on flag before loading the ruleset.

### Where does the i-have-adhd plugin look for the always-on flag file?

By default, the script looks for `.i-have-adhd-always` inside the directory specified by the `CLAUDE_CONFIG_DIR` environment variable, or falls back to `~/.claude/` if that variable is unset. This path construction occurs in `hooks/always-on.mjs` using `path.join(os.homedir(), ".claude")`.

### How do I temporarily disable the ruleset without deleting the flag file?

Type `stop adhd mode` during your Claude Code session. While the SessionStart hook still runs and outputs the initial activation message, the plugin mechanism detects this specific command string and suppresses the ruleset enforcement for the remainder of that session.

### What happens if the SKILL.md file is missing when the hook runs?

If [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) cannot be found at the resolved path, `hooks/always-on.mjs` calls `process.exit(0)` immediately after the file existence check on line 26. This silent failure ensures that the session continues normally without the ruleset, as the plugin assumes the installation is incomplete or corrupted.