# How to Debug a SessionStart Hook That Won't Fire in the i-have-adhd Plugin

> Fix a non-firing SessionStart hook in the i-have-adhd plugin. Debug hooks.json, opt-in flags, and Node.js execution for automatic Claude Code rule injection.

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

---

**TLDR:** A non-firing SessionStart hook typically stems from missing registration in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json), a missing opt-in flag file at `~/.claude/.i-have-adhd-always`, or Node.js execution failures in `hooks/always-on.mjs`. Verify these three components to restore the automatic injection of ADHD coding rules into Claude Code sessions.

The **SessionStart hook** in the `ayghri/i-have-adhd` repository is responsible for injecting the project's ADHD-friendly ruleset at the beginning of every Claude Code session. When debugging a hook that never fires, you must trace the execution path from registration through the opt-in check to the final script execution. The hook only activates when specific conditions are met across multiple files in the codebase.

## Root Causes of SessionStart Hook Failures

The implementation relies on a chain of dependencies. A break in any link prevents the hook from firing.

### Missing Hook Registration

The hook must be declared in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) with a specific command structure. If the `SessionStart` array is absent or malformed, Claude Code never triggers the automation.

### Absent Opt-In Flag

According to the source in `hooks/always-on.mjs`, the script checks for a flag file at line 20:

```javascript
const flagPath = path.join(claudeDir, ".i-have-adhd-always");
if (!fs.existsSync(flagPath)) process.exit(0);

```

Without this file present at `~/.claude/.i-have-adhd-always` (or `$CLAUDE_CONFIG_DIR/.i-have-adhd-always`), the script exits silently before loading any rules.

### Node.js Environment Issues

The hook executes a complex one-liner Node command that dynamically imports `hooks/always-on.mjs`. If **Node.js** is unavailable, environment variables like `CLAUDE_PLUGIN_ROOT` or `PLUGIN_ROOT` are misconfigured, or the script lacks read permissions, the hook fails.

## Step-by-Step Debugging Process

Follow these steps in order to isolate the failure point.

### 1. Verify Hook Registration in hooks/hooks.json

Open [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) and confirm the `SessionStart` entry exists. It must contain a command type that invokes Node with the specific import logic:

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

```

If this block is missing, copy it exactly as shown. The `matcher` field ensures the hook fires on session startup, resume, clear, and compact operations.

### 2. Confirm the Opt-In Flag File Exists

The script looks for the flag in the Claude configuration directory. Determine your config location:

```bash
echo $CLAUDE_CONFIG_DIR   # If set

echo ~/.claude            # Default location

```

Create the flag file if it is missing:

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

```

Verify the file is readable:

```bash
ls -l ~/.claude/.i-have-adhd-always

```

If the file cannot be read, the hook exits immediately at line 20 of `hooks/always-on.mjs` without producing output.

### 3. Validate Node.js Script Execution

Manually run the hook script to surface runtime errors:

```bash
node hooks/always-on.mjs

```

Expected output begins with:

```

ADHD MODE ACTIVE (always-on). The ruleset below applies to every response...

```

If the command fails:
- Run `node -v` to confirm Node.js is installed.
- Check that `CLAUDE_PLUGIN_ROOT` or `PLUGIN_ROOT` points to the plugin directory, or ensure you run the command from the repository root so the relative path `hooks/always-on.mjs` resolves correctly.
- Check the exit code with `echo $?`. A non-zero code indicates an error occurred before the catch-all block could force `process.exit(0)`.

### 4. Inspect the Skill File Path

After validating the flag, the script loads [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) at lines 24-25. Verify this file exists:

```bash
ls -l skills/i-have-adhd/SKILL.md

```

If the skill file is missing or corrupted, the hook exits early without injecting rules. Restore the original [`SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/SKILL.md) from the repository if it is damaged.

### 5. Run the Automated Test Suite

The repository includes [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py) to validate hook behavior. Execute the tests to verify your environment:

```bash
python3 -m unittest discover -s tests -v

```

Look specifically for the `test_always_on_hooks.SessionStart` test results. If this test fails, it indicates the hook is not firing when the flag file is present. Adjust your environment until the test passes.

## Platform-Specific Considerations for Windows

On Windows systems, the repository provides `hooks/always-on.ps1` as a PowerShell fallback. If you are using PowerShell instead of Node.js, ensure the `.ps1` script is called and that the same flag-checking logic applies. The opt-in file location remains the same, but verify PowerShell execution policies allow the script to run.

## Summary

- **Hook registration** requires the exact `SessionStart` JSON structure in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) with the Node command that imports `hooks/always-on.mjs`.
- **Opt-in mechanism** depends on the existence of `~/.claude/.i-have-adhd-always` (or the equivalent path in `$CLAUDE_CONFIG_DIR`).
- **Execution environment** needs working Node.js, correct environment variables, and read permissions for both the flag file and [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md).
- **Testing** with `python3 -m unittest discover -s tests -v` confirms the hook fires correctly under automation.

## Frequently Asked Questions

### Where is the SessionStart hook registered?

The registration exists in [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) within the `SessionStart` array. This file tells Claude Code to execute the Node command that loads `hooks/always-on.mjs` when sessions start, resume, clear, or compact.

### Why does the SessionStart hook exit immediately without output?

The script exits silently at line 20 of `hooks/always-on.mjs` if the opt-in flag file `.i-have-adhd-always` is missing from the Claude configuration directory. It also exits 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, preventing partial rule injection.

### What environment variables does the SessionStart hook require?

The hook command checks for `CLAUDE_PLUGIN_ROOT` first, then falls back to `PLUGIN_ROOT`. If neither is set, the script attempts to resolve paths relative to the current working directory, which may fail if executed from the wrong directory.

### How can I manually test the SessionStart hook?

Run `node hooks/always-on.mjs` from the repository root. If the flag file exists and the skill file is readable, you will see the ADHD ruleset printed to stdout. If you see no output, check that `~/.claude/.i-have-adhd-always` exists and that Node.js can access both the script and the skill file.