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

TLDR: A non-firing SessionStart hook typically stems from missing registration in 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 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:

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 and confirm the SessionStart entry exists. It must contain a command type that invokes Node with the specific import logic:

{
  "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:

echo $CLAUDE_CONFIG_DIR   # If set

echo ~/.claude            # Default location

Create the flag file if it is missing:

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

Verify the file is readable:

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:

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 at lines 24-25. Verify this file exists:

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 from the repository if it is damaged.

5. Run the Automated Test Suite

The repository includes tests/test_always_on_hooks.py to validate hook behavior. Execute the tests to verify your environment:

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 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.
  • 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 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 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.

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 →